React Dropbox Component using Material-UI-Next - reactjs

Is there a way to nest a dropdown component with MenuItem? I would like to declare one component whenever I need to use a drop down instead of nesting two.
Am I on the right path or should drop down components be handled differently than normal React components?
App.js (this currently works nesting MenuItem within DropDown)
<DropDown
label="Drop Down Label"
value={this.state.selectedState}
onChange={this.handleChange}
>
{stateAbbreviationData.map(option => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</DropDown>;
App.js (how I would like to use my component, does not work)
Shows code (data.map(option => ( ))} ) instead of a list when clicked. Further code below.
<DropDown
label="Drop Down Label"
value={this.state.selectedState}
onChange={this.handleChange}
menuData="stateAbbreviationData"
menuKey={stateAbbreviationData.value}
menuValue={stateAbbreviationData.value}
menuOptionLabel={stateAbbreviationData.label}
/>;
DropDown.js
import React from "react";
import TextField from "material-ui/TextField";
import DropDownMenu from "./DropDownMenu";
const styles = {
minWidth: 175
};
class DropDown extends React.Component {
render() {
return (
<TextField
select
label={this.props.label}
value={this.props.value}
style={styles}
onChange={this.props.onChange}
margin="normal"
InputLabelProps={{
shrink: true
}}
menuData={this.props.menuData}
>
<DropDownMenu
key={this.props.menuKey}
value={this.props.menuValue}
optionLabel={this.props.menuOptionLabel}
/>
</TextField>
);
}
}
export default DropDown;
DropDownMenu.js
import React from "react";
import MenuItem from "material-ui/Menu/MenuItem";
const styles = {
minWidth: 175
};
class DropDownMenu extends React.Component {
render() {
return (
<div>
{this.props.menuData.map(option => (
<MenuItem key={this.props.key} value={this.props.value}>
{this.props.optionLabel}
</MenuItem>
))}
</div>
);
}
}
export default DropDownMenu;
StateAbbreviationData.js
const stateAbbreviationData = [
{
value: "AL",
label: "AL"
},
{
value: "AK",
label: "AK"
}
// ...
];
export default stateAbbreviationData;

I think your first example is the one that you want to create with your DropDown component. Try removing DropDownMenu component, and doing all of the logic inside DropDown:
import React from "react";
import TextField from "material-ui/TextField";
import MenuItem from "material-ui/Menu/MenuItem";
const styles = {
minWidth: 175
};
class DropDown extends React.Component {
render() {
return (
<TextField
select
label={this.props.label}
value={this.props.value}
style={styles}
onChange={this.props.onChange}
margin="normal"
InputLabelProps={{
shrink: true
}}
>
{this.props.menuData.map(option => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</TextField>
);
}
}
export default DropDown;
And then you can use it the way you want to:
<DropDown
label="Drop Down Label"
value={this.state.selectedState}
onChange={this.handleChange}
menuData={stateAbbreviationData} // <-- Put in braces
/>
Note:
With Material UI, I've used MenuItem components inside of the Select component, rather than the TextField component like you're doing:
https://material-ui.com/demos/selects/
There were a couple issues with your existing code I'll point out.
As I indicated above, you need to pass the data stateAbbreviationData into the DropDown component's props using curly braces:
<DropDown
label="Drop Down Label"
value={this.state.selectedState}
onChange={this.handleChange}
menuData={stateAbbreviationData} // <-- This change
menuKey={stateAbbreviationData.value}
menuValue={stateAbbreviationData.value}
menuOptionLabel={stateAbbreviationData.label}
/>
There's another bug with the way you're setting the menuKey, menuValue, and manuOptionLabel props. With the code you included, it seems like stateAbbreviationData is an array of objects.
In the props, you're trying to access properties on that array that don't exist: stateAbbreviationData.value and stateAbbreviationData.label. The objects in the array have those bits, but the array itself doesn't.
<DropDown
label="Drop Down Label"
value={this.state.selectedState}
onChange={this.handleChange}
menuData={stateAbbreviationData}
menuKey={stateAbbreviationData.value} // <-- Doesn't exist
menuValue={stateAbbreviationData.value} // <-- Doesn't exist
menuOptionLabel={stateAbbreviationData.label} // <-- Doesn't exist
/>
There's also a bug where you're passing menuData to the TextField component, but instead you should pass it to DropDownMenu.
That said, I recommend going with the first solution I proposed. Good luck!

Related

Paasing props to MUI Box component overrider (Typescript)

I cant work out how to pass props to Box component override.
I need to pass position="end"" as required by InputAdornment but cant find how in the docs.
Full component is
<Select
value={value}
onChange={handleChange}
input={
<OutlinedInput
endAdornment={
photoRequired && (
<Box component={InputAdornment} position="end" pr={3}>
{required && <Gallery />}
<Gallery />
</Box>
)
}
/>
}
>
{choices.map((choice, i) => (
<MenuItem key={i} value={i + 1}>
{choice}
</MenuItem>
))}
</Select>
I am getting error trying to pass in the way above as its not expected on Box.
Warning: Failed prop type: The prop `position` is marked as required in `ForwardRef(InputAdornment)`, but its value is `undefined`.```
Try creating your own InputAdornment component that uses the position, such as:
const EndInputAdornment = () => {
return <InputAdornment position="end"/>
};
Then you can use that component to the Box:
<Box component={EndInputAdornment} pr={3}>
...
or if you don't want to create a separate component:
<Box component={<InputAdornment position="end"/>} pr={3}>
should work

How to change variant of text input in react admin

I wanted to change the variant of TextInput using material UI. The default variant is Standard and I want to change that to outlined but not able to do so. Followed documentation for material-ui Material-ui documentation
Below is my code snippet
<TabbedForm>
<FormTab label="INFO">
{/* <TextInput disabled label="" source="id" type="hidden"/> */}
<TextInput label="Name" source="name" variant="outlined"/>
<TextInput source="shortdesc" />
</FormTab>
</TabbedForm>
You need to pass in variant="standard" to the react-admin <TextInput> component. The issue is that the <Filter> and <Create> pages, etc, automatically pass in a bunch of props, like record and basePath, which includes variant: undefined for some reason. So if you just write <TextInput variant="standard" ... />, your prop will get overwritten. And then when the TextInput component passes its props to the ResettableTextField component, the now undefined variant prop gets defaulted to "filled"...
So, you just need to extract the TextInput component out, so that your variant prop comes last in the props order:
export const TextInput = props => <RaTextInput {...props} variant="standard" />
Personally, I've abstracted this all out into a HOC, because this is what you have to do for all the Inputs that use TextFields:
import React from 'react'
import {
TextInput as RaTextInput,
NumberInput as RaNumberInput,
SelectInput as RaSelectInput,
DateInput as RaDateInput,
DateTimeInput as RaDateTimeInput,
NullableBooleanInput as RaNullableBooleanInput,
AutocompleteInput as RaAutocompleteInput,
} from 'react-admin'
const standardize = Component => props => <Component {...props} variant="standard" />
export const TextInput = standardize(RaTextInput)
export const NumberInput = standardize(RaNumberInput)
export const SelectInput = standardize(RaSelectInput)
export const DateInput = standardize(RaDateInput)
export const DateTimeInput = standardize(RaDateTimeInput)
export const NullableBooleanInput = standardize(RaNullableBooleanInput)
export const AutocompleteInput = standardize(RaAutocompleteInput)
Check my code
<TabbedForm>
<FormTab label="INFO">
{/* <TextInput disabled label="" source="id" type="hidden"/> */}
<TextField label="Name" source="name" variant="outlined"/>
<TextField source="shortdesc" />
</FormTab>
</TabbedForm>
check my fork
React-admin has fixed this.
You can now use the variant="outlined | standard | filled" prop on the component enclosing the input.
Ex: SimpleForm, Create, Filter, etc.
The Git resolution

React Semantic UI - How to disable a child component based on parent

How does one disable the input box here, depending on whether a checkbox has been checked or not?
I have verified the checked property - it is in working order (shows true or false boolean values).
However, I can't seem to get the disabled property in Input to work.
Below is my code:
import React from 'react';
import { Button, Checkbox, Input, List } from 'semantic-ui-react'
import PropTypes from 'prop-types';
const CategoryCheckBox = ({ type = 'checkbox', name, mylabel, checked, onChange }) => (
<React.Fragment>
<List horizontal relaxed>
<List.Item>
<Checkbox style={{ paddingLeft:"1em", paddingBottom: "1em" }} label={mylabel} checked={checked} onChange={onChange} />
<List.Content>
<Input style={{ maxWidth:"9em", paddingLeft:"1em" }} size='mini' focus placeholder='Min' disabled={checked === true ? true : false} />
<Input style={{ maxWidth:"9em", paddingLeft:"1em" }} size='mini' focus placeholder='Max' />
</List.Content>
</List.Item>
</List>
</React.Fragment>
);
CategoryCheckBox.propTypes = {
type: PropTypes.string,
name: PropTypes.string.isRequired,
mylabel: PropTypes.string.isRequired,
checked: PropTypes.bool,
onChange: PropTypes.func.isRequired,
}
export default CategoryCheckBox;
From the main program,
The component is called with the below parameters:
<CategoryCheckBox name={item.value} mylabel={item.text} checked={this.state.checkedItems.get(item.value)} onChange={this.handleChange} />
Below is my screenshot for the component along with React debugger showing the checked value.
Any Help will be highly appreciated.
Tried to set up most of the important code - Newbie to React myself. Can't get the index.js in the working order. But it gives you a good idea of my code https://codesandbox.io/embed/2pyoxpr6rn?fontsize=14
Changes:
1- You are not assigning the name to CheckBox element in CategoryCheckBox, add name here:
<Checkbox
style={{ paddingLeft: "1em", paddingBottom: "1em" }}
label={mylabel}
// ↓↓↓↓↓↓↓↓ here
name={name}
checked={checked}
onChange={onChange}
/>
2- Add disable condition for Max input field also:
<Input
style={{ maxWidth: "9em", paddingLeft: "1em" }}
size="mini"
focus
placeholder="Max"
// ↓↓↓↓↓↓↓↓ here
disabled={!checked}
/>
3- Most importantly, You are storing data in states in App component so it needs to be a class component.
Check Working Codesandbox with all these changes.
After looking at your code the problem is with your app component. Your app component has a state and therefore cannot be a stateless functional component. Your App component should look something like this.
import React from "react";
import ReactDOM from "react-dom";
import { Checkbox, Label, Header, Card, Form, Button, Container, Icon, Step, Select, Dropdown, List } from 'semantic-ui-react';
import womensBoutiqueOptions from "./womensBoutiqueOptions";
import CategoryCheckBox from "./CategoryCheckBox";
import "./styles.css";
class App() extends React.Component {
state = {
date: new Date(),
time: '10:00',
checkedItems: new Map()
}
handleChange(e) {
const item = e.target.name;
const isChecked = e.target.checked;
this.setState(prevState => ({ checkedItems: prevState.checkedItems.set(item, isChecked) }));
}
render() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Form size="large" key="large">
<h4 className="ui dividing header">Womens Information</h4>
<Form.Group widths='equal'>
<Form.Field>
<React.Fragment>
<List horizontal >
{
womensBoutiqueOptions.map(item => (
<List.Item key={item.key}>
<List.Content>
<CategoryCheckBox name={item.value} mylabel={item.text} checked={this.state.checkedItems.get(item.value)} onChange={this.handleChange} />
</List.Content>
</List.Item>
))
}
</List>
</React.Fragment>
</Form.Field>
</Form.Group>
</Form>
</div>
)
}
);
}
Check out the react documentation for some more information on why

Add custom column to Datagrid in admin-on-rest

I've created an admin panel in React.js using admin-on-rest. I have a page showing a user list pulling data from an api. I need to add a custom column at the end of each row of that table that will display a progress bar for each user.
I'm using a module called rc-progress to generate the progress bar. Here's an example display on a separate page currently from the user list:
import React from 'react';
import { Line } from 'rc-progress';
var Progress = React.createClass({
render: function render() {
return (
<Line percent="40" strokeWidth="1" strokeColor="#38bcd5" />
);
}
});
export default Progress;
I'm not sure how to append a custom column to a Datagrid in admin-on-rest to add in that progress bar.
Here's the code I have for the User list which is displaying the data from the API correctly. I've added a comment where I'd want to add the progress bar:
import React from 'react';
import { List, Datagrid, TextField} from 'admin-on-rest';
import { Line } from 'rc-progress';
export const UserList = (props) => (
<List {...props}>
<Datagrid>
<TextField source="firstName" />
<TextField source="lastName" />
<TextField source="email" />
<TextField source="phone" />
<TextField source="type" />
//I'd like to add the progress bar below:
//<Line percent="40" strokeWidth="1" strokeColor="#38bcd5" />
</Datagrid>
</List>
);
export default UserList;
Any help is much appreciated!
Update (Solution)
So on the recommendation of Gildas I've tried to use the FunctionField. Here is the working version of the code. (The progress bar has hard-coded values right now)
import React from 'react';
import { List, Datagrid, TextField} from 'admin-on-rest';
import { Line } from 'rc-progress';
import { FunctionField } from 'admin-on-rest'
export const UserList = (props) => (
<List {...props}>
<Datagrid>
<TextField source="firstName" />
<TextField source="lastName" />
<TextField source="email" />
<TextField source="phone" />
<TextField source="type" />
//Here is the working version below:
<FunctionField label="Progress" render= {function render() { return ( <Line percent="40" strokeWidth="1" strokeColor="#38bcd5" />);}} />
</Datagrid>
</List>
);
export default UserList;
Here's a current screenshot:
The FunctionField should do the trick: https://marmelab.com/admin-on-rest/Fields.html#functionfield
If not, have you tried following the custom field documentation ?

How to detect if React Material UI Select Field is expanded?

I am trying to determine if SelectField is expanded or not, i.e. if the dropdown and it's MenuItems are visible.
Currently I use roughly following approach:
<SelectField onClick={() => this.setState({ isExpanded: true })} >
<MenuItem primaryText={
<MenuItemContent onHide={() => this.setState({ isExpanded: false })}} />
}>
</SelectField>
and in MenuItemContent I implement
class MenuItemContent extends React.Component {
componentWillUnmount = () => this.props.onHide()
}
This approach has a drawback, that when you click outside the menu, the componentWillUnmount call is not triggered immidiately, but cca after 200ms, despite the MenuItems are no longer visible.
The Select field menu can be expanded by clicking it, and when is expanded, it will hide either when some menu item is clicked, or user clicks outside the menu.
This is how the SelectField looks when is expanded:
And here is collapsed:
For the better handling of SelectField close event you can use property dropDownMenuProps:
/**
* Object that can handle and override any property of component DropDownMenu.
*/
dropDownMenuProps: PropTypes.object,
By using this prop we can pass to DropDownMenu component onClose prop with handler function, which will be fired instantly after DropDownMenu close (caused by ESC, outside click or item select). Unfortunately DropDownMenu component dont provide similar prop to determine the opening so the only way (without
extending the component) is to follow your approach with onClick event handler.
Here is my working test example:
onSelectClose = () => {
console.log("close")
}
onSelectOpen = () => {
console.log("open")
}
render() {
return (
<MuiThemeProvider>
<div className="App">
<SelectField
floatingLabelText="Frequency"
onClick={this.onSelectOpen}
dropDownMenuProps={{
onClose: this.onSelectClose
}}
value={this.state.value}
onChange={this.handleChange}>
<MenuItem value={1} primaryText="Never" />
<MenuItem value={2} primaryText="Every Night" />
<MenuItem value={3} primaryText="Weeknights" />
<MenuItem value={4} primaryText="Weekends" />
<MenuItem value={5} primaryText="Weekly" />
</SelectField>
</div>
</MuiThemeProvider>
);
}
Detecting if React Material UI Select Field is expanded
The answer is available in Materia-UI Select-API documentation.
Link: https://material-ui.com/api/select/

Resources