How to get selected and creatable value in AutoComplete (Material UI). I bind Data from onChange method to setState but this is not what I expected - material-ui

I want to get AutoComplete onChange data. I got data from onChange((e,value) => console.log(value).
But how to put data on State .
This AutoComplete component behaviour is (Creatable and selectable) . I got all data on onChange but on in the state, If I set the data on State , selectOnFocus is not working.
How to get rid of that?
Thank you
`
<Autocomplete
multiple
limitTags={5}
onChange={(event, newValue) => {
setToEmails(newValue);
}}
filterOptions={(options, params) => {
const filtered = filter(options, params);
if (params.inputValue !== "") {
filtered.push({
inputValue: params.inputValue,
label: `Add "${params.inputValue}"`,
value: nanoid(7),
});
}
return filtered;
}}
selectOnFocus
blurOnSelect
clearOnBlur
handleHomeEndKeys
id="free-solo-with-text-demo"
options={toBlockers}
getOptionLabel={(option) => {
if (typeof option === "string") {
return option;
}
if (option.inputValue) {
return option.inputValue;
}
return option.label;
}}
renderOption={(option) => option.label}
freeSolo
renderInput={(params) => (
<TextField {...params} label="Blockers" variant="outlined" />
)}
/>
`
If I use this , Selectable is not working.

Later on I got solved by myself . This took about 3 days ,
here is full example of Creatable with selectable with formData thing.
const [toEmails, setToEmails] = useState([]);
//if you want to post this data on formData , you have structured this way
const appendedObject = [
{ label: "_creative", value: "node_process_Lh1L8RsafJbUas" },
{ label: "Azim__bro", value: "node_process_LhL1s8RaafJbUas" },
];
const finalBlockers = [...appendedObject, ...toEmails];
formData.append("attachmentList", []);
console.log({ toEmails, finalBlockers });
if (finalBlockers) {
for (const blocker of finalBlockers) {
console.log({ blocker });
formData.append("blockers", JSON.stringify(blocker));
}
}
<Autocomplete
multiple
limitTags={5}
getOptionSelected={(option, value) => {
const optionTitle =
typeof option === "string" ? option : option.value;
const valueTitle =
typeof value === "string" ? value : value.value;
return optionTitle === valueTitle;
}}
onChange={(event, newValue) => {
if (Array.isArray(newValue)) {
const updatedArrayValue = newValue.filter((e) =>
typeof e === "string" ? e.trim() : e
);
const newArrayValue = [...updatedArrayValue];
const updatedArray = newArrayValue.map((item) => {
if (typeof item === "string") {
const newItem = {};
newItem.label = item;
newItem.value = nanoid(12);
newItem.type = "created";
return newItem;
}
return item;
});
setToEmails(updatedArray);
}
}}
filterOptions={(options, params) => {
const filtered = filter(options, params);
if (params.inputValue !== "") {
filtered.push({
inputValue: params.inputValue,
title: `Add "${params.inputValue}"`,
value: nanoid(12),
type: "created",
});
}
return filtered;
}}
selectOnFocus
clearOnBlur
handleHomeEndKeys
id="free-solo-with-text-demo"
options={toBlockers}
filterSelectedOptions
getOptionLabel={(option) => {
// Value selected with enter, right from the input
if (typeof option === "string") {
return option;
}
// Add "xxx" option created dynamically
if (option.inputValue) {
return option.inputValue;
}
// Regular option
return option.label;
}}
renderOption={(option) => option.label}
freeSolo
renderInput={(params) => (
<TextField {...params} label="Blockers" variant="outlined" />
)}
/>

Related

fireEvent.click with parameters

I have a radio button that when I click it calls a function with two parameters in the function (value and step), and when I make a test with testing librairy I would like that when I simulate the click of the button I return the two parameters.
Button
<Form.Check
inline
label={'form.email'}
name="media"
value={`LETTER`}
type="radio"
data-testid="EMAIL"
onClick={(e) => {
const target = e.target as HTMLTextAreaElement;
changeMedia(target.value, data);
}}
/>
Function ChangeMedia
const changeMedia = (value: any, step: any) => {
step.definition?.steps
.filter((obj: any) => obj.media.includes(value))
.map((item: any) => {
if (value === 'EMAIL') {
item.media = 'LETTER';
return item;
} else if (value === 'LETTER') {
item.media = 'EMAIL';
return item;
}
return item;
});
return data;
};
Objet Step
const step = {
definition: {
steps: [
{
document_model: 'template_circularization',
email_model: 'email_circularization',
label: {
EN: 'Circularization',
FR: 'Circularisation',
},
media: 'LETTER',
name: 'mail_circularization',
onDemandOnly: true,
},
],
},
};
Test
const radio = screen.getByTestId('EMAIL');
fireEvent.click(radio, { target: { value: 'EMAIL, step: step } });
when I do a console.log of value and step I get this :
value EMAIL
step {}
what would be the solution to retrieve the step object via firevent.click ?

react-map-gl-draw addFeatures creating duplicates

Describe the Issue
When feature is deleted and new feature is added for vertices to update. The new feature is created twice. Every time this cycle is repeated.
Function deleteVertex have the the code of this problem.
Actual Result
new duplicate feature is added.
Expected Result
Only 1 new feature should be created.
Reproduce Steps
Add a feature. Remove the Feature any 1 index of coordinates. Remove the old Feature. Add the this new modified version of Feature using addFeatures.
Question
Also I want to know if there is any easy way to add or remove the vertices.
Code
const MapOverLay = ({
type,
viewport,
setViewport,
save,
close,
previousFeatures,
defaultViewPort,
}) => {
const editorRef = useRef(null);
const featureRef = useRef(null);
const locateIconRef = useRef(false);
const [previousFeature, setPreviousFeature] = useState(previousFeatures);
const [mapData, setMapData] = useState({
editor: {
type: previousFeatures.length ? 'Editing' : '',
mode: previousFeatures.length ? new EditingMode() : null,
selectedFeatureIndex: null,
},
});
const onSelect = map => {
if (map.selectedEditHandleIndex !== null) {
featureRef.current = {
type: 'selectedEditHandleIndex',
index: map.selectedEditHandleIndex,
featureIndex: map.selectedFeatureIndex,
};
}
else {
featureRef.current = {
type: 'selectedFeatureIndex',
index: map.selectedFeatureIndex,
};
}
};
const onUpdate = ({ editType, data }) => {
if (editType === 'addFeature') {
const temp_data = makeClone(mapData);
temp_data.editor.type = 'Editing';
temp_data.editor.mode = new EditingMode();
setMapData(temp_data);
}
if (previousFeatures.length) {
setPreviousFeature(data);
}
};
const deleteVertex = () => {
const feature = editorRef.current.getFeatures()[0];
if (feature.geometry.coordinates[0].length !== 2) {
feature.geometry.coordinates[0].splice(featureRef.current.index, 1);
editorRef.current.deleteFeatures(featureRef.current.featureIndex);
editorRef.current.addFeatures(feature);
console.log('Delete');
}
else {
editorRef.current.deleteFeatures(featureRef.current.featureIndex);
}
featureRef.current = null;
};
// console.log(editorRef.current);
const deleteFeature = () => {
if (previousFeature.length) {
setPreviousFeature([]);
}
else {
editorRef.current.deleteFeatures(featureRef.current.index);
}
save([]);
};
const onDelete = () => {
if (featureRef.current !== null) {
switch (featureRef.current.type) {
case 'selectedEditHandleIndex':
deleteVertex();
return;
case 'selectedFeatureIndex':
deleteFeature();
return;
default:
break;
}
}
};
const onSave = map => {
save(map.getFeatures());
};
const getDrawModeStyle = () => {
switch (type) {
case 'Polygon':
return style.polygon;
case 'Point':
return style.point;
}
};
const getDrawModePressedStyle = () => {
switch (type) {
case 'Polygon':
return style.pressed_polygon;
case 'Point':
return style.pressed_point;
}
};
const getDrawMode = () => {
switch (type) {
case 'Polygon':
return new DrawPolygonMode();
case 'Point':
return new DrawPointMode();
}
};
const onToolClick = () => {
if (editorRef.current.getFeatures().length === 0) {
const temp_data = makeClone(mapData);
if (type === temp_data.editor.type) {
temp_data.editor.type = '';
temp_data.editor.mode = null;
setMapData(temp_data);
}
else {
temp_data.editor.type = type;
temp_data.editor.mode = getDrawMode();
setMapData(temp_data);
}
}
};
const locate = map => {
locateIconRef.current = true;
const features = map.getFeatures();
if (features.length) {
const center = centerOfMass(features[0]);
setViewport(prevState => ({
...prevState,
zoom: 10,
longitude: center.geometry.coordinates[0],
latitude: center.geometry.coordinates[1],
}));
}
else {
defaultViewPort();
}
};
const viewPortChangeFromMap = nextViewport => {
locateIconRef.current = false;
setViewport(nextViewport);
};
const _renderDrawTools = () => {
// copy from mapbox
return (
<>
<div className='mapboxgl-ctrl-top-left'>
<div className={['mapboxgl-ctrl-group mapboxgl-ctrl', style.navigation_group_top_left].join(' ')}>
<button
className="mapbox-gl-draw_ctrl-draw-btn"
title="Save"
onClick={() => onSave(editorRef.current)}
/>
<button
id={getDrawModeStyle()}
className={
[
'mapbox-gl-draw_ctrl-draw-btn mapbox-gl-draw_polygon',
mapData.editor.type === type ? getDrawModePressedStyle() : '',
].join(' ')
}
title="Select"
onClick={() => onToolClick()}
/>
<button
className="mapbox-gl-draw_ctrl-draw-btn mapbox-gl-draw_trash"
title="Remove"
onClick={onDelete}
/>
</div>
</div>
<div className='mapboxgl-ctrl-top-right'>
<div className={['mapboxgl-ctrl-group mapboxgl-ctrl', style.navigation_group_top_right].join(' ')}>
<button
className="mapbox-gl-draw_ctrl-draw-btn"
title="Close"
onClick={close}
/>
</div>
</div>
<div className='mapboxgl-ctrl-bottom-right'>
<div className={['mapboxgl-ctrl-group mapboxgl-ctrl', style.navigation_group_bottom_right].join(' ')}>
<button
className="mapbox-gl-draw_ctrl-draw-btn"
title="Focus"
onClick={() => locate(editorRef.current)}
/>
</div>
</div>
</>
);
};
return (
<ReactMapGL
{...viewport}
mapStyle="mapbox://styles/giddyops/ckips5wdw61xb17qs3eorsoj9"
mapboxApiAccessToken={MAPBOX_TOKEN}
onViewportChange={viewPortChangeFromMap}
attributionControl={false}
transitionDuration={1000}
transitionInterpolator={locateIconRef.current ? new FlyToInterpolator() : null}
>
<Editor
ref={editorRef}
mode={mapData.editor.mode}
clickRadius={12}
features={previousFeature.length ? previousFeature : null}
onSelect={onSelect}
onUpdate={onUpdate}
editHandleShape={'circle'}
featureStyle={getFeatureStyle}
editHandleStyle={getEditHandleStyle}
onClick={e => console.log(e)}
/>
{_renderDrawTools()}
</ReactMapGL>
);
};
I have reviewed your code and found out that it could be issue that delete features was finished deleting way after than adding feature.
you can solve this issue by making the following changes:
make deleteVertex function async.
cloning the feature in the first line.
await the editorRef.current.deleteFeatures(featureRef.current.featureIndex);

How to use setFieldValue and pass the value as props between components

I'm trying to use ant design forms in my sample registration form, when i'm trying to use setFieldsValue it throws error as "Cannot use setFieldsValue unless getFieldDecorator is used". But I had already used getFieldDecorator in my code.Here is a sample of my code.
handleChange = (e) => {
const fname = e.target.name;
const fvalue = e.target.value;
this.props.setFieldsValue({
fname: fvalue
});
}
render(){
const { getFieldDecorator } = this.props.form
return (
<Row gutter={4}>
<Col className="reg-personal-details-grid-column" span={24}>
<FormItem {...formItemLayout} label="First Name">
{getFieldDecorator("firstName", {
rules: [
{
required: true
}
]
})(
<Input
placeholder="First Name"
required
name="firstName"
onChange={this.handleChange}
/>
)}
</FormItem>
</Col>
</Row>
)
}
fname is not defined in getFieldDecorator.
You should do this:
handleChange = (e) => {
const fname = e.target.name;
const fvalue = e.target.value;
this.props.form.setFieldsValue({
[fname]: fvalue
});
}
You can create your own function to do this:
export const setFieldValue = (
form: FormInstance,
name: NamePath,
value: string
): void => {
if (form && form.getFieldValue(name)) {
const fixname: string[] = [];
if (typeof name == 'object') {
name.forEach((node: string) => {
fixname.push(node);
});
} else {
fixname.push(String(name));
}
let fieldsValue: unknown;
fixname.reverse().forEach((node: string) => {
fieldsValue = {
[String(node)]: fieldsValue != undefined ? fieldsValue : value,
};
});
form.setFieldsValue(fieldsValue);
}
};
and you can use like that
setFieldValue(form, 'phone', '1111111111');
or
setFieldValue(form, ['phones', 'mobile'], '2222222222');
In Antd v4, you only required to call the setFieldsValue in useEffect function.
const [form] = Form.useForm()
useEffect(() => {
//companyInfo -> dynamic obj fetched from API
form.setFieldsValue(companyInfo);
}, [companyInfo, form]);
Then in Form use the form prop as follow:
<Form
//passing form prop
form={form}
labelCol={{ span: 8 }}
wrapperCol={{ span: 14 }}
onFinish={onFinish}
labelAlign="left"
colon={false}
>
you can use it like this
const formRef = useRef(null);
useEffect(() => {
formRef.current?.setFieldsValue({
name: data?.firstName,
});
}, [data]);
return (
<Form ref={formRef}>
<Form.Item name="name">
<Input/>
</Form.Item>

Update values in a pair of text fields

I have a form component:
import React from 'react'
import Relay from 'react-relay'
import { browserHistory } from 'react-router'
import MenuItem from 'material-ui/MenuItem'
import TextField from 'material-ui/TextField'
import CreateTransformationSetMutation from '../mutations/CreateTransformationSetMutation'
class CreateTransformationSetDialog extends React.Component {
componentWillMount() {
this.props.setOnDialog({
onSubmit: this.onSubmit,
title: 'Create and Apply Transformation Set'
})
}
initial_state = {
targetTableName: '',
transformations: this.props.viewer.version.columns.edges.map(edge => edge.node).map(column => {
return {
targetColumnName: column.name,
ruleExpression: '{{' + column.name + '}}'
}
})
}
state = this.initial_state
onSubmit = () => {
const onSuccess = (response) => {
browserHistory.push('/table')
}
const onFailure = () => {}
Relay.Store.commitUpdate(
new CreateTransformationSetMutation(
{
viewer: this.props.viewer,
version: this.props.viewer.version,
targetTableName: this.state.targetTableName,
transformations: JSON.stringify({transformations: this.state.transformations}),
}
),
{ onSuccess: onSuccess }
)
}
handleTableNameChange = (e) => {
this.setState({
targetTableName: e.target.value
})
}
handleTransChange = (e) => {
////what should go here////
}
render() {
return (
<div>
<TextField
floatingLabelText="Table Name"
value={this.state.targetTableName}
onChange={this.handleTableNameChange}
/>
<br />
{
this.props.viewer.version.columns.edges.map((edge) => edge.node).map((column, i) =>
<div key={column.id}>
<TextField
name="targetColumnName"
floatingLabelText="Target Column"
value={this.state.transformations[i].targetColumnName}
onChange={this.handleTransChange}
style={{ margin: 12 }}
/>
<TextField
name="ruleExpression"
floatingLabelText="Rule to Apply"
value={this.state.transformations[i].ruleExpression}
onChange={this.handleTransChange}
style={{ margin: 12 }}
/>
</div>
)
}
</div>
)
}
}
export default Relay.createContainer(CreateTransformationSetDialog, {
fragments: {
viewer: () => Relay.QL`
fragment on Viewer {
${CreateTransformationSetMutation.getFragment('viewer')}
version(id: $modalEntityId) #include(if: $modalShow) {
${CreateTransformationSetMutation.getFragment('version')}
id
name
columns(first: 100) {
edges {
node {
id
name
}
}
}
}
}
`
},
initialVariables: {
modalEntityId: '',
modalName: '',
modalShow: true,
},
prepareVariables: ({ modalEntityId, modalName }) => {
return {
modalEntityId,
modalName,
modalShow: modalName === 'createTransformationSet'
}
}
})
Each input is a set of two material-ui TextFields that have a default value obtained from this.state.transformations. However, I am trying to update the values in these fields and I am not having any luck.
The default state of transformations will look something like this:
transformations: [
{targetColumnName: 'ID', ruleExpression: '{{ID}}'},
{targetColumnName: 'First Name', ruleExpression: '{{FirstName}}'},
{targetColumnName: 'Last Name', ruleExpression: '{{LastName}}'}
]
And I want to be able to update it so that the state can change, for example to:
transformations: [
{targetColumnName: 'ID', ruleExpression: '{{ID}}'},
{targetColumnName: 'First Name', ruleExpression: '{{FirstName}}'},
{targetColumnName: 'Percentage', ruleExpression: '{{Percentage/100}}'}
]
The form is built using Relay but that is not relevant to this question.
Any help with this would be much appreciated.
Thanks for your time.
I had a similar problem and instead I put the value in value attribute, uses defaultValue='some-value' so that it can be mutated and changed.
It seems that what you are trying to do is not possible to protect from XSS (Cross-site scripting)
Write like this:
<TextField
name="targetColumnName"
floatingLabelText="Target Column"
defaultValue={this.state.transformations[i].targetColumnName} {/* change from value to defaultValue */}
onChange={this.handleTransChange}
style={{ margin: 12 }}
/>

Getting values from form input groups

I have a group of form inputs that are produced like so:
Please see UPDATE 2 below for full component.
So, if there are three columns then three of this group will be shown in the form. I am trying to extract the data from these inputs but I only need the ids. How can I extract the column id from the TextField?
Also, I need to get the data (i.e. the ids) per group so that they appear in an array:
transformations: [{columnId: 1, ruleId: 4}, {columnId: 2, ruleId: 2}, {columnId:3 , ruleId: 1}]
These are just example values, the main problem as I mentioned, is that I'm not sure how to extract the value of the columnId from the first input. I'm also struggling with getting the multiple sets of data.
Any help with this problem would be much appreciated.
Thanks for your time.
UPDATE:
handleRuleChange looks like this:
handleRuleChange = (e, index, value) => {
this.setState({
ruleId: value
})
}
UPDATE 2:
Here is the component:
import React from 'react'
import Relay from 'react-relay'
import { browserHistory } from 'react-router'
import SelectField from 'material-ui/SelectField'
import MenuItem from 'material-ui/MenuItem'
import TextField from 'material-ui/TextField'
import CreateTransformationSetMutation from '../mutations/CreateTransformationSetMutation'
class CreateTransformationSetDialog extends React.Component {
componentWillMount() {
this.props.setOnDialog({
onSubmit: this.onSubmit,
title: 'Create and Apply Transformation Set'
})
}
initial_state = {
targetTableName: '',
ruleId: 'UnVsZTo1',
}
state = this.initial_state
onSubmit = () => {
const onSuccess = (response) => {
console.log(response)
browserHistory.push('/table')
}
const onFailure = () => {}
Relay.Store.commitUpdate(
new CreateTransformationSetMutation(
{
viewer: this.props.viewer,
version: this.props.viewer.version,
targetTableName: this.state.targetTableName,
transformations: ///this is where I need to get the values///,
}
),
{ onSuccess: onSuccess }
)
}
handleTextChange = (e) => {
this.setState({
targetTableName: e.target.value
})
}
handleRuleChange = (e, index, value) => {
this.setState({
ruleId: value
})
}
render() {
return (
<div>
<TextField
floatingLabelText="Table Name"
value={this.state.targetTableName}
onChange={this.handleTextChange}
/>
<br />
{
this.props.viewer.version.columns.edges.map((edge) => edge.node).map((column) =>
<div key={column.id}>
<TextField
id={column.id}
floatingLabelText="Column"
value={column.name}
disabled={true}
style={{ margin: 12 }}
/>
<SelectField
floatingLabelText="Select a Rule"
value={this.state.ruleId}
onChange={this.handleRuleChange}
style={{ margin: 12 }}
>
{
this.props.viewer.allRules.edges.map(edge => edge.node).map(rule =>
<MenuItem
key={rule.id}
value={rule.id}
primaryText={rule.name}
/>
)
}
</SelectField>
</div>
)
}
</div>
)
}
}
export default Relay.createContainer(CreateTransformationSetDialog, {
fragments: {
viewer: () => Relay.QL`
fragment on Viewer {
${CreateTransformationSetMutation.getFragment('viewer')}
version(id: $modalEntityId) #include(if: $modalShow) {
${CreateTransformationSetMutation.getFragment('version')}
id
name
columns(first: 100) {
edges {
node {
id
name
}
}
}
}
allRules(first: 100) {
edges {
node {
id
name
}
}
}
}
`
},
initialVariables: {
modalEntityId: '',
modalName: '',
modalShow: true,
},
prepareVariables: ({ modalEntityId, modalName }) => {
return {
modalEntityId,
modalName,
modalShow: modalName === 'createTransformationSet'
}
}
})
It is using Relay but that isn't connected to the question, just need to extract the data from the inputs into the transformations array.
This can meet your requirement. Most of code will be understandable. feel free to ask for queries.
class CreateTransformationSetDialog extends React.Component {
componentWillMount() {
this.props.setOnDialog({
onSubmit: this.onSubmit,
title: 'Create and Apply Transformation Set'
})
}
initial_state = {
targetTableName: '',
transformations: [];
ruleId:'UnVsZTo1' //default values for all rules
}
state = this.initial_state
onSubmit = () => {
const onSuccess = (response) => {
console.log(response)
browserHistory.push('/table')
}
const onFailure = () => {}
Relay.Store.commitUpdate(
new CreateTransformationSetMutation(
{
viewer: this.props.viewer,
version: this.props.viewer.version,
targetTableName: this.state.targetTableName,
transformations: this.state.transformations,
}
),
{ onSuccess: onSuccess }
)
}
handleTextChange = (e) => {
this.setState({
targetTableName: e.target.value
})
}
handleRuleChange = (index, ruleId, columnId) => { //TODO: make use of index if needed
let transformations = this.state.transformations;
const isInStateWithIndex = transformations.findIndex((el) => el.columnId === columnId);
if(isInStateWithIndex > -1){
transformations[isInStateWithIndex].ruleId = ruleId; //changed rule
}else{
transformations.push({columnId: columnId, ruleId: ruleId}); //new column added to state.
}
this.setState({
transformations: transformations
}); //do with es6 spread operators to avoid immutability if any
}
render() {
return (
<div>
<TextField
floatingLabelText="Table Name"
value={this.state.targetTableName}
onChange={this.handleTextChange}
/>
<br />
{
this.props.viewer.version.columns.edges.map((edge) => edge.node).map((column) =>
<div key={column.id}>
<TextField
id={column.id}
floatingLabelText="Column"
value={column.name}
disabled={true}
style={{ margin: 12 }}
/>
<SelectField
floatingLabelText="Select a Rule"
value={this.state.ruleId}
onChange={(e, index, value) => this.handleRuleChange(index, value, column.id )}
style={{ margin: 12 }}
>
{
this.props.viewer.allRules.edges.map(edge => edge.node).map(rule =>
<MenuItem
key={rule.id}
value={rule.id}
primaryText={rule.name}
/>
)
}
</SelectField>
</div>
)
}
</div>
)
}
}
Maintaining the state for transformations in state with dynamically created columns.