Deselect checkboxes based on dropdown select value in ProComponents React - reactjs

Resources for ProComponents that are being used in this demo.
ProForm same properties as ProModal
ProFormCheckBox.Group
ProFormSelect
I am trying to deselect all checkboxes every time I select a new item.
So for example if I select PCA from the dropdown and checked boxed 2.
Example 1
Then switched over to LSCA from the dropdown I want to deselect all checkboxes.
Example 2
Instead, what happens check box 2 is still selected.
Example 3
I have it set where each dropdown item has a different list of checkboxes set into it.
They are four different arrays. The more interesting parts are the three different useState. One controls the state of which dropdown item is selected. Another controls the state of which array of checkboxes should be displayed. The last controls the state of which checkboxes should be marked. Notes of interest in the code are
// Controls the state of the dropdown menu to be selected
const [selected, setSelected] = useState('');
// Controls the state of which array of checkboxes should be displayed
const [checkBoxes, setCheckBoxes] = useState([]);
// Controls the state of which checkboxes are checkmarked
const [markedCheckBoxes, setMarkedCheckBoxes] = useState([]);
Next code of interest is the function changeSelectOptionHandler which is run onChange of the ProFormSelect. This should run setMarkedCheckBoxes to set the state into an empty array so no boxes are selected.
/** Function that will set different values to state variable
* based on which dropdown is selected
*/
const changeSelectOptionHandler = (event) => {
// This should set the state of the setMarkedCheckBoxes to be empty
setMarkedCheckBoxes([]);
// Sets the state of which array of checkboxes should be displayed based on event
checkBoxOptions(event);
// Sets the state of which dropdown is selected based on the event
setSelected(event);
};
According to the docs I should set the value as what checkBoxes should be marked in ProFormCheckbox.Group
<ProFormCheckbox.Group
name="rows"
label="Select Rows"
options={checkBoxes}
onChange={(e) => {
console.log('state changes');
setMarkedCheckBoxes(e);
}}
// This is where I set which checkboxes should be marked with value
// initialValue={markedCheckBoxes}
value={markedCheckBoxes}
/>
I was able to use the React Dev Tools and confirm values are updated for markedCheckBoxes based on when a new dropdown item is selected which should be an empty array. I also tested that when I cancel or submit the modalForm that markedCheckBoxes is an empty array and is correctly displayed with setting the value on the ProFormCheckbox.Group. So I am stumped on how to correctly display what is on the value in ProFormCheckbox.Group after updating the select menu. Below is the full code snippet of said RowModal component.
import { PlusOutlined } from '#ant-design/icons';
import { Button, message } from 'antd';
import { useState } from 'react';
import ProForm, { ModalForm, ProFormSelect, ProFormCheckbox } from '#ant-design/pro-form';
import { updateRule } from '#/services/ant-design-pro/api';
const RowModal = ({ orderId, actionRef }) => {
/** Different arrays for different dropdowns */
const eca = ['1', '2', '3', '4', '5'];
const pca = ['1', '2'];
const lsca = ['1', '2', '3', '4', '5', '6'];
const mobility = ['1', '2', '3', '4'];
// Controls the state of the dropdown menu to be selected
const [selected, setSelected] = useState('');
// Controls the state of which array of checkboxes should be displayed
const [checkBoxes, setCheckBoxes] = useState([]);
// Controls the state of which checkboxes are checkmarked
const [markedCheckBoxes, setMarkedCheckBoxes] = useState([]);
/** Function that will set different values to state variable
* based on which dropdown is selected
*/
const changeSelectOptionHandler = (event) => {
// This should set the state of the setMarkedCheckBoxes to be empty
setMarkedCheckBoxes([]);
// Sets the state of which array of checkboxes should be displayed based on event
checkBoxOptions(event);
// Sets the state of which dropdown is selected based on the event
setSelected(event);
};
/** This will be used to create set of checkboxes that user will see based on what they select in dropdown*/
const checkBoxOptions = (event) => {
/** Setting Type variable according to dropdown */
if (event === 'ECA') setCheckBoxes(eca);
else if (event === 'PCA') setCheckBoxes(pca);
else if (event === 'LSCA') setCheckBoxes(lsca);
else if (event === 'Mobility') setCheckBoxes(mobility);
else setCheckBoxes([]);
};
return (
<ModalForm
title="Assign to Area and Row"
trigger={
<Button type="primary">
<PlusOutlined />
Assign
</Button>
}
autoFocusFirstInput
modalProps={{
destroyOnClose: true,
onCancel: () => {
setSelected('');
setCheckBoxes([]);
setMarkedCheckBoxes([]);
},
}}
onFinish={async (values) => {
const newValues = { ...values, order: orderId };
const req = await updateRule('http://127.0.0.1:3000/api/v1/floorPlans', {
data: newValues,
});
message.success('Success');
setSelected('');
setCheckBoxes([]);
setMarkedCheckBoxes([]);
actionRef.current?.reloadAndRest?.();
return true;
}}
// initialValues={{ rows: ['A'] }}
>
<ProForm.Group>
<ProFormSelect
request={async () => [
{
value: 'PCA',
label: 'PCA',
},
{
value: 'ECA',
label: 'ECA',
},
{
value: 'LSCA',
label: 'LSCA',
},
{
value: 'Mobility',
label: 'Mobility',
},
]}
// On change of dropdown, changeSelectOptionHandler will be called
onChange={changeSelectOptionHandler}
width="xs"
name="area"
label="Select Area"
value={selected}
/>
</ProForm.Group>
<ProFormCheckbox.Group
name="rows"
label="Select Rows"
options={checkBoxes}
onChange={(e) => {
console.log('state changes');
setMarkedCheckBoxes(e);
}}
// This is where I set which checkboxes should be marked with value
// initialValue={markedCheckBoxes}
value={markedCheckBoxes}
/>
</ModalForm>
);
};
export default RowModal;
Thanks in advance!

ProForm is a repackaging of antd Form
So you can use Form API for your purpose
import { useForm } from 'antd/lib/form/Form'
//...
const [form] = useForm();
// and then pass FormInstance in your component
<ModalForm
form={form}
//...
/>
// then in your handlers where you want to modify values use
form.setFieldsValue({
"fieldName": value
})

Related

How to handle multiple checkboxes in React form?

I have a form that loads preset checkbox selections from the backend. I query the checkbox menu and iterate over it in JSX. Now I want to be able to select/deselect. To handle this, I start out by creating an array of objects matching the size of menu checkbox items like so:
useEffect(() => {
if (!profile) { // this checks if the reusable form is a create or update. If this is an update, I need to prefill an existing array of objects with added key/value pair of checked: true
setConcerns(Array(menuItems.length).fill({ checked: false }))
}
}, [menuItems]) // this happens first
// if it's an update form
useEffect(() => {
if (profile) {
const updatedConcerns = existingConcerns.map((c, i) => ({
...concerns,
c.id: c.id,
checked: true,
}))
}
}, [existingConcerns]) // this happens second
This sets up the concerns array for toggle.
In my JSX I have a Checkbox component:
{menuItems &&
menuItems.map((item: CheckboxProps, index: number) => (
<Checkbox
testID={item?.title}
label={item?.title}
checked={concerns && !!concerns[index]?.checked}
onValueChange={() => handleConcerns(index)}
/>
))}
And the handleConcerns method so far:
const handleConcerns = (index: number) => {
const concernsCopy = [...concerns]
concernsCopy[index].checked = !concernsCopy[index].checked
setConcerns(concernsCopy)
// the rest will determine how to add selected items to concerns
array for submit
}
This causes all of the checkboxes to toggle. This is just the start of pushing menu data into each index of concerns array but first need to get the checkboxes working and be able to have an array of chosen indices to submit to backend.

Selecting created option on menu close/select blur using creatable component

Is there some way to instruct react-select to select an option on menu close or select blur, but only if it is the one created (not from default list)?
Context:
I have a list of e-mail addresses and want to allow user to select from the list or type new e-mail address and then hit Submit button. I do the select part with react-select's Creatable component and it works.
import CreatableSelect from 'react-select/creatable';
<CreatableSelect
options={options}
isMulti={true}
isSearchable={true}
name={'emailAddresses'}
hideSelectedOptions={true}
isValidNewOption={(inputValue) => validateEmail(inputValue)}
/>
But what happens to my users is that they type new e-mail address, do not understand they need to click the newly created option in dropdown menu and directly hit the Submit button of the form. Thus the menu closes because select's focus is stolen and form is submitted with no e-mail address selected.
I look for a way how can I select the created option before the menu is closed and the typed option disappears.
You can keep track of the inputValue and add the inputValue as a new option when the onMenuClose and onBlur callbacks are triggered.
Keep in mind that both onBlur and onMenuClose will fire if you click anywhere outside of the select area. onMenuClose can also fire alone without onBlur if you press Esc key so you will need to write additional logic to handle that extra edge case.
function MySelect() {
const [value, setValue] = React.useState([]);
const [inputValue, setInputValue] = React.useState("");
const isInputPreviouslyBlurred = React.useRef(false);
const createOptionFromInputValue = () => {
if (!inputValue) return;
setValue((v) => {
return [...(v ? v : []), { label: inputValue, value: inputValue }];
});
};
const onInputBlur = () => {
isInputPreviouslyBlurred.current = true;
createOptionFromInputValue();
};
const onMenuClose = () => {
if (!isInputPreviouslyBlurred.current) {
createOptionFromInputValue();
}
else {} // option's already been created from the input blur event. Skip.
isInputPreviouslyBlurred.current = false;
};
return (
<CreatableSelect
isMulti
value={value}
onChange={setValue}
inputValue={inputValue}
onInputChange={setInputValue}
options={options}
onMenuClose={onMenuClose}
onBlur={onInputBlur}
/>
);
}
Live Demo

Single onChange that can handle single- and multi-select Form changes

I have a Form with a couple Form.Select attributes. My onChange() works for the <Form.Select> attributes without multiple set. However, it cannot handle selections from <Form.Select> attributes that do have multiple set.
I would like to have a single onChange function that can handle data changing for instances of Form.Select with or without the "multiple" flag set.
The Form Class:
class SomeForm extends React.Component {
handleSubmit = event => {
event.preventDefault();
this.props.onSubmit(this.state);
};
onChange = event => {
const {
target: { name, value }
} = event;
this.setState({
[name]: value
});
console.log("name: " + name)
console.log("value: " + value)
};
render() {
return (
<Form onSubmit={this.handleSubmit} onChange={this.onChange}>
<Form.Select label="Size" options={sizes} placeholder="size" name="size" />
<Form.Select
label="Bit Flags"
placeholder="Bit flags"
name="flags"
fluid
multiple
search
selection
options={bits}
/>
<Form.Button type="submit">Submit</Form.Button>
</Form>
);
}
}
The logs are never called when I select options from the Form with multiple set.
Some possible bits to populate the options prop of Form.Select:
const bits = [
{key: '1', text: "some text 1", value: "some_text_1"},
{key: '2', text: "some text 2", value: "some_text_2"},
];
How can I modify onChange() to handle both Form.Select attributes as listed above?
Please note that this question is different from question on StackOverflow which are concerned only with an onChange that can only be used for updating an array.
Multiple selects are a weird beast. This post describes how to retrieve the selected values.
If you want to define a form-level handler, you need to make an exception for multiple selects:
const onChange = (event) => {
const value = isMultipleSelect(event.target)
? getSelectedValuesFromMultipleSelect(event.target)
: event.target.value
this.setState({[event.target.name]: value})
};
const isMultipleSelect = (selectTag) => selectTag.tagName === 'SELECT' && selectTag.multiple
const getSelectedValuesFromMultipleSelect = (selectTag) => [...selectTag.options].filter(o => o.selected).map(o => o.value)

How can i define a default value for react-select v1

I had a react-select rendering a list of emails, and i need to keep the selected emails as a default option when the email is selected and saved, but the defaultValues are not working. How can i do that?
Here is my select component:
const [selectedOption, setSelectedOption] = useState("")
const makeEmailOption = item => ({
value: item.id,
label: item.ccEmail,
id: item.id,
chipLabel: item.ccEmail,
rest: item,
selected: item.selected
})
const makeEmailOptions = items => items.map(makeEmailOption)
const handleChange = (value) => {
setSelectedOption(value)
props.emails(value)
}
return (
<div>
<Select
multi={true}
name={props.name}
options={makeEmailOptions(props.ccemailfilter)}
onChange={handleChange}
value={selectedOption}
/>
</div>
)
I receive everything as props and work with that to make the options. How can i do that to make the default value if a field selected is true?
You almost have it, but in this case, you are setting the value to the selectedOption instead of setting the defaultValue. Also, you are changing the default value each time there is a change, which shouldn't be needed.
const defaultVal = {value: selectedOption, label: selectedOption};
return (
<div>
<Select
multi={true}
name={props.name}
options={makeEmailOptions(props.ccemailfilter)}
defaultValue={defaultVal}
/>
</div>
)
I came with the following solution, since my component use a function to set some variables to the select, i use a useEffect to call that with a filter right after the page render.
useEffect(() => {
handleChange(makeEmailOption(props.ccemailfilter.filter(x => x.selected)))
}, [])
const handleChange = (value) => {
setSelectedOption(value)
props.emails(value)
}
So, the handleChange are called on the onChange of the select and once after the page loads, to create a value to the select to use.

REACT.js initial radio select

I have been trying to modify existing code by adding a few of new functionalities.
I have this function that renders set of radiobuttons based on variable ACCREDITATION_TYPES:
createRadioCalibration(name) {
const { id, value, labels } = this.props;
const _t = this.props.intl.formatMessage;
const ACCREDITATION_TYPES = [
[CALIBRATION_ACCREDITED, _t(messages.calibrationAccredited)],
[CALIBRATION_NOT_ACCREDITED, _t(messages.calibrationNotAccredited)]
];
return <FormChoiceGroup
type = "radio"
values = {ACCREDITATION_TYPES.map(mapValueArray)}
key = {`${id}_${name}`}
name = {`${id}_${name}`}
value = {value[name]}
handleChange = {this.handleFieldChangeFn(name)}
/>;
}
The radios by default are all unchecked. When clicked this function is fired up:
handleFieldChangeFn(name) {
return (e) => {
const { handleFieldChange, id } = this.props;
handleFieldChange(id, name, e.target.value);
};
}
The form is rendered as follows:
render () {
const FIELDS = {
[CALIBRATION]: this.createRadioCalibration(CALIBRATION),
};
return (
<div className="">
<label>{_t(messages.calibration)}</label>
{ FIELDS[CALIBRATION] }
How can I select any option I want as an initial state? There are only two options now but what if there were five?
Also how can I manipulate the radio selection based on onChange event of other elements of the form, namely a drop down menu?
Answering my question I got two minuses for asking: in the constructor one needs to add the following this.props.handleFieldChange( 'id', 'radio_button_group_name', 'value_of_the_output_that_should_be_selected' );
That will select particular radio in a loop.

Resources