React admin custom input component with text input base style - reactjs

I created a DateTimePicker component for my react-admin project:
import { DateTimePicker, DateTimePickerProps, MuiPickersUtilsProvider } from "#material-ui/pickers";
import { FC } from "react";
import { FieldTitle, InputProps, useInput } from "react-admin";
import MomentUtils from "#date-io/moment";
import moment from "moment";
import "moment/locale/fr";
interface DateTimeInputProps extends InputProps<DateTimePickerProps> {
label: string;
}
export const DateTimeInput: FC<DateTimeInputProps> = ({ source, label, resource, options }) => {
const {
input: { value, onChange },
isRequired,
} = useInput({ source });
return (
<MuiPickersUtilsProvider
libInstance={moment}
utils={MomentUtils}
locale='fr'
>
<DateTimePicker
label={<FieldTitle
label={label}
source={source}
resource={resource}
isRequired={isRequired}
/>}
value={value || null}
onChange={onChange}
format="llll"
ampm={false}
{...options}
/>
</MuiPickersUtilsProvider>
);
}
It works great, however the design does not follow the other classic inputs:
What is the simplest way to keep the same design across custom material-ui components?

There are 3 variants to choose from that are applied to MUI input elements: outlined, filled and standard. You need to provide DateTimePicker with inputVariant prop set to filled to get a "greyish" look. Plus you need to pass a className prop so that react-admin can pass parent controled classses (this should fix width issues).
<DateTimePicker {...props} inputVariant="filled" className={props.className} />
You can see the look of outlined, filled and standard clicking link below:
https://mui.com/components/text-fields/#basic-textfield

Related

DatePicker MUI how to display "Clear" text on the left side of the calendar

I have DesktopDatePicker from material-ui(version 5.0.0) and like to display "Clear" text on the left side of the calendar.
import * as React from "react";
import dayjs from "dayjs";
import TextField from "#mui/material/TextField";
import { AdapterDayjs } from "#mui/x-date-pickers/AdapterDayjs";
import { LocalizationProvider } from "#mui/x-date-pickers/LocalizationProvider";
import { DesktopDatePicker } from "#mui/x-date-pickers/DesktopDatePicker";
export default function ResponsiveDatePickers() {
const [value, setValue] = React.useState(dayjs("2022-04-07"));
return (
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DesktopDatePicker
label="For desktop"
value={value}
minDate={dayjs("2017-01-01")}
onChange={(newValue) => {
setValue(newValue);
}}
componentsProps={{
actionBar: { actions: ["clear"], position: "left" }
}}
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
);
}
I added position:"left" in actionBar but this didn't work.
Since it is justified by a flexbox, you will need to add justifyContent: "flex-start" to your action bar style properties.
eg:
componentsProps={{
actionBar: { actions: ["clear"], style: {justifyContent: "flex-start"} }
}}
To further answer the question in the comment, since MUI components all give you access to styling I tried it and it worked - this is the MUIv4 way to access styling and it appears that it still works. The MUIv5 way to do it would likely be to access styling through the sx property that the components now have.
componentsProps={{
actionBar: { actions: ["clear"], sx: {justifyContent: "flex-start"} }
}}
All MUI components give access to styling with the sx property.

Cant wrap <Tooltip> component around <DatePicker> component - Not forwarding props correctly error

Link to code: https://codesandbox.io/s/upbeat-bogdan-2pt7tn?file=/demo.tsx
When I wrap a MUI Tooltip control around a DatePicker I get the following console error:
MUI: The children component of the Tooltip is not forwarding its
props correctly. Please make sure that props are spread on the same
element that the ref is applied to.
in Tooltip (created by BasicDatePicker)
in BasicDatePicker
Here's the code...
import * as React from "react";
import { Dayjs } from "dayjs";
import TextField from "#mui/material/TextField";
import { AdapterDayjs } from "#mui/x-date-pickers/AdapterDayjs";
import { LocalizationProvider } from "#mui/x-date-pickers/LocalizationProvider";
import { DatePicker } from "#mui/x-date-pickers/DatePicker";
import { Tooltip } from "#mui/material";
export default function BasicDatePicker() {
const [value, setValue] = React.useState<Dayjs | null>(null);
return (
<LocalizationProvider dateAdapter={AdapterDayjs}>
<Tooltip
title={"Choose Date"}
placement="bottom"
data-cy={"business-identity-date-of-formation-tooltip"}
>
<DatePicker
label="Basic example"
value={value}
onChange={(newValue) => {
setValue(newValue);
}}
renderInput={(params) => <TextField {...params} />}
/>
</Tooltip>
</LocalizationProvider>
);
}
If I replace the DatePicker with something like an IconButton it works fine.
On my real dev project this is within a Formik form too. The error is a bit different:
Warning: Failed prop type: Invalid prop 'children' supplid to 'ForwardRef(Tooltip)'. Expected an element that can hold a ref. Did you accidently use a plain function component for an element instead?
According to this answer a MUI component should be supported by the Tooltip....
Your Tooltip is not working properly because the child of a Material-UI Tooltip must be able to hold a ref.
The following can hold a ref:
Any Material-UI component
I also saw this solution. But I wanted the Tooltip at the DatePicker level, not at the textbox level.
Do you know how I can fix this issue?
I wrapped the child in a <div> and it works.

How can I apply Proptypes properly on my React component?

I have the following component:
import React from 'react';
import PropTypes from 'prop-types';
import IconButton from '#material-ui/core/IconButton';
import TextField from '#material-ui/core/TextField';
import ClearIcon from '#material-ui/icons/Clear';
const InputSearch = ({ onClearSearch, onSearch, ...searchProps }) => {
const { id, value } = searchProps;
const onClear = (event) => {
onClearSearch(event, id);
};
return (
<TextField
id={id}
name={id}
onChange={onSearch}
value={value}
InputProps={{
endAdornment: value && (
<IconButton
className={classes.searchIcon}
onClick={onClear}
>
<ClearIcon fontSize={'small'} color={'primary'} />
</IconButton>
),
}}
/>
);
};
InputSearch.propTypes = {
onClearSearch: PropTypes.func.isRequired,
};
export default InputSearch;
As you can see, I'm trying to apply a required validation using propTypes. But then, when I try to use the component without the onClearSearch function, no error is being shown:
<InputSearch
value={searchBy}
onSearch={handleSearch}
/>
So what I'm doing wrong?
Your code is right... you can open developer tool of chrome by pressing F12 -> go to Console and you can see prop type error
for more detail you can see
https://blog.logrocket.com/validating-react-component-props-with-prop-types-ef14b29963fc/
If you want your project give prop-type error in terminal then you have to setup eslint in your project.

Validate for Material UI Input without using Formik ValidationSchema

I'm trying to add simple custom validation (non-empty or minimum characters) to a MUI TextField that is part of React functional component using the Formik useField hook. This reusable component will be a part of Formik form in it's parent component. The use of this functional child component is for it to be a reusable component and so the validation for the Textfield should occur within the component instead of the ValidationScheme attribute in the parent component's Formik tag since the form is not predefined. How can I add this custom validation to the TextField within the child component?
import React from 'react';
import { useField } from "formik";
import { TextField, TextFieldProps } from '#material-ui/core';
type FormikTextFieldProps = {
formikKey: string,
} & TextFieldProps
const FormikTextField = ({ formikKey, ...props }: FormikTextFieldProps) => {
const [field, meta, helpers] = useField(formikKey);
const validate = () => {
if(!field.value){
helpers.setError('The field is empty')
}
}
return (
<>
<TextField
id={field.name}
name={field.name}
helperText={meta.touched ? meta.error : ""}
error={meta.touched && Boolean(meta.error)}
value={field.value}
onChange={field.onChange}
{...props}
/>
{meta.touched && meta.error ? (
<div className="error">{meta.error}</div>
) : null}
</>
)
}
export default FormikTextField

Use style functions for material-ui components besides Box

I'm just starting with Material UI. Thanks for bearing with me.
I know you can use things like <Box mx={2}> out-of-the-box (ha). So if I wanted to put a margin around, say, a TextField, I could wrap it in a box.
Is there a simple way to set up my app's theme so that any component can use those style function props? (m, p, display, etc)
So that I could to <TextField mx={2}/> without having to wrap it in a Box.
The docs imply that you can do this:
(the example uses ThemeProvider from styled-components but I'm assuming that MUI's ThemeProvider works the same way???)
import React from 'react'
import { ThemeProvider } from 'styled-components'
const theme = {
spacing: 4,
palette: {
primary: '#007bff',
},
};
export default function App() {
return (
<ThemeProvider theme={theme}>
{/* children */}
</ThemeProvider>
)
}
I've tried this but it crashes from the TextField's my prop:
import { createMuiTheme, TextField, ThemeProvider } from '#material-ui/core';
// Greatly simplified version of my component
const App = () => <TextField my={2}/>
let theme = createMuiTheme({})
export default () =>
<ThemeProvider theme={ theme }>
<App/>
</ThemeProvider>;
I can do something like this and it works:
function App() {
const Input = styled(TextField)(compose(spacing))
return <Input my={3}/>
}
But then I'd have to compose my components every time I want to do use the style functions.
The docs are showing how the theme can parameterize the Box features (e.g. such that a spacing unit is 4px instead of 8px) -- the theme doesn't do anything to enable those features.
Material-UI is intending to support #material-ui/system features on core components in v5, but that is still months away.
Your main options are doing something like you showed in your example (though you should move const Input = styled(TextField)(compose(spacing)) to the top-level rather than doing this within render of App). You could put this in a separate file and import this component instead of TextField whenever you want to use those features. For instance:
MyTextField.js
import TextField from "#material-ui/core/TextField";
import { styled } from "#material-ui/core/styles";
import { compose, spacing } from "#material-ui/system";
export default styled(TextField)(compose(spacing));
App.js
import React from "react";
import TextField from "./MyTextField";
export default function App() {
return (
<div className="App">
<TextField variant="outlined" label="Material-UI system demo" />
</div>
);
}
Another option is to use Box with the clone prop and wrap the component you want to style. For instance:
import React from "react";
import TextField from "#material-ui/core/TextField";
import Box from "#material-ui/core/Box";
export default function App() {
return (
<div className="App">
<Box my={3} clone>
<TextField variant="outlined" label="Box demo" />
</Box>
</div>
);
}
You can also use the component prop of Box:
import React from "react";
import TextField from "#material-ui/core/TextField";
import Box from "#material-ui/core/Box";
export default function App() {
return (
<div className="App">
<Box my={3} component={TextField} variant="outlined" label="Box demo" />
</div>
);
}
Related answers:
Material-UI Grid does not hide whe use Display
Dynamic icon size in Material-UI
Box vs className vs style for vertical spacing in Material UI
Reusable component using theme-based Box features
Using Material-UI Box Component with the Drawer Compoment
How to use override Button using Box component in Material-UI?

Resources