How to set initialValue for dynamic form field (ant design)? - javascript

I am struggling with setting the initialValue for the dynamic form field. Lets say we have this example from AntDesign docs.
https://codesandbox.io/s/wonderful-lichterman-br63z?file=/index.js
and the point is, to set the initial value for every newly added fields, in this example i want the "last name" to be set to "kowalski".
following ant design docs :
"In most case, we always recommend to use Form initialValues. Use Item initialValue only when dynamic field usage"
so i was trying to set the initialValue but in result i only get this warning :
"React does not recognize the initialValue prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase initialvalue instead. If you accidentally passed it from a parent component, remove it from the DOM element."
thanks for any advice.

I found an easier implementation. Turns out add method allows one to set field defaultValue.
<Form.Item>
<Button
type="dashed"
onClick={() => {
add({ lastName: 'mejia', firstName: 'cesar' })
}}
style={{ width: '100%' }}
>
<PlusOutlined /> Add field
</Button>
</Form.Item>
See:
https://codesandbox.io/s/fervent-morning-c9vhk

Related

Im using AntD components library. i have 2 <Form.List> nested inside another <Form.List> and im having trouble to setFieldsValue to nested inputs

I've created a reproducable example. Here is the link: https://codesandbox.io/s/size-antd-5-1-6-forked-315hum?file=/demo.tsx
So i have to set the fields value, but the last nested
<Form.List name={[concatenationName, "options"]}>
is kind of bugged. Because if we watch the logs, the value is set properly. But the value is not rendered in the input.
Here is how it´s rendered:
And here are the values that shows the form watcher:
What im expecting is that those options, actually shows the value of the input. It's like it gets the value, but it doesnt render the value. I had no problems with other <Form.List>, but this one seems to be kind of bugged.
Any suggestions why is not rendering the value in the input. And why all the other inputs, when i do the setFieldsValue.
I've added a DEBUG button to do easier debugging. It basically setFieldsValue to all inputs, and with it we can see how the last nested inputs dont render its value, but other inputs do render the value.
I found the error, and it was <Form.Item></Form.Item> not wrapping directly the input, so it wasnt able to set the value
<Form.Item
{...concatenationOptionRestField}
name={[concatenationOptionsName, 'content']}
rules={[
{
required: true,
message: 'Porfavor complete el campo',
},
]}
>
<Input
style={{
width: '100%',
}}
placeholder="Ingresa opción de respuesta"
/>
</Form.Item>

react-admin ReferenceField label not showing when used in a custom component

So I need a ReferenceField to access data from another table. Since I am doing this often i extracted this in a custom component
CustomField.tsx
const CustomField = (props: any) => {
const record = useRecordContext();
return (
<ReferenceField
record={record}
source="someId"
reference="table"
link="show"
label="some label"
{...props}
>
<TextField source="name" />
</ReferenceField>
);
};
now when i use the component:
<CustomField/>
everithing is working fine, data is displayed fine, except no label is shown.
So I am expecting some form of label at the top but no signs.
If I do it without creating a custom field, everything is working just fine, the label and sorting is there. But when I extract the code into a separate component it doesn't seem to work.
Looks like the attributes lose their default values and behavior when extracted to a separate component.
My current workaround
<OrganisationField label="Some label" sortBy="something" />
that is fine, it works but it's not practical (and it's annoying) to do this everytime I or someone else wants to use the component, since that should already be defined inside it.
When you say "no label is shown", I assume that's when you use your custom Field inside a Datagrid.
Datagrid inspects its children for their label prop to display the column header. To make your label inspect-able, declare it as defaultProp:
CustomField.defaultProps = {
label: "someId"
}
This is explained in the react-admin "writing a custom field" documentation: https://marmelab.com/react-admin/Fields.html#writing-your-own-field-component

Testing material-ui TextField by role using react-testing-library

I have some components which include some mui TextFields, and there are two situations for my components:
One TextField is designed for LicenseCode and it can't have a label.
Also there are some TextFields that will be created via the map function also I can't use the label for each of them.
So, I can't use getByRole for testing them.
To mitigate this in testing I've tried some solutions, But I think there should be a better way. These are my founded solutions:
I've disabled eslint and use documentQuerySecletor for that.
/*eslint-disable */
const activationCodeInputs = document.querySelectorAll('.codeItem input');
expect(activationCodeInputs).toHaveLength(5);
const systemIdTextarea = document.getElementById('systemId');
expect(systemIdTextarea).not.toBeNull();
/*eslint-enable */
Also, Find an article that used data-testid and passed it to TextField viainputProps
// code
<TextField
inputProps={{ 'data-testid': 'activationCode' }}
/>
<TextField
inputProps={{ 'data-testid': 'systemId' }}
/>
// tests
const activationCodeInputs = screen.getAllByTestId('activationCode');
expect(activationCodeInputs).toHaveLength(5);
const systemIdTextarea = screen.getByTestId('systemId');
expect(systemIdTextarea).toBeInTheDocument();
Since it is just a text field which is a regular element, Do I have to write tests with getByRole only as the doc says the first preferred way, is it?
For Material UI and React Testing I've just used a label on the Textfield and used getByLabelText for testing to get the input with that label
<TextField
label="input label"
variant="outlined"
/>
screen.getByLabelText(/^input label/i)
If you don't have a label associated to the TextField and there are multiple TextFields rendered in a list, using and querying via a testid is just fine.
If you have trouble finding the best selector you can always use screen.logTestingPlaygroundURL() after you rendered your component in a test. This will give you a URL to the testing library playground where you can check the best selectors for your rendered elements.

Component re-render issue [Snippet Attached]

I am making a resume builder application and the whole structure was almost done.
Complete working codesandbox:
Here I have made components into stepper for each section,
index.js
<form onSubmit={handleSubmit}>
<Stepper
steps={sections}
activeStep={currentPage}
activeColor="red"
defaultBarColor="red"
completeColor="green"
completeBarColor="green"
/>
{currentPage === 1 && (
<>
<BasicDetails />
<button onClick={next}>Next</button>
</>
)}
{currentPage === 2 && (
<>
<EmploymentDetails />
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<button onClick={prev}>Back</button>
<button onClick={next}>Next</button>
</div>
</>
)}
{currentPage === 3 && (
<>
<pre>{JSON.stringify(value, null, 2)}</pre>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<button onClick={prev}>Back</button>
<button onClick={handleSubmit}>Submit</button>
</div>
</>
)}
</form>
Steps to reproduce issue:
-> In Step 1 Enter First Name, Last Name, and Profile Summary
-> Click on Next button to move to next step.
-> Then click the back button to move backward to Step 1 (Currently in Step 2)
-> Here the values in First Name, Last Name are there but the value entered in text editor alone gets lost
Please refer the below image with text editor value entered for better understanding of the problem I am facing..
Text editor value alone gets lost if we switch forth/back the steps. But the entered value is stored in the form Context and not rendered in the Text Editor box.
Note:
Text editor is made as a component and it was used in Step 1 (For profile summary) and also in Step 2 (For employment description) and in both cases switching between steps, the value entered inside the text editor gets lost.
Analysis:
Based on my understanding, this happens because on navigating to other steps, the component get re-rendered and the EditorContainer component gets called and in text_editor.js it was given
this.state = {
editorState: EditorState.createEmpty(),
};
So it was created as empty.
So how can I control the component from getting re-rendered so that the data entered in text editor won't get lost.
Kindly please help me to retain the values entered inside the text editor. Big thanks in advance..
This happens because we're only saving our EditorContainer value to our Context, but we're not using it when we rerender the EditorContainer component.
The fix would be to pass the saved value as prop to our EditorContainer component.
Then before we render the EditorContainer, we'll convert that value to EditorState which can be done using convertFromHTML function, and set that as our editorState state.
Step 1: Pass value prop to EditorContainer
// basic_details.js
<EditorContainer
name="profileSummary"
value={basicDetails.profileSummary}
onChange={(event) => handleInputChange(event)}
/>
// employment_details.js
<EditorContainer
name="description"
value={inputField.description}
onChange={(event) => handleInputChange(index, event)}
/>
Step 2: Convert the value prop to EditorState
// text_editor.js
...
componentDidMount() {
// https://draftjs.org/docs/api-reference-data-conversion/#convertfromhtml
const { value } = this.props;
const blocksFromHTML = convertFromHTML(value);
const state = ContentState.createFromBlockArray(
blocksFromHTML.contentBlocks,
blocksFromHTML.entityMap,
);
const editorState = EditorState.createWithContent(state);
this.setState({ editorState });
}
That's it! Check the demo below.
Edit Fix demo to check value if is string.
This is a great question. This is basically a design approach issue: For a scenario like yours, you need to design your component this way:
let's analyze the picture:
MAIN COMPONENT:
This is the component that should hold the state for the entire form-filling process. STE1-4 Are just views that allows you to input data that must all be updated in the main component. So This means, you must have state in main component and pass the state properties and props, including their update/setter methods.
STEP COMPONENT
This applies for all Step Components.
These components should do nothing except display the form step using state values received via props and update state by using setter methods, which are also received via props.
Conclusion:
Put your state in your main component, each step component should only display the form and update the main state. This means that by the time each step component is re-rendered, it will receive values updated in the main component. And you will achieve that via props.
It's very simple - you need to store editors state in your parent component. Try to use BasicDetails state for this.

Material UI Autocomplete component not showing values from react state

I am trying to get value from the state for materialUI's autocomplete component.
I am facing the following problem : -
Autocomplte working fine for selecting the value and with onChange function it saving it into the state too.
But when I refresh my page/ re-render it is not showing value on the textfeild(from saved state):
<Autocomplete
name={"TideLocation"}
disabled={p.disabled}
options={data_source}
getOptionLabel={option => option.text}
inputValue={this.state.tidelocation_searchtext}
onChange={_this.handleUpdateTideLocationField}
onNewRequest={_this.handleChangeTideLocation}
onBlur={_this.handleBlurTideLocationField}
onUpdateInput={_this.handleUpdateTideLocationField}
renderInput={(params) => (
<TextField className="autoCompleteTxt"{...params} label="Location" />
)}
/>
I tried with the debugger and found its getting value in this.state.tidelocation_searchtext
but failed to set it with params.
Thanks in advance !!
Ps: I tried with defaultValue and search text nothing worked for me
following is my ONchangeFunction
handleUpdateTideLocationField = (str, value) => {
debugger
this.setState({tidelocation_searchtext: value.text});
}
after selecting a value,following value saved in sate :
tidelocation_searchtext: "Auckland"
So I found the solution all by myself by doing research and several hit and try, following is the solution of my problem:
<Autocomplete
name={"TideLocation"}
disabled={p.disabled}
options={data_source.map(option=>option.text)}
defaultValue={this.state.tidelocation_searchtext}
onChange={_this.handleUpdateTideLocationField}
onNewRequest={_this.handleChangeTideLocation}
onBlur={_this.handleBlurTideLocationField}
onUpdateInput={_this.handleUpdateTideLocationField}
renderInput={(params) => (
<TextField className="autoCompleteTxt"{...params} label="Location" />
)}
/>
Basically I was doing the following things wrong :
1.There is no need of using input inputValue={this.state.tidelocation_searchtext}& getOptionLabel={option => option.text}
2.as my data is in object form I have to convert it into a string so default value can match this from the state value
options={data_source.map(option=>option.text)}
Thank you all for your valuable support and solution !!
Removing inputValue has worked for me, even when passing object as options.
Using: "#material-ui/core": "^4.12.3"
If data/state not saved externally f.e. in local storage then it will be lost on page refresh, always. It's normal - like RAM memory without power - it (page/app state) only exists in memory !!
It's like using using cookie to keep you logged in.
If you really need such functionality then use f.e. redux-persist
You are right, if object type options are passed, material-ui's AutoComplete component does not seem to reflect the value when mounted. (Perhaps a bug?)
I was able to get around this by passing the proper characters to inputValue.
#RebelCoder
Maybe you should have initialized tidelocation_searchtext.
For me it was coming from the dropdown z-index which was hidden by another css behaviour.
I added this in a css file :
/* Dropdown MUI Component Autocomplete*/
div[role="presentation"].MuiAutocomplete-popper {
z-index: 1000000;
}
And it appeared finally. A bit hacky, but I think it was caused by another library that had something of that kind.
Note that I added several css elements to the selector, because just using the class wasn't enough.

Categories

Resources