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

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.

Related

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

React Admin - How to disable validation onChange

I'm using react-admin, and I have a huge form, with a bunch of custom validation. It's very slow, even with the build version.
I tried to find a way to disable the validation on change and to have it only on blur or submit. But I have not found a solution or even workaround.
Every time a key is pressed in one of my input text (for example), the validation is triggered multiple times, and it takes a while for the letter to appear.
That's why I want to disable the validation on change.
Here's an example of one of my forms, every letter I write in one of my FormTab, the "validate me" is showing.
export const ThemeCreate: FC = (props: any) => (
<Create {...props} title="ui.themes.create" mutationMode="pessimistic">
<TabbedForm
toolbar={<GenericCreateToolbar />}
validateOnBlur
warnWhenUnsavedChanges
validate={() => {
console.log('validate me!');
}}
>
<MainFormTab />
<TranslationsFormTab />
</TabbedForm>
</Create>
);
You need to use the validateOnBlur={true} prop in the form component.
This prop is from final-form's <Form> component, see the last one in this doc page https://final-form.org/docs/react-final-form/types/FormProps

Create a custom Show Layout component

I'm trying to create a custom layout component, so I can design my Show page better.
I'm not sure why some things work and some don't. My plan is to use the Material-UI <Grid> component. I know that the Grid component doesn't pass the props, then I'm extending to my own:
export default function OrderShow(props) {
return (
<Show {...props}>
<CustomGrid>
<TextField source="shortId" label="Short ID" />
</CustomGrid>
</Show>
);
}
My CustomGrid component, which is just a draft yet, is cloning its children to pass the props:
function CustomGrid(props) {
return React.Children.map(props.children, (child) => React.cloneElement(child, props));
}
When I use it, the TextField receives the source, and renders it correctly. However the label is gone. I'm not sure why this happens.
Another problem is that when I try to use the CustomGrid with a ReferenceField, it doesn't work at all:
<Show {...props}>
<CustomGrid>
<ReferenceField source="user" reference="users" label="Name" link="show">
<FunctionField render={(record) => `${record.firstName} ${record.lastName}`} />
</ReferenceField>
</CustomGrid>
</Show>
I can see that the props are passed until the ReferenceField, but it's lost before reaching the FunctionField:
Is there a better way to customize the SimpleShowLayout and continue using the TextField and other components from react-admin?
Yes, there is a way. Simply put react-admin's SimpleShowLayout is a little bit more coplex then just passing the props to the children fields. That's why you have to recursively iterate until you reach a field and perform the stuff the react-admin does.
Finally I have decided to share some privately developed components in a new package I am building. I have added layouts which work both with Box and Grid material-ui's components. You can have a look here: ra-compact-ui

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

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

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