Doing validation on certain columns in React-Data-Grid - reactjs

I want to implement validation on react-data-grid table, in which only cells which i defined in the column itself will be validated, and validation only occurs if the state is set to true.
So i have the following codes here
const [ validateMode, setValidateMode ] = useState(false);
const errorBackground = { backgroundColor: 'some error color in here' }
const DescriptionFormatter = props => {
const { value, row } = props;
let template;
if (!validateMode){
template = value;
}
if (validateMode){
let validationStyling = Boolean(value.length) ? {} : errorBackground;
template = <div style={{ ...validationStyling }}>value</div>
}
return template;
}
const tableCols = [
{
key: 'id',
name: 'No'
},
{
key: 'name',
name: 'Name',
editable: !disabled,
editor: <AutoComplete options={nameList} />
},
{
key: 'description',
name: 'Description',
editable: !disabled,
formatter: DescriptionFormatter
}
];
When button is pressed, it will trigger the validateMode state to true, and when that happen, i want the description column to validate the cell and return error background cell if value is empty, otherwise default background if there's value in it (simple checking if value is empty or not).
I can't seem to get the validateMode console log working when i press the button. Its only showing on page init, or when the cell changes (like when inserting value to it).I cannot get the validation working in here, unless if i do it wrongly.
Please help me on this.

Can't believe im answering my own question lol
Anyway i found the answer already. Validation cannot be done at the columns definition. It has to be done on the cell level of the datagrid. You can pass states to the cell and do your validation there, and change the CSS of the cell by using conditional className to show if validation is true or false.
Snippets of the codes to make it work
import ReactDataGrid, { Row, Cell } from "react-data-grid";
/*
Validator is a state passed from the parent page that indicates whether page is on validation or not, in case
if dev want the table to validate when a trigger hits the page (like submit button?)
*/
const { validator } = props;
const CustomCell = props => {
let valueValidation = false;
if (validator){
/* Insert custom validation in here */
return ( <Cell {...props} className={ validator && valueValidation ? '' : classes.error } /> );
}
/* If not in validation mode, display normal rows */
if (!validator){
return ( <Cell {...props} /> );
}
}
const CustomRow = props => {
return ( <Row {...props} cellRenderer={cellProps => <CustomCell {...cellProps} />} /> );
}
return (
<ReactDataGrid
rowRenderer={CustomRow}
/>
);

Related

MUI DataGrid onCellEditStop changes previous value

MUI DataGrid
onCellEditStop changes value of previously edited cell when changing other cell.
I saw a post that said that using onCellEditCommit is a solution, but it's deprecated so.. I need another way to fix it
const onCellEditStopHandler = (params: GridCellParams) => {
const { id, field, value } = params;
const faction = staffFactions.find((faction) => faction.id === id);
console.log('triggered');
if (!faction) return;
const factionWithoutActive = staffFactions.filter(
(faction) => faction.id !== id
);
if (field === 'maxVehicles') {
faction.maxVehicles = value;
} else if (field === 'maxMembers') {
faction.maxMembers = value;
}
setStaffFactions([...factionWithoutActive, faction]);
};
<ReactDataGrid
experimentalFeatures={{ newEditingApi: true }}
rows={rows || []}
columns={columns}
onCellEditStop={onCellEditStopHandler}
/>
In the docs, there are many ways that you could handle an editable component.
https://mui.com/x/react-data-grid/editing
For your code, maybe you could check out this section below and try using the processRowUpdate prop. It gets called once the editing stops.
https://mui.com/x/react-data-grid/editing/#persistence

MUI Datagrid disable user from typing further if validation does not pass

Is there any way to prevent a user from typing into a cell during edit mode if a validation fails? MUI documentation only shows validation in form of a snack bar but still allows users to continue to type.
In the below example, the field "data" should only allow the user to type up to 10 characters in length. The expected behavior would be to not record any new characters after 10.
DataGrid Component So Far
import React from "react";
import { DataGrid } from "#mui/x-data-grid";
export default function FairValuePricingLatest({ rows, handleCellEdit }) {
const columns = [
{
field: "id",
headerName: "Id"
},
{
field: "data",
headerName: "Data",
editable: true,
preProcessEditCellProps(params) {
const invalid = params.props.value.length > 10;
if (invalid) {
return { ...params.props, error: invalid }
}
}
];
return (
<DataGrid
editMode="cell"
experimentalFeatures={{ newEditingApi: true }}
processRowUpdate={handleCellEdit}
rows={rows}
columns={columns}
/>
)
}

Mui DataGrid > renderCell > useValue from renderCell

I have a field in the Datagrid with renderCell. The Value i have to display will be fetched inside the AlertIssues-Component as the original Data just provides the uuid to fetch the corresponding data (issue amount). So props.row._id is use in AlertIssues with a hook to retrieve the issues of the Alert in this row.
{
field: "issues",
type: "number",
headerClassName: "iconHeader",
// valueFormatter: params => {
// console.log("value formater",params )
// useGetIssueValue cannot be used here: (hook rules)
// let theValue = useGetIssueValue(params.id)
// return theValue
// },
// useGetIssueValue cannot be used here: (hook rules)
// valueGetter: params => useGetIssueValue(params.id)
renderCell: (props: GridRenderCellParams<Number>) => {
// useGetIssueValue is used inside AlertIssues
// and works to display the right amount
return(
<AlertIssues {...props} />
)},
},
export const AlertIssues = (props: GridRenderCellParams<Number>) => {
const { row } = props;
// i am getting my amount here without trouble.
// but it is just the displayed value ...
const alertIssueAmount = useGetIssueValue(row.id);
...
return <>alertIssueAmount</>
i tried to use "valueGetter" or "valueFormatter" to get the amount i need. but inside these functions i am not allowed to call my useData-hook, as they are functions and not React-Components or Hooks.
the row itself does not have the value i got inside of AlertIssues...
i am very lost here, how can i retrieve my issueAmount-value from the hook and use it in Datagrid? (e.g. for filter and sort)

How to make MUI's Autocomplete display the label from the matching value in props.options?

I have a multilingual website where there are certain Autocompletes whose options array need to have its items labels translated, which is done very easily.
However, it would be harder to update the current chosen value, stored elsewhere. Since Autocomplete uses the label from the value prop instead of using the item of same ID within options, it ends up like this:
const vehicles = [
{ id: 1, label: "Car" },
{ id: 2, label: "Bus" }
];
const currentVehicleValue = {
id: 2,
label: "Ônibus"
};
export default function ComboBox() {
return (
<Autocomplete
disablePortal
id="combo-box-demo"
options={vehicles}
value={currentVehicleValue}
renderInput={(params) => <TextField {...params} label="Vehicle" />}
/>
);
}
Is there a way to just tell Autocomplete to use the label inside the options prop instead of the one within the value
demo
EDIT: I mean to have the label within options being shown while I don't type anything. As in, the language changed, the options were translated, but the currentValue was not, so it would be nice to have the Autocomplete use the label from the matching item within options as long as I don't type anything.
Edit after clarification
You can change how the <TextField/> is rendered by tweaking the props it receives.
In the example below, I find the currentVehicle by its id and change the inputProps.value to be the vehicle label.
Also, to ensure MUI finds the currentValue correctly within the options, you will need to use the isOptionEqualToValue to compare the options ids instead of strict equality
const vehicles = [
{ id: 1, label: "Car" },
{ id: 2, label: "Bus" }
];
const currentVehicleValue = {
id: 2,
label: "Ônibus"
};
function compareValueToOption(option, value) {
return option.id === value.id;
}
function ComboBox() {
return (
<Autocomplete
disablePortal
id="combo-box-demo"
options={vehicles}
value={currentVehicleValue}
renderInput={ComboBoxInput}
isOptionEqualToValue={compareValueToOption}
/>
);
}
function ComboBoxInput(props) {
const currentVehicle = vehicles.find(
(vehicle) => vehicle.id === currentVehicleValue.id
);
props.inputProps.value = currentVehicle.label;
return <TextField {...props} label="Vehicle" />;
}
Here's a working demo

React Redux - select all checkbox

I have been searching on Google all day to try and find a way to solve my issue.
I've created a "product selection page" and I'm trying to add a "select all" checkbox that will select any number of products that are displayed (this will vary depending on the customer).
It's coming along and I've got all the checkboxes working but I can't get "select all" to work. Admittedly I'm using some in-house libraries and I think that's what's giving me trouble as I'm unable to find examples that look like what I've done so far.
OK, so the code to create my checkboxGroup is here:
let productSelectionList = (
<FormGroup className="productInfo">
<Field
component={CheckboxGroup}
name="checkboxField"
vertical={true}
choices={this.createProductList()}
onChange={this.handleCheckboxClick}
helpText="Select all that apply."
label="Which accounts should use this new mailing address?"
/>
</FormGroup>
);
As you can see, my choices will be created in the createProductList method. That looks like this:
createProductList() {
const { products } = this.props;
const selectAllCheckbox = <b>Select All Accounts</b>;
let productList = [];
productList.push({ label: selectAllCheckbox, value: "selectAll" });
if (products && products.length > 0) {
products.forEach((product, idx) => {
productList.push({
label: product.productDescription,
value: product.displayArrangementId
});
});
}
return productList;
}
Also note that here I've also created the "Select All Accounts" entry and then pushed it onto the list with a value of "selectAll". The actual products are then pushed on, each having a label and a value (although only the label is displayed. The end result looks like this:
Select Products checkboxes
I've managed to isolate the "select all" checkbox with this function:
handleCheckboxClick(event) {
// var items = this.state.items.slice();
if (event.selectAll) {
this.setState({
'isSelectAllClicked': true
});
} else {
this.setState({
'isSelectAllClicked': false
});
}
}
I also created this componentDidUpdate function:
componentDidUpdate(prevProps, prevState) {
if (this.state.isSelectAllClicked !== prevState.isSelectAllClicked && this.state.isSelectAllClicked){
console.log("if this ", this.state.isSelectAllClicked);
console.log("if this ", this.props);
} else if (this.state.isSelectAllClicked !== prevState.isSelectAllClicked && !this.state.isSelectAllClicked){
console.log("else this ", this.state.isSelectAllClicked);
console.log("else this ", this.props);
}
}
So in the console, I'm able to see that when the "select all" checkbox is clicked, I do get a "True" flag, and unclicking it I get a "False". But now how can I select the remaining boxes (I will admit that I am EXTREMELY new to React/Redux and that I don't have any previous checkboxes experience).
In Chrome, I'm able to see my this.props as shown here..
this.props
You can see that this.props.productList.values.checkboxField shows the values of true for the "select all" checkbox as well as for four of the products. But that's because I manually checked off those four products for this test member that has 14 products. How can I get "check all" to select all 14 products?
Did I go about this the wrong way? (please tell me that this is still doable) :(
My guess is your single product checkboxes are bound to some data you have in state, whether local or redux. The checkbox input type has a checked prop which accepts a boolean value which will determine if the checkbox is checked or not.
The idea would be to set all items checked prop (whatever you are actually using for that value) to true upon clicking the select all checkbox. Here is example code you can try and run.
import React, { Component } from 'react';
import './App.css';
class App extends Component {
state = {
items: [
{
label: "first",
checked: false,
},
{
label: "last",
checked: false,
}
],
selectAll: false,
}
renderCheckbooks = (item) => {
return (
<div key={item.label}>
<span>{item.label}</span>
<input type="checkbox" checked={item.checked} />
</div>
);
}
selectAll = (e) => {
if (this.state.selectAll) {
this.setState({ selectAll: false }, () => {
let items = [...this.state.items];
items = items.map(item => {
return {
...item,
checked: false,
}
})
this.setState({ items })
});
} else {
this.setState({ selectAll: true }, () => {
let items = [...this.state.items];
items = items.map(item => {
return {
...item,
checked: true,
}
})
this.setState({ items })
});
}
}
render() {
return (
<div className="App">
{this.state.items.map(this.renderCheckbooks)}
<span>Select all</span>
<input onClick={this.selectAll} type="checkbox" checked={this.state.selectAll} />
</div>
);
}
}
export default App;
I have items in state. Each item has a checked prop which I pass to the checkbox getting rendered for that item. If the prop is true, the checkbox will be checked otherwise it wont be. When I click on select all, I map thru my items to make each one checked so that the checkbox gets checked.
Here is a link to a codesandbox where you can see this in action and mess with the code.
There is a package grouped-checkboxes which can solve this problem.
In your case you could map over your products like this:
import React from 'react';
import {
CheckboxGroup,
AllCheckerCheckbox,
Checkbox
} from "#createnl/grouped-checkboxes";
const App = (props) => {
const { products } = props
return (
<CheckboxGroup onChange={console.log}>
<label>
<AllCheckerCheckbox />
Select all accounts
</label>
{products.map(product => (
<label>
<Checkbox id={product.value} />
{product.label}
</label>
))}
</CheckboxGroup>
)
}
More examples see https://codesandbox.io/s/grouped-checkboxes-v5sww

Resources