How to take TimeInput only in React-Admin? - javascript

This code from React-Admin docs allows me to input Date and Time:
import { DateTimeInput } from 'react-admin';
<DateTimeInput source="published_at" />
Is there a way to input ONLY time in React-Admin?

https://mui.com/x/react-date-pickers/time-picker/
import * as React from 'react';
import TextField from '#mui/material/TextField';
import { AdapterDateFns } from '#mui/x-date-pickers/AdapterDateFns';
import { LocalizationProvider } from '#mui/x-date-pickers/LocalizationProvider';
import { TimePicker } from '#mui/x-date-pickers/TimePicker';
export default function BasicTimePicker() {
const [value, setValue] = React.useState<Date | null>(null);
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<TimePicker
label="Basic example"
value={value}
onChange={(newValue) => {
setValue(newValue);
}}
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
);
}

Related

Material-ui Autocomplete defaultValue not working

I tried setting the defaultValue to be "Chairs" and it is not working.
These are the codes:
import React, { useState } from "react";
import TextField from "#mui/material/TextField";
import Autocomplete from "#mui/material/Autocomplete";
import { Items2 } from "./Items2";
export default function ComboBox() {
const [selected, setSelected] = useState("");
console.log(selected);
return (
<>
you selected: {selected}
<br />
<br />
<Autocomplete
disablePortal
isOptionEqualToValue={(option, value) => option?.label === value?.label}
id="combo-box-demo"
options={Items2}
defaultValue="Chairs"
fullwidth
value={selected}
onChange={(event, value) => setSelected(value)}
renderInput={(params) => <TextField {...params} label="Items" />}
/>
</>
);
}
Also in codesandbox: https://codesandbox.io/s/combobox-material-demo-forked-g88fi?file=/demo.js:0-904
You just need to set default value for selected state and remove defaultValue from Autocomplete component:
import React, { useState } from "react";
import TextField from "#mui/material/TextField";
import Autocomplete from "#mui/material/Autocomplete";
import { Items2 } from "./Items2";
export default function ComboBox() {
const [selected, setSelected] = useState("Chairs");
console.log(selected);
return (
<>
you selected: {selected}
<br />
<br />
<Autocomplete
disablePortal
isOptionEqualToValue={(option, value) => option?.label === value?.label}
id="combo-box-demo"
options={Items2}
fullwidth
value={selected}
onChange={(event, value) => setSelected(value)}
renderInput={(params) => <TextField {...params} label="Items" />}
/>
</>
);
}

React component test fails with: TestingLibraryElementError: Found multiple elements by: [data-testid="some-id"]

I couldn't find any solution in any other questions similar to mine so I decided to ask you for help.
I have tests to my <Login/> page:
import React from 'react';
import { render } from '../../__test__/utils';
import Login from '.';
test('Renders the Login page', () => {
const { getByTestId } = render(<Login />);
expect(getByTestId('login-page')).toBeInTheDocument(); //WORKS
});
test('Renders disabled submit button by default', () => {
const { getByTestId } = render(<Login />);
expect(getByTestId('login-button')).toHaveAttribute('disabled'); // ERROR HERE
});
test('should match base snapshot', () => {
const { asFragment } = render(<Login />);
expect(asFragment()).toMatchSnapshot(); //WORKS
});
I am using the 'utils.js' in my tests because I want to wrap every testing component in the providers I am using. Here is how the utils file looks like:
import React, { useState } from 'react';
import { render as rtlRender } from '#testing-library/react';
import '#testing-library/jest-dom/extend-expect';
import { renderRoutes } from 'react-router-config';
import { Router } from 'react-router-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import { ThemeProvider } from '#material-ui/styles';
import { createMemoryHistory } from 'history';
import StylesProvider from '../components/StylesProvider';
import routes from '../routes';
import reducer from '../reducers';
import { theme } from '../theme';
const history = createMemoryHistory();
const render = (
ui,
{ initialState, store = createStore(reducer, initialState), ...renderOptions } = {}
) => {
const Wrapper = ({ children }) => {
const [direction] = useState('ltr');
return (
<Provider store={store}>
<ThemeProvider theme={theme}>
<StylesProvider direction={direction}>
<Router history={history}>
{children}
{renderRoutes(routes)}
</Router>
</StylesProvider>
</ThemeProvider>
</Provider>
);
};
return rtlRender(ui, { wrapper: Wrapper, ...renderOptions });
};
export * from '#testing-library/react';
export { render };
Unfortunately, I cannot show the whole app code so I'll make it short. The <Login /> component looks like below:
import React from 'react';
import { makeStyles } from '#material-ui/styles';
import {
Card, CardContent, CardHeader, Typography, Divider, Grid
} from '#material-ui/core';
import Page from 'src/components/Page';
import LoginForm from './LoginForm';
const Login = () => (
<Page className={classes.root} title="Login" dataTestId="login-page">
//here the component body//
<LoginForm
afterLoginRoute="/overview"
loginEndpoint="login"
userRole={userRoles.USER}
className={classes.loginForm}
/>
</Page>
)
In the <LoginForm /> component I have a Material-UI button with data-testid="login-button"
LoginForm:
return (
<div>
<Snackbar open={loginStatus.error} autoHideDuration={6000}>
<Alert severity="error">
Se ha producido un error, comprueba los credenciales y prueba de nuevo.
</Alert>
</Snackbar>
<form {...rest} className={clsx(classes.root, className)} onSubmit={handleSubmit}>
<div className={classes.fields}>
<TextField
className={classes.input}
error={hasError('email')}
fullWidth
helperText={hasError('email') ? formState.errors.email[0] : null}
label="Email"
name="email"
onChange={handleChange}
value={formState.values.email || ''}
variant="outlined"
/>
<TextField
className={classes.input}
error={hasError('password')}
fullWidth
helperText={hasError('password') ? formState.errors.password[0] : null}
label="Contraseña"
name="password"
onChange={handleChange}
type="password"
value={formState.values.password || ''}
variant="outlined"
/>
</div>
<Button
className={classes.submitButton}
color="secondary"
disabled={!formState.isValid}
size="large"
type="submit"
variant="contained"
data-testid="login-button"
>
Entrar
</Button>
</form>
</div>
);
Current test result I have is TestingLibraryElementError: Found multiple elements by: [data-testid="login-button"]. I am out of ideas right now. Could this happen because I try to pass the data-testid prop to the <Button /> which is imported from Material-UI library? Then, shouldn't I see other error message? Please help.

How to get my withStyles classes to work in my export class component in my React app

I'm trying to apply styles to my React form. I'm using withStytles from Material UI.
The styles however are not taking into affect. I tried testing the code in my first <DialogContentText> in the code below, but it's not working.
EDIT: edited my code to reflect David's answer.
import React, { Component, Fragment } from 'react';
import { withStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
import Dialog from '#material-ui/core/Dialog';
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogContentText from '#material-ui/core/DialogContentText';
import DialogTitle from '#material-ui/core/DialogTitle';
import IconButton from '#material-ui/core/IconButton';
import AddCircleIcon from '#material-ui/icons/AddCircle';
import TextField from '#material-ui/core/TextField';
import FormHelperText from '#material-ui/core/FormHelperText';
import Select from '#material-ui/core/Select';
import MenuItem from '#material-ui/core/MenuItem';
import InputLabel from '#material-ui/core/InputLabel';
import FormControl from '#material-ui/core/FormControl';
const styles = theme => ({
formControl: {
width: 500
},
selectEmpty: {
marginTop: theme.spacing(2),
},});
export default class extends Component {
state = {
open: false,
bucket: {
name:'',
category: '',
about: '',
}
}
handleToggle = () => {
this.setState({
open: !this.state.open
})
}
handleChange = name => ({ target: { value } }) => {
this.setState({
bucket:{
...this.state.bucket,
[name]: value,
}
})
}
render() {
const { open, bucket: { name, category, about } } = this.state;
const { classes } = this.props;
return <Fragment>
<IconButton color="primary" size="large" onClick={this.handleToggle}>
<AddCircleIcon/>
</IconButton>
<Dialog open={open} onClose={this.handleToggle} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Create your Bucket</DialogTitle>
<DialogContent>
<DialogContentText>
Get started! Make your bucket by telling us a little bit more.
</DialogContentText>
<form>
<TextField
id="filled-password-input"
label="Bucket Title"
value={name}
variant="filled"
onChange={this.handleChange('name')}
margin="normal"
required = "true"
className = {classes.formControl}
/>
<br/>
<br/>
<FormControl>
<InputLabel htmlFor="category"> What type of Bucket </InputLabel>
<Select
labelId="demo-simple-select-helper-label"
id="demo-simple-select-helper"
value={category}
onChange={this.handleChange('category')}
required = "true"
>
<MenuItem value={'personal'}> Personal </MenuItem>
<MenuItem value={'social'}> Social </MenuItem>
</Select>
</FormControl>
<FormHelperText>Is this your personal or social Bucket?</FormHelperText>
<TextField
id="filled-password-input"
label="About"
multiline
rows="5"
value={about}
variant="filled"
onChange={this.handleChange('about')}
margin="normal"
/>
<FormHelperText>Will this be your tech stock watchlist?</FormHelperText>
<br/>
<br/>
</form>
</DialogContent>
<DialogActions>
<Button
color="primary"
variant="raised"
onClick={this.handleSubmit}
>
Create Bucket
</Button>
</DialogActions>
</Dialog>
</Fragment>
}
}
export withStyles(styles)(YourComponent);
How would I be able to apply these styles to my code below? Thank you for assistance.
TL;DR: You are trying to use a React Hook const classes = useStyles; within a class component. Also, useStyles is a function, not an object.
NL;PR: Hooks only work on functional components (useStyles() hooks are obtained via makeStyles() instead of withStyles()). You apply HOC withStyles(YourComponent) to your class component not your styles, so you can access const {classes, ...rest} = this.props; in the render() method.
It should look like this:
const styles = theme => ({
formControl: {
width: 500
},
selectEmpty: {
marginTop: theme.spacing(2),
},});
class YourComponent extends PureComponent {
//...
render(){
const {classes, ...rest} = this.props;
// now your can access classes.formControl ...
}
}
export withStyles(styles)(YourComponent);

Too many re-renders in react native app, what can I do?

I have a react native app with expo-client and gives a too-many re-renders error I will post the files, notice that I use react-native-paper.
This is App.js which is a wrapper for app
import React from "react";
import { Provider as PaperProvider, DefaultTheme } from "react-native-paper";
import { StatusBar } from "expo-status-bar";
import Homescreen from "./views/screens/homescreen";
//import { SafeAreaView, Text } from "react-native";
export default function App() {
return (
<PaperProvider theme={theme}>
<Homescreen />
<StatusBar />
</PaperProvider>
);
}
const theme = {
...DefaultTheme,
};
This is the homescreen which is a wrapper for login and sign up components
import React from "react";
import { View } from "react-native";
import { NavigationContainer } from "#react-navigation/native";
import { createStackNavigator } from "#react-navigation/stack";
import SignUp from "../components/signup";
import Login from "../components/login";
import { Button } from "react-native-paper";
export default function Homescreen({ navigation }) {
const Stack = createStackNavigator();
return (
<View>
<Button onPress={() => navigation.navigate("SignUp")}>SignUp</Button>
<Button onPress={() => navigation.navigate("LogIn")}>Login</Button>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="SignUp" component={SignUp} />
<Stack.Screen name="LogIn" component={Login} />
</Stack.Navigator>
</NavigationContainer>
</View>
);
}
There are sign up and login components which are very identical
import React, { useState } from "react";
import { View } from "react-native";
import { TextInput, Button } from "react-native-paper";
export default function SignUp() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const signUp = (t1, t2) => {
setEmail("");
setPassword("");
};
const changeEmailHandler = (e) => {
setEmail(e.target.value);
};
const changePasswordHandler = (e) => {
setPassword(e.target.value);
};
return (
<View>
<TextInput
mode="outlined"
left
label="email"
placeholder="Enter your email: "
onChangeText={changeEmailHandler}
value={email}
/>
<TextInput
mode="outlined"
left
label="password"
placeholder="Enter your password: "
onChangeText={changePasswordHandler}
value={password}
type="password"
/>
<Button
icon="arrow-right-alt"
mode="contained"
onClick={signUp(email, password)}
>
Join us now
</Button>
</View>
);
}
import React, { useState } from "react";
import { View } from "react-native";
import { TextInput, Button } from "react-native-paper";
export default function Login() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const logIn = (t1, t2) => {
setEmail("");
setPassword("");
};
const changeEmailHandler = (e) => {
setEmail(e.target.value);
};
const changePasswordHandler = (e) => {
setPassword(e.target.value);
};
return (
<View>
<TextInput
mode={outlined}
left
label="email"
placeholder="Enter your email: "
onChangeText={changeEmailHandler}
value={email}
/>
<TextInput
mode={outlined}
left
label="password"
placeholder="Enter your password: "
onChangeText={changePasswordHandler}
value={password}
type="password"
/>
<Button
icon="arrow-right-alt"
mode="contained"
onClick={logIn(email, password)}
></Button>
</View>
);
}
This is the appbar component
import React from "react";
import { View } from "react-native";
import { Appbar } from "react-native-paper";
export default function CustomAppBar() {
return (
<View>
<Appbar>
<Appbar.Header>
<Appbar.Content title="Date Planner" />
</Appbar.Header>
</Appbar>
</View>
);
}
The onClick event in the <Button> is the problem. It calls signUp(email, password) on every render which causes an infinite loop because inside there is a call for setPassword. Instead in onClick you can pass a callback, see my suggestion below.
You need to modify your button as:
<Button
icon="arrow-right-alt"
mode="contained"
onClick={() => signUp(email, password)}
>
Join us now
</Button>
In this way the signUp function will be called only on click event.
Based on the comment also needs to change onClick={logIn(email, password)} as well similarly as suggested above.

Testing a simple component is rendered when wrapped by withFormik() using JEST

I have a simple component with some input and dropdown elements, I am trying to test that the elements are rendered. By finding the element 'input' in my expect statement.
Here is what I tried in my test file:
import React from 'react'
import { shallow } from 'enzyme'
import AddUser from '../Components/AddUser'
describe('AddUser', () => {
let wrapper
let mockFetchDetailsActions
let mockHandleCancel
const match = {
params: { id: '1212212' }
}
beforeEach(() => {
mockFetchDetailsActions = jest.fn()
mockHandleCancel = jest.fn()
wrapper = shallow(
<AddUser
match={match}
actions={{
fetchDetailsActions: mockFetchDetailsActions,
handleCancel: mockHandleCancel
}}
/>
)
})
describe('Component rendered', () => {
it('Elements rendered correctly', () => {
expect(wrapper.find('input').length).toBe(6)
})
})
})
Here is my component:
/* eslint-disable no-invalid-this */
import React, { Fragment } from 'react'
import PropTypes from 'prop-types'
import { withStyles } from '#material-ui/core/styles'
import GridContainer from './Grid/GridContainer'
import GridItem from './Grid/GridItem'
import { TextField } from 'formik-material-ui'
import { Field, Form } from 'formik'
import dashboardStyle from '../styles/dashboardStyle'
import Card from './Card/Card'
import CardBody from './Card/CardBody'
import * as Constants from '../actions/actionTypes'
import SaveAndCancelButtons from './Common/saveAndCancelButtons'
class AddUser extends React.Component {
componentDidMount () {
if (this.props.match.params.id) {
this.props.actions.fetchDetailsActions(Constants.FETCH_DETAILS_API_CALL_REQUEST, this.props.match.params.id)
} else {
this.props.actions.handleCancel()
}
}
handleChange = name => event => {
this.props.actions.handleInputChangeAction(name, event.target.value)
}
onSave = () => {
const userDetails = {
user: this.props.values.user
}
if (userDetails && userDetails.user.id) {
this.props.actions.updateDetailsActions(Constants.UPDATE_USER_API_CALL_REQUEST, userDetails.user.id, userDetails)
} else {
this.props.actions.addNewUserAction(Constants.ADD_USER_API_CALL_REQUEST, userDetails)
}
}
handleCancel = () => {
this.props.history.push('/admin_console')
this.props.actions.handleCancel()
}
render () {
const { classes, isFetching } = this.props
return (
<Form>
<Field
name="user"
render={feildProps => (
<Fragment>
<GridContainer>
<GridItem xs={12} sm={12} md={12}>
<Card>
<h2 className={classes.cardTitleWhite}>Add User</h2>
<CardBody isFetching={isFetching}>
<GridContainer>
<GridItem xs={12} sm={12} md={4}>
<Field
label="First Name"
name={`user.first_name`}
className={this.props.classes.textField}
margin="normal"
variant="outlined"
component={TextField}
/>
<Field
label="Secondary Email"
name={`user.email_secondary`}
className={this.props.classes.textField}
margin="normal"
variant="outlined"
component={TextField}
/>
</GridItem>
<GridItem xs={12} sm={12} md={4}>
<Field
label="Last Name"
name={`user.last_name`}
className={this.props.classes.textField}
margin="normal"
variant="outlined"
component={TextField}
/>
<Field
label="Mobile Phone"
name={`user.mobile_phone`}
className={this.props.classes.textField}
margin="normal"
variant="outlined"
component={TextField}
/>
</GridItem>
<GridItem xs={12} sm={12} md={4}>
<Field
label="Email"
name={`user.email`}
className={this.props.classes.textField}
margin="normal"
variant="outlined"
component={TextField}
/>
<Field
label="Work Phone"
name={`user.work_phone`}
className={this.props.classes.textField}
margin="normal"
variant="outlined"
component={TextField}
/>
</GridItem>
</GridContainer>
</CardBody>
</Card>
<SaveAndCancelButtons
handleSave={() => {
this.onSave()
}}
routingLink="/people"
label="Save"
/>
</GridItem>
</GridContainer>
</Fragment>
)}
/>
</Form>
)
}
}
AddUser.propTypes = {
classes: PropTypes.object.isRequired
}
export default withStyles(dashboardStyle)(AddUser)
Here is my withFormik() wrapper:
import { withStyles } from '#material-ui/core/styles'
import { withFormik } from 'formik'
import * as Yup from 'yup'
import AddUser from './AddUser'
const styles = theme => ({
textField: {
width: '100%'
}
})
const validations = Yup.object().shape({
user: Yup.object().shape({
first_name: Yup.string().required('Required'),
last_name: Yup.string().required('Required')
})
})
const withFormikWrapper = withFormik({
validationSchema: validations,
enableReinitialize: true
})(AddUser)
export default withStyles(styles)(withFormikWrapper)
Expected result:
Found 6 elements.
Actual Results:
AddUser › Component rendered › Elements rendered correctly
expect(received).toBe(expected) // Object.is equality
Expected: 6
Received: 0
26 | describe('Component rendered', () => {
27 | it('Elements rendered correctly', () => {
> 28 | expect(wrapper.find('input').length).toBe(6)
| ^
29 | })
30 | })
31 | })
Try this with mount instead of shallow.
I was able to make it work by using mount and also imported the component from withFormikWrapper() instead of importing from its own.
In test file:
before:
import AddUser from '../Components/AddUser'
Now:
import AddUser from '../Components/AddUserWithFormik'

Categories