{
return Array.isArray(value);
}
@@ -60,3 +76,16 @@ export function localStorageAvailable() {
return false;
}
}
+export function mapColDefTypeToInputType(type: string) {
+ switch (type) {
+ case 'string':
+ return 'text';
+ case 'number':
+ case 'date':
+ return type;
+ case 'dateTime':
+ return 'datetime-local';
+ default:
+ return 'text';
+ }
+}
diff --git a/packages/grid/x-grid-data-generator/src/renderer/renderCountry.tsx b/packages/grid/x-grid-data-generator/src/renderer/renderCountry.tsx
index e150584630de..efa2500c58bf 100644
--- a/packages/grid/x-grid-data-generator/src/renderer/renderCountry.tsx
+++ b/packages/grid/x-grid-data-generator/src/renderer/renderCountry.tsx
@@ -5,7 +5,7 @@ import { GridCellParams } from '@material-ui/x-grid';
// ISO 3166-1 alpha-2
// ⚠️ No support for IE 11
function countryToFlag(isoCode: string) {
- return typeof String.fromCodePoint !== 'undefined'
+ return typeof String.fromCodePoint !== 'undefined' && isoCode
? isoCode
.toUpperCase()
.replace(/./g, (char) => String.fromCodePoint(char.charCodeAt(0) + 127397))
@@ -42,7 +42,7 @@ const Country = React.memo(function Country(props: CountryProps) {
return (
- {countryToFlag(value.code)}
+ {value.code && countryToFlag(value.code)}
{value.label}
);
diff --git a/packages/grid/x-grid/src/tests/apiRef.XGrid.test.tsx b/packages/grid/x-grid/src/tests/apiRef.XGrid.test.tsx
index f84f5aa55852..dcad48a0e19b 100644
--- a/packages/grid/x-grid/src/tests/apiRef.XGrid.test.tsx
+++ b/packages/grid/x-grid/src/tests/apiRef.XGrid.test.tsx
@@ -1,8 +1,14 @@
-import { GridApiRef, GridRowData, useGridApiRef, XGrid } from '@material-ui/x-grid';
+import {
+ GridApiRef,
+ GridComponentProps,
+ GridRowData,
+ useGridApiRef,
+ XGrid,
+} from '@material-ui/x-grid';
import { expect } from 'chai';
import * as React from 'react';
import { useFakeTimers } from 'sinon';
-import { getColumnValues } from 'test/utils/helperFn';
+import { getCell, getColumnValues } from 'test/utils/helperFn';
import { createClientRenderStrictMode } from 'test/utils';
describe(' - apiRef', () => {
@@ -46,11 +52,16 @@ describe(' - apiRef', () => {
let apiRef: GridApiRef;
- const TestCase = () => {
+ const TestCase = (props: Partial) => {
apiRef = useGridApiRef();
return (
-
+
);
};
@@ -157,4 +168,34 @@ describe(' - apiRef', () => {
expect(apiRef.current.getDataAsCsv()).to.equal('Brand\r\nNike\r\nAdidas\r\nPuma');
});
+
+ it('should allow to switch between cell mode', () => {
+ baselineProps.columns = baselineProps.columns.map((col) => ({ ...col, editable: true }));
+
+ render();
+ apiRef!.current.setCellMode(1, 'brand', 'edit');
+ const cell = getCell(1, 0);
+
+ expect(cell.classList.contains('MuiDataGrid-cellEditable')).to.equal(true);
+ expect(cell.classList.contains('MuiDataGrid-cellEditing')).to.equal(true);
+ expect(cell.querySelector('input')!.value).to.equal('Adidas');
+
+ apiRef!.current.setCellMode(1, 'brand', 'view');
+ expect(cell.classList.contains('MuiDataGrid-cellEditable')).to.equal(true);
+ expect(cell.classList.contains('MuiDataGrid-cellEditing')).to.equal(false);
+ expect(cell.querySelector('input')).to.equal(null);
+ });
+
+ it('isCellEditable should add the class MuiDataGrid-cellEditable to editable cells but not prevent a cell from switching mode', () => {
+ baselineProps.columns = baselineProps.columns.map((col) => ({ ...col, editable: true }));
+
+ render( params.value === 'Adidas'} />);
+ const cellNike = getCell(0, 0);
+ expect(cellNike!.classList.contains('MuiDataGrid-cellEditable')).to.equal(false);
+ const cellAdidas = getCell(1, 0);
+ expect(cellAdidas!.classList.contains('MuiDataGrid-cellEditable')).to.equal(true);
+
+ apiRef!.current.setCellMode(0, 'brand', 'edit');
+ expect(cellNike.classList.contains('MuiDataGrid-cellEditing')).to.equal(true);
+ });
});
diff --git a/packages/grid/x-grid/src/tests/rows.XGrid.test.tsx b/packages/grid/x-grid/src/tests/rows.XGrid.test.tsx
index 042200a72780..803d6f36ffd3 100644
--- a/packages/grid/x-grid/src/tests/rows.XGrid.test.tsx
+++ b/packages/grid/x-grid/src/tests/rows.XGrid.test.tsx
@@ -2,8 +2,8 @@ import * as React from 'react';
import { createClientRenderStrictMode } from 'test/utils';
import { useFakeTimers } from 'sinon';
import { expect } from 'chai';
-import { getColumnValues } from 'test/utils/helperFn';
-import { GridApiRef, useGridApiRef, XGrid } from '@material-ui/x-grid';
+import { getCell, getColumnValues } from 'test/utils/helperFn';
+import { GridApiRef, GridColDef, GridRowData, useGridApiRef, XGrid } from '@material-ui/x-grid';
describe(' - Rows ', () => {
let clock;
@@ -18,7 +18,7 @@ describe(' - Rows ', () => {
// TODO v5: replace with createClientRender
const render = createClientRenderStrictMode();
- const baselineProps = {
+ const baselineProps: { columns: GridColDef[]; rows: GridRowData[] } = {
rows: [
{
clientId: 'c1',
@@ -70,5 +70,33 @@ describe(' - Rows ', () => {
expect(getColumnValues(2)).to.deep.equal(['11', '30', '31']);
});
});
+
+ it('should allow to switch between cell mode', () => {
+ let apiRef: GridApiRef;
+ const editableProps = { ...baselineProps };
+ editableProps.columns = editableProps.columns.map((col) => ({ ...col, editable: true }));
+ const getRowId = (row) => `${row.clientId}`;
+
+ const Test = () => {
+ apiRef = useGridApiRef();
+ return (
+
+
+
+ );
+ };
+ render();
+ apiRef!.current.setCellMode('c2', 'first', 'edit');
+ const cell = getCell(1, 1);
+
+ expect(cell).to.have.class('MuiDataGrid-cellEditable');
+ expect(cell).to.have.class('MuiDataGrid-cellEditing');
+ expect(cell.querySelector('input')!.value).to.equal('Jack');
+ apiRef!.current.setCellMode('c2', 'first', 'view');
+
+ expect(cell).to.have.class('MuiDataGrid-cellEditable');
+ expect(cell).not.to.have.class('MuiDataGrid-cellEditing');
+ expect(cell.querySelector('input')).to.equal(null);
+ });
});
});
diff --git a/packages/storybook/.storybook/preview.tsx b/packages/storybook/.storybook/preview.tsx
index 0fa4e545bfbc..b9f540b2b564 100644
--- a/packages/storybook/.storybook/preview.tsx
+++ b/packages/storybook/.storybook/preview.tsx
@@ -9,7 +9,7 @@ LicenseInfo.setLicenseKey(
);
configureActions({
- depth: 3,
+ depth: 6,
limit: 10,
});
diff --git a/packages/storybook/package.json b/packages/storybook/package.json
index 041a3ef42c48..46eea8009ffc 100644
--- a/packages/storybook/package.json
+++ b/packages/storybook/package.json
@@ -41,5 +41,11 @@
"source-map-loader": "^1.0.2",
"string-replace-loader": "^3.0.1",
"ts-loader": "^8.0.2"
- }
+ },
+ "browserslist": [
+ ">0.3%",
+ "not ie 11",
+ "not dead",
+ "not op_mini all"
+ ]
}
diff --git a/packages/storybook/src/stories/grid-rows.stories.tsx b/packages/storybook/src/stories/grid-rows.stories.tsx
index bdd78709ecfa..9058a2855e6d 100644
--- a/packages/storybook/src/stories/grid-rows.stories.tsx
+++ b/packages/storybook/src/stories/grid-rows.stories.tsx
@@ -5,14 +5,19 @@ import Popper from '@material-ui/core/Popper';
import Paper from '@material-ui/core/Paper';
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles';
import {
+ GridCellValue,
GridCellParams,
+ GridEditRowsModel,
+ GridLoadIcon,
GridColDef,
isOverflown,
GridRowData,
useGridApiRef,
XGrid,
+ GridEditCellParams,
} from '@material-ui/x-grid';
import { useDemoData } from '@material-ui/x-grid-data-generator';
+import { action } from '@storybook/addon-actions';
import { randomInt } from '../data/random-generator';
export default {
@@ -347,3 +352,280 @@ export function ExpendRowCell() {
);
}
+
+// Requirements
+// TODO demo with Cell edit with value getter
+// Todo demo with cell not editable according to value
+// demo with cell edit validation, email, username(serverside)
+
+const baselineEditProps = {
+ rows: [
+ {
+ id: 0,
+ firstname: 'Damien',
+ lastname: 'Tassone',
+ email: 'damien@material-ui.com',
+ username: 'Damo',
+ lastLogin: new Date(),
+ age: 25,
+ DOB: new Date(1996, 10, 2),
+ meetup: new Date(2020, 2, 25, 10, 50, 0),
+ },
+ {
+ id: 1,
+ firstname: 'Jon',
+ lastname: 'Wood',
+ email: 'jon@material-ui.com',
+ username: 'jon',
+ lastLogin: new Date(),
+ age: 25,
+ DOB: new Date(1992, 1, 20),
+ meetup: new Date(2020, 4, 15, 10, 50, 0),
+ },
+ {
+ id: 2,
+ firstname: 'James',
+ lastname: 'Smith',
+ email: 'james@material-ui.com',
+ username: 'smithhhh',
+ lastLogin: new Date(),
+ age: 25,
+ DOB: new Date(1986, 0, 12),
+ meetup: new Date(2020, 3, 5, 10, 50, 0),
+ },
+ ],
+ columns: [
+ { field: 'firstname', editable: true },
+ { field: 'lastname', editable: true },
+ {
+ field: 'fullname',
+ editable: true,
+ valueGetter: ({ row }) => `${row.firstname} ${row.lastname}`,
+ },
+ { field: 'username', editable: true },
+ { field: 'email', editable: true, width: 150 },
+ { field: 'age', width: 50, type: 'number', editable: true },
+ { field: 'DOB', width: 120, type: 'date', editable: true },
+ { field: 'meetup', width: 180, type: 'dateTime', editable: true },
+ { field: 'lastLogin', width: 180, type: 'dateTime', editable: false },
+ ],
+};
+function validateEmail(email) {
+ const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
+ return re.test(String(email).toLowerCase());
+}
+
+const useEditCellStyles = makeStyles({
+ root: {
+ '& .MuiDataGrid-cellEditable': {
+ backgroundColor: 'rgba(184,250,158,0.19)',
+ color: '#1a3e72',
+ },
+ '& .MuiDataGrid-cellEditing': {
+ backgroundColor: 'rgb(255,215,115, 0.19)',
+ color: '#1a3e72',
+ },
+ '& .Mui-error': {
+ backgroundColor: 'rgb(126,10,15, 0.1)',
+ color: '#750f0f',
+ },
+ },
+});
+
+export function EditRowsControl() {
+ const apiRef = useGridApiRef();
+ const classes = useEditCellStyles();
+
+ const [selectedCell, setSelectedCell] = React.useState<[string, string, GridCellValue] | null>(
+ null,
+ );
+ const [isEditable, setIsEditable] = React.useState(false);
+ const [editRowsModel, setEditRowsModel] = React.useState({});
+
+ const editRow = React.useCallback(() => {
+ if (!selectedCell) {
+ return;
+ }
+ const [id, field, value] = selectedCell;
+
+ setEditRowsModel((state) => {
+ const editRowState: GridEditRowsModel = { ...state };
+ editRowState[id] = editRowState[id] ? { ...editRowState[id] } : {};
+ editRowState[id][field] = { value };
+
+ return { ...state, ...editRowState };
+ });
+ }, [selectedCell]);
+
+ const onCellClick = React.useCallback((params: GridCellParams) => {
+ setSelectedCell([params.row.id!.toString(), params.field, params.value]);
+ setIsEditable(!!params.isEditable);
+ }, []);
+
+ const onCellDoubleClick = React.useCallback(
+ (params: GridCellParams) => {
+ if (params.isEditable) {
+ apiRef.current.setCellMode(params.row.id!.toString(), params.field, 'edit');
+ }
+ },
+ [apiRef],
+ );
+
+ const isCellEditable = React.useCallback((params: GridCellParams) => params.row.id !== 0, []);
+
+ const onEditCellChange = React.useCallback(
+ ({ id, update }: GridEditCellParams) => {
+ if (update.email) {
+ const isValid = validateEmail(update.email.value);
+ const newState = {};
+ newState[id] = {
+ ...editRowsModel[id],
+ email: { ...update.email, error: !isValid },
+ };
+ newState[id].email.value += 'EXTERRRR';
+ setEditRowsModel((state) => ({ ...state, ...newState }));
+ return;
+ }
+ const newState = {};
+ newState[id] = {
+ ...editRowsModel[id],
+ ...update,
+ };
+ setEditRowsModel((state) => ({ ...state, ...newState }));
+ },
+ [editRowsModel],
+ );
+
+ const onEditCellChangeCommitted = React.useCallback(
+ ({ id, update }: GridEditCellParams) => {
+ const field = Object.keys(update)[0]!;
+ const rowUpdate = { id };
+ rowUpdate[field] = update[field].value;
+
+ if (update.email) {
+ const newState = {};
+ const componentProps = {
+ endAdornment: ,
+ };
+ newState[id] = {};
+ newState[id][field] = { ...update.email, ...componentProps };
+ setEditRowsModel((state) => ({ ...state, ...newState }));
+ setTimeout(() => {
+ apiRef.current.updateRows([rowUpdate]);
+ apiRef.current.setCellMode(id, field, 'view');
+ }, 2000);
+ } else if (update.fullname && update.fullname.value) {
+ const [firstname, lastname] = update.fullname.value.toString().split(' ');
+ apiRef.current.updateRows([{ id, firstname, lastname }]);
+ apiRef.current.setCellMode(id, field, 'view');
+ } else {
+ apiRef.current.updateRows([rowUpdate]);
+ apiRef.current.setCellMode(id, field, 'view');
+ }
+ },
+ [apiRef],
+ );
+
+ return (
+
+ Green cells are editable! Click + EDIT or Double click
+
+
+
+
+
+
+
+ );
+}
+export function EditRowsBasic() {
+ const apiRef = useGridApiRef();
+
+ const onCellDoubleClick = React.useCallback(
+ (params: GridCellParams) => {
+ if (params.isEditable) {
+ apiRef.current.setCellMode(params.row.id!.toString(), params.field, 'edit');
+ }
+ },
+ [apiRef],
+ );
+
+ return (
+
+ Double click to edit.
+
+
+
+
+ );
+}
+const singleData = { rows: [...baselineEditProps.rows], columns: [...baselineEditProps.columns] };
+singleData.rows.length = 1;
+singleData.columns.length = 1;
+singleData.columns[0].width = 200;
+
+export function SingleCellBasic() {
+ const apiRef = useGridApiRef();
+ const onCellDoubleClick = React.useCallback(
+ (params: GridCellParams) => {
+ if (params.isEditable) {
+ apiRef.current.setCellMode(params.row.id!.toString(), params.field, 'edit');
+ }
+ },
+ [apiRef],
+ );
+
+ return (
+
+ Double click to edit.
+
+
+
+
+ );
+}
+export function CommodityEdit() {
+ const apiRef = useGridApiRef();
+ const onCellDoubleClick = React.useCallback(
+ (params: GridCellParams) => {
+ apiRef.current.setCellMode(params.row.id!.toString(), params.field, 'edit');
+ },
+ [apiRef],
+ );
+
+ const { data } = useDemoData({
+ dataSet: 'Commodity',
+ rowLength: 100000,
+ });
+
+ return (
+
+
+
+
+
+ );
+}