I have an MUI table and I made it editable with custom component. However, I am having problem with the MUI DesktopDatePicker. Whenever I change the value of the date, picking from the calendar or manual input, the component just closes. I think it might be due to the fact that I am using useState as it triggers a re-render. I have tried using useRef which would not cause the component to close but the value won't be updated when picking from calendar. Also, the default value of the date component should be the current value of the date in the selected row. Right now, I am using new Date() as a default.
this is the code for my column:
const [dateValue, setDateValue] = React.useState(new Date());
const handleDateChange = (newValue) => {
setDateValue(newValue)
}
const columns = [
{
title: "Sequence",
field: "sequence",
editable: "never",
render: rowData => {
return (
<Typography>
{rowData.sequence }
</Typography >
)
},
},
{
title: "Activity Name",
field: "activityName",
editable: "never",
render: rowData => {
return (
<Typography>
{rowData.activityName}
</Typography >
)
},
},
{
title: "Date",
field: "activityDate",
render: rowData => {
return (
<Typography>
{ rowData.activityDate ? moment(rowData.activityDate).format('DD-MMM-yyyy') : '' }
</Typography >
)
},
editComponent: (props) => (
<DesktopDatePicker
label="Date"
inputFormat="dd/MMM/yyyy"
value={ dateValue}
onChange={ handleDateChange }
renderInput={(params) => <TextField {...params} />}
/>
)
},
{
title: "Cost (Import)",
field: "costImport",
editable: "never",
render: rowData => {
return (
<Typography>
{ rowData.costImport ? (rowData.costImport)?.toLocaleString('en', {maximumFractionDigits: 2, minimumFractionDigits: 2 }) : '' }
</Typography >
)
},
},
{
title: "Cost",
field: "cost",
render: rowData => {
return (
<Typography>
{rowData.cost ? Number(rowData.cost)?.toLocaleString('en', { maximumFractionDigits: 2, minimumFractionDigits: 2 }) : '' }
</Typography >
)
},
editComponent: (props) => {
return (
<TextField
type="number"
decimalScale='2'
value={props.rowData.cost}
onChange={e => props.onChange(e.target.value)}
defaultValue={props.rowData.cost}
/>
)
}
},
{
title: "Status",
field: "activityStatus",
render: rowData => {
return (
<Typography>
{rowData.activityStatus ? rowData.activityStatus : ''}
</Typography >
)
},
editComponent: (props) => {
return (
<Select
value={props.rowData.activityStatus}
onChange={e => props.onChange(e.target.value)}
>
<MenuItem value={'Canceled'}>Cancled</MenuItem>
<MenuItem value={'Finish'}>Finish</MenuItem>
<MenuItem value={'Open'}>Open</MenuItem>
<MenuItem value={'Process'}>Process</MenuItem>
</Select>
)
}
},
];
Image of UI after selecting date from calendar:
Related
In menu/ the name of my invited people are not diplayed there is only the InfoIcon in the Cell. I want to create a Popover, when you click on the InfoIcon, you get all the info of the invited people(name and location).
export default function Display() {
const { dishes } = JsonData;
const [anchor, setAnchor] = useState(null);
const openPopover = (event) => {
setAnchor(event.currentTarget);
};
const data = useMemo(
() => [
...
{
//Problem: participants not displayed and click not working
Header: "Invited",
id: "invited",
accessor: (row) => row.invited.map(({ name }) => name).join(", "),
Cell: (props) => (
<div>
<InfoIcon />
<Popover
open={Boolean(anchor)}
anchorEl={anchor}
anchorOrigin={{
vertical: "top",
horizontal: "left"
}}
transformOrigin={{
vertical: "bottom",
horizontal: "right"
}}
>
<Typography variant="h1">{props.participants}</Typography>
</Popover>
</div>
)
},
],
[]
);
return (
<Table
data={dishes}
columns={data}
/>
);
}
Here is my code
In addition to saving the clicked element into state so the Popover component has an element ref it needs to also store in state which specific row's participants to render into the popover. Currently the code is using a singular boolean value for all the popovers. Use the row.id to open a specific popover.
Don't forget to add the "anchor" state to the dependency array so the popover gets the latest state.
function Display() {
const { menus } = JsonData;
const [anchorId, setAnchorId] = useState(null);
const [anchorEl, setAnchorEl] = useState(null);
const openPopover = id => (event) => {
setAnchorId(id);
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorId(null);
setAnchorEl(null);
};
const data = useMemo(
() => [
{
Header: "Id",
accessor: (row) => row.id
},
{
Header: "Invited",
id: "invited",
accessor: (row) => row.invited,
Cell: (props) => (
<div>
{props.value.map(({ name }) => name).join(", ")}
<InfoIcon onClick={openPopover(props.row.id)} />
<Popover
open={anchorId === props.row.id}
onClose={handleClose}
anchorEl={anchorEl}
anchorOrigin={{
vertical: "top",
horizontal: "left"
}}
transformOrigin={{
vertical: "bottom",
horizontal: "right"
}}
>
<Typography variant="h6">
{props.value.map(({ name, location }) => (
<div key={name}>
<p>{name}</p>
<p>Location: {location}</p>
</div>
))}
</Typography>
</Popover>
</div>
)
},
{
Header: "Title",
accessor: (row) => ({ title: row.title, id: row.id }),
Cell: ({ value }) => (
<Link to={{ pathname: `/menu/${value.id}` }}>{value.title}</Link>
)
}
],
[anchorEl, anchorId]
);
const initialState = {
sortBy: [
{ desc: false, id: "id" },
{ desc: false, id: "invited" },
{ desc: false, id: "title" }
]
};
return (
<Table
data={menus}
columns={data}
initialState={initialState}
withCellBorder
withRowBorder
withSorting
withPagination
/>
);
}
The child component will add data and the parent component will display the latest data that the child component inserted, my problem is the useState didnt work properly, because the data is successfully saved but i cant see the data inserted in the parent component, need to refresh or load the page to see the latest data.
Parent component
const fetchData = async () => {
const response = await getDepartment('fmDepartments', 'fmDepartmentsId', 'All', 100, 0);
console.log(response)
response.data.map(function(u){
newUsers.push({
sequence:u.sequence,
name: u.name,
datecreated: u.datecreated,
dateupdated: u.dateupdated,
status: u.status,
actions: ([
<div className={classes.root}>
{u.status !== 1 ?(
<Tooltip title={<span style={{fontSize: 15, fontWeight: 600,}}>Deactivate</span>} arrow>
<IconButton
aria-label="Deactivate"
color="primary"
size="small"
className={classes.deactivateButton}
onClick={() => {handleActivate(u.id)}}
>
<HighlightOffIcon />
</IconButton>
</Tooltip>
):(
<Tooltip title={<span style={{fontSize: 15, fontWeight: 600,}}>Activate</span>} arrow>
<IconButton
aria-label="Active"
color="primary"
size="small"
className={classes.activateButton}
onClick={() => {handleDeactivate(u.id)}}
>
<CheckCircleOutlineIcon />
</IconButton>
</Tooltip>
)
}
</div>
])
})
})
setNewUserLists(newUsers)
}
useEffect(() => {
fetchData();
}, [reRender]);
const options = {
filter: true,
filterType: 'checkbox',
selectableRows: 'none',
print: false,
customToolbar: () => {
return (
<CustomToolbar/> //here is the link to the child component
)
},
setFilterChipProps: (colIndex, colName, data) => {
return {
color: 'primary',
variant: 'outlined',
className: 'testClass123',
};
}
};
return (
<>
<MUIDataTable
title={"Department"}
data={newUserLists}
columns={columns}
options={options}
/>
</>
)
child component
class CustomToolbar extends React.Component {
constructor(props) {
super(props)
this.HandleAdd = this.HandleAdd.bind(this);
this.state =false //here
}
HandleAdd = () => {
Swal.fire({
title: 'Add Department',
text: "Input department name below.",
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Save',
html: generateInputForms({
strname: '',
intsequence: ''
}),
preConfirm: () => {
let strname = document.getElementById('strname').value;
let intsequence = document.getElementById('intsequence').value;
if (!strname) {
Swal.showValidationMessage('The Department field is required.')
}
if (!intsequence) {
Swal.showValidationMessage('The Sequence field is required.')
}
return {
strname: document.getElementById('strname').value,
intsequence: document.getElementById('intsequence').value
}
}
}).then((result) => {
if (result.isConfirmed) {
let request = {
strresourcename: "Richard",
strapplicationcode: "SchoolApp",
strmodulename: "Department",
strtablename: "fmDepartments",
strfieldid: "fmDepartmentsId",
strname:document.getElementById('strname').value,
intsequence:document.getElementById('intsequence').value
}
addDepartment(request).then(res =>{
if (res.status == 200){
Swal.fire({
icon: 'success',
title: 'Department',
text: 'New Department has been added successfully.',
}).then(res => {
this.setState(!this.state); //here
})
}else{
Swal.fire({
icon: 'error',
title: 'Oops',
text: 'Something went wrong.',
})
}
})
}
})
}
render() {
const { classes } = this.props;
return (
<React.Fragment>
<Tooltip title={"Add"}>
<Button
variant="contained"
color="primary"
size="small"
style={{
textTransform: 'unset',
outline: 'none',
marginLeft: 20,
backgroundColor: '#00B029',
}}
onClick={this.HandleAdd}
className={classes.button}
startIcon={<AddIcon className={classes.addIcon} style={{color: '#fff',}} />}
>
Add
</Button>
</Tooltip>
</React.Fragment>
);
}
}
*I use the ref property in the flatlist component in react native, but I get an undefined is not an
object error. I looked at many places on
the internet but did not get a satisfactory answer. Please help me :)*
const DATA = [
{
id: 0,
title: "First Item",
},
{
id: 1,
title: "Second Item",
},
];
const [selectedId, setSelectedId] = useState(null);
**React.useEffect(() => {
this.el.scrollToIndex({ animated: true, index:selectedId});
}, []);**
const Item = ({ item, onPress, style }) => (
<TouchableOpacity onPress={onPress} style={[styles.item, style]}>
<Text style={styles.title}>{item.title}</Text>
</TouchableOpacity>
);
const renderItem = ({ item }) => {
const backgroundColor = item.id === selectedId ? "#6e3b6e" : "#f9c2ff";
return (
<Item
item={item}
onPress={() => setSelectedId(item.id)}
style={{ backgroundColor }}
/>
);
};
return (
<>
<FlatList
data={DATA}
horizontal={true}
renderItem={renderItem}
keyExtractor={(item) => item.id}
extraData={selectedId}
**ref={(el) => this.el = el}**strong text
/>
</>
)
}```
To use a ref, you need to use useRef (since you're on a function component). On function components you won't find yourself using this. When using useRef, you'll need to use .current to access the item itself. So, you can see I'm doing el.current.scrollToIndex
Also, you'll need to add selectedId as a dependency to your useEffect
export default function App() {
const DATA = [
{
id: 0,
title: "First Item",
},
{
id: 1,
title: "Second Item",
},
{
id: 2,
title: "Second Item",
},
{
id: 3,
title: "Second Item",
},
{
id: 4,
title: "Second Item",
},
{
id: 5,
title: "Second Item",
},
{
id: 6,
title: "Second Item",
},
];
const [selectedId, setSelectedId] = React.useState(null);
const el = React.useRef()
React.useEffect(() => {
el.current.scrollToIndex({ animated: true, index:selectedId});
}, [selectedId]);
const Item = ({ item, onPress, style }) => (
<TouchableOpacity onPress={onPress} style={[styles.item, style]}>
<Text style={styles.title}>{item.title}</Text>
</TouchableOpacity>
);
const renderItem = ({ item }) => {
const backgroundColor = item.id === selectedId ? "#6e3b6e" : "#f9c2ff";
return (
<Item
item={item}
onPress={() => setSelectedId(item.id)}
style={{ backgroundColor }}
/>
);
};
return (
<>
<FlatList
data={DATA}
horizontal={true}
renderItem={renderItem}
keyExtractor={(item) => item.id}
extraData={selectedId}
ref={el}
/>
</>
)
}
I formerly have a checkbox like this
renderCategoriesMD() {
const { classes, categoriesArray } = this.props;
const { checkedCategories, reset } = this.state;
if (categoriesArray && categoriesArray.length > 0) {
return categoriesArray.map((cat, index) => {
return (
<div>
<FormControlLabel
key={index}
control={
<Checkbox
tabIndex={-1}
onClick={() => this.handleToggleCats(index)}
checked={checkedCategories.indexOf(index) !== -1}
checkedIcon={<Check className={classes.checkedIcon} />}
icon={<Check className={classes.uncheckedIcon} />}
classes={{
checked: classes.checked,
root: classes.checkRoot,
}}
/>
}
classes={{ label: classes.label }}
label={
<Typography style={{ fontSize: 13 }}>
{cat.name || ""}
</Typography>
}
/>
{!reset && (
<div style={{ marginLeft: 10 }}>
{this.renderUnderSubCategoriesMD(cat)}
</div>
)}
</div>
);
});
}
return null;
}
What this does is populate options of checkboxes like the image below.
what i want to achieve now is to have react-select of multiple options so instead of checkboxes i can select the options from react select.
<Select
value={this.state.value}
name="filter__statistics"
options={options}
onChange={this.handleChange}
multi={true}
/>
I recommend you to use the npm packages, it is very easy to use and implement. npm react-select
There is an example
import React from 'react';
import Select from 'react-select';
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' },
];
class App extends React.Component {
state = {
selectedOption: null,
};
handleChange = selectedOption => {
this.setState({ selectedOption });
console.log(`Option selected:`, selectedOption);
};
render() {
const { selectedOption } = this.state;
return (
<Select
value={selectedOption}
onChange={this.handleChange}
options={options}
/>
);
}
}
I am new to react so I might be missing something
I am using react-select to store multiple elements and am using the map function to display elements which is working fine. But when I am using the same element in another class to display in a list element it shows a blank.
Here is the code where I am displaying the multiple options.
const Departments = [
{ label: "OneIT", value: "OneIT" },
{ label: "HR", value: "HR" },
{ label: "Vigilance", value: "Vigilance" },
{ label: "Ethics", value: "Ethics" },
{ label: "Corporate Services", value: "Corporate Services" },
{ label: "Legal", value: "Legal" },
{ label: "Sports", value: "Sports" },
{ label: "TQM", value: "TQM" },
{ label: "Iron Making", value: "Iron Making" },
{ label: "TMH", value: "TMH" },
];
class MultiSelect2 extends Component {
state = {
selectedOptions: [],
};
handleChangeField = (selectedOptions) => {
this.setState({ selectedOptions });
};
render() {
const { selectedOption } = this.state;
return (
<div className="container">
<div className="row">
<div className="col-md-2"></div>
<div className="col-md-8">
<span>Select Department</span>
<Select
value={selectedOption}
options={Departments}
onChange={this.handleChangeField}
isMulti
/>
{this.state.selectedOptions.map((o) => (
<p>{o.value}</p>
))}
</div>
<div className="col-md-4"></div>
</div>
</div>
);
}
}
I am trying to display this in another class in the list item but it is not showing.
export class Confirm extends Component {
state = {
selectedOptions: [],
};
render() {
const {
values: { selectedOptions },
} = this.props;
return (
<List>
<ListItemText primary="Departments" secondary={selectedOptions} />
</List>
);
}
}
I know I might be missing something very basic but please help
#Kubwimana Adrien
Here is the rest of the code:
export class Confirm extends Component {
state = {
rows: [],
idx: [],
selectedOptions: []
};
continue = e => {
e.preventDefault();
//Process Form//
this.props.nextStep();
};
back = e => {
e.preventDefault();
this.props.prevStep();
};
render() {
const {
values: {
Title,
Details,
What,
Why,
How,
Status,
Cost,
Benefits,
Kpi_Before,
Kpi_After,
Time,
dateTime,
Base_Before,
Target_Before,
UOM_Before,
idx,
selectedOptions
}
} = this.props;
return (
<MuiThemeProvider theme={theme}>
<React.Fragment>
<div className={useStyles.root}>
<AppBar position="static">
<Toolbar>
<Typography
gutterBottom
align="center"
style={{ width: "100%", alignItems: "center" }}
>
Confirm Information
</Typography>
</Toolbar>
</AppBar>
</div>
<br />
<h3>Are you sure to continue and confirm your information?</h3>
<List>
<ListItemText primary="Departments" secondary={selectedOptions} />
<ListItemText primary="Title" secondary={Title} />
<ListItemText primary="Kpi_Before" secondary={Kpi_Before} />
</List>
<p> {this.state.selectedOptions.map(o => (
<p>{o.value}</p>
))}</p>
<br />
<Button
variant="contained"
color="primary"
style={styles.button}
onClick={this.continue}
>
Confirm & Continue
</Button>
<Button
variant="contained"
color="default"
style={styles.button}
onClick={this.back}
>
Back
</Button>
</React.Fragment>
</MuiThemeProvider>
);
}
}
const theme = createMuiTheme({
palette: {
primary: blue,
secondary: purple
},
status: {
danger: "orange"
}
});
const styles = {
button: {
margin: 15
}
};
export default Confirm;