Skip to content

Commit

Permalink
feat(react-grid): add the ability to prevent editing by a column (#729)
Browse files Browse the repository at this point in the history
  • Loading branch information
SergeyAlexeev authored Feb 15, 2018
1 parent 5b8de2d commit 3c88c4c
Show file tree
Hide file tree
Showing 12 changed files with 199 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React from 'react';<%&additionalImports%>
import {
EditingState,
} from '@devexpress/dx-react-grid';
import {
Grid,
Table,
TableHeaderRow,
TableEditRow,
TableEditColumn,
} from '@devexpress/dx-react-grid-<%&themeName%>';

import {
generateRows,
defaultColumnValues,
} from '../../../demo-data/generator';

const getRowId = row => row.id;

export default class Demo extends React.PureComponent {
constructor(props) {
super(props);

this.state = {
columns: [
{ name: 'name', title: 'Name' },
{ name: 'sex', title: 'Sex' },
{ name: 'city', title: 'City' },
{ name: 'car', title: 'Car' },
],
rows: generateRows({
columnValues: { id: ({ index }) => index, ...defaultColumnValues },
length: 8,
}),
editingStateColumnExtensions: [
{ columnName: 'name', editingEnabled: false },
],
};

this.commitChanges = this.commitChanges.bind(this);
}
commitChanges({ added, changed, deleted }) {
let { rows } = this.state;
if (added) {
const startingAddedId = (rows.length - 1) > 0 ? rows[rows.length - 1].id + 1 : 0;
rows = [
...rows,
...added.map((row, index) => ({
id: startingAddedId + index,
...row,
})),
];
}
if (changed) {
rows = rows.map(row => (changed[row.id] ? { ...row, ...changed[row.id] } : row));
}
if (deleted) {
const deletedSet = new Set(deleted);
rows = rows.filter(row => !deletedSet.has(row.id));
}
this.setState({ rows });
}
render() {
const { rows, columns, editingStateColumnExtensions } = this.state;

return (
<<%&wrapperTag%>>
<Grid
rows={rows}
columns={columns}
getRowId={getRowId}
>
<EditingState
onCommitChanges={this.commitChanges}
defaultEditingRowIds={[0]}
columnExtensions={editingStateColumnExtensions}
/>
<Table />
<TableHeaderRow />
<TableEditRow />
<TableEditColumn
showAddCommand
showEditCommand
showDeleteCommand
/>
</Grid>
</<%&wrapperTag%>>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as PropTypes from 'prop-types';

export const EditCell = ({
column, value, onValueChange, style, children,
row, tableRow, tableColumn, ...restProps
row, tableRow, tableColumn, editingEnabled, ...restProps
}) => (
<td
style={{
Expand All @@ -19,6 +19,7 @@ export const EditCell = ({
className="form-control"
value={value}
onChange={e => onValueChange(e.target.value)}
readOnly={!editingEnabled}
style={{
width: '100%',
textAlign: tableColumn && tableColumn.align,
Expand All @@ -36,6 +37,7 @@ EditCell.propTypes = {
value: PropTypes.any,
onValueChange: PropTypes.func.isRequired,
style: PropTypes.object,
editingEnabled: PropTypes.bool,
children: PropTypes.node,
};

Expand All @@ -47,4 +49,5 @@ EditCell.defaultProps = {
value: '',
style: null,
children: undefined,
editingEnabled: true,
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ import { shallow } from 'enzyme';
import { EditCell } from './table-edit-cell';

describe('EditCell', () => {
const defaultProps = {
onValueChange: () => {},
};
it('should pass style to the root element', () => {
const tree = shallow((
<EditCell
{...defaultProps}
value="a"
onValueChange={() => {}}
style={{
width: '40px',
height: '10px',
Expand All @@ -24,7 +27,7 @@ describe('EditCell', () => {
it('should render children if passed', () => {
const tree = shallow((
<EditCell
onValueChange={() => {}}
{...defaultProps}
>
<span className="test" />
</EditCell>
Expand All @@ -37,12 +40,24 @@ describe('EditCell', () => {
it('should pass rest props to the root element', () => {
const tree = shallow((
<EditCell
onValueChange={() => {}}
{...defaultProps}
className="custom-class"
/>
));

expect(tree.is('.custom-class'))
.toBeTruthy();
});

it('should render readonly editor if editing is not allowed', () => {
const tree = shallow((
<EditCell
{...defaultProps}
editingEnabled={false}
/>
));

expect(tree.find('input').prop('readOnly'))
.toBeTruthy();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const styles = theme => ({

const EditCellBase = ({
column, value, onValueChange, style, classes, children,
row, tableRow, tableColumn, className, ...restProps
row, tableRow, tableColumn, editingEnabled, className, ...restProps
}) => {
const inputClasses = classNames({
[classes.inputRight]: tableColumn && tableColumn.align === 'right',
Expand All @@ -37,6 +37,7 @@ const EditCellBase = ({
className={classes.inputRoot}
classes={{ input: inputClasses }}
value={value || ''}
disabled={!editingEnabled}
onChange={e => onValueChange(e.target.value)}
/>
)}
Expand All @@ -53,6 +54,7 @@ EditCellBase.propTypes = {
onValueChange: PropTypes.func.isRequired,
style: PropTypes.object,
classes: PropTypes.object.isRequired,
editingEnabled: PropTypes.bool,
children: PropTypes.node,
className: PropTypes.string,
};
Expand All @@ -66,6 +68,7 @@ EditCellBase.defaultProps = {
style: null,
children: undefined,
className: undefined,
editingEnabled: true,
};

export const EditCell = withStyles(styles, { name: 'EditCell' })(EditCellBase);
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import { createShallow, getClasses } from 'material-ui/test-utils';
import { EditCell } from './table-edit-cell';

describe('EditCell', () => {
const defaultProps = {
onValueChange: () => {},
};

let shallow;
let classes;

Expand All @@ -16,8 +20,8 @@ describe('EditCell', () => {
it('should render without exceptions', () => {
const tree = shallow((
<EditCell
{...defaultProps}
value=""
onValueChange={() => {}}
/>
));
expect(tree.find(`.${classes.cell}`).exists()).toBeTruthy();
Expand Down Expand Up @@ -46,8 +50,8 @@ describe('EditCell', () => {
it('should take column align into account', () => {
const tree = shallow((
<EditCell
{...defaultProps}
value=""
onValueChange={() => {}}
/>
));

Expand All @@ -61,8 +65,8 @@ describe('EditCell', () => {
it('should take column align into account if align is "right"', () => {
const tree = shallow((
<EditCell
{...defaultProps}
value=""
onValueChange={() => {}}
tableColumn={{ align: 'right' }}
/>
));
Expand All @@ -75,8 +79,8 @@ describe('EditCell', () => {
it('should pass style to the root element', () => {
const tree = shallow((
<EditCell
{...defaultProps}
value="a"
onValueChange={() => {}}
style={{
width: '40px',
height: '10px',
Expand All @@ -93,7 +97,7 @@ describe('EditCell', () => {
it('should render children if passed', () => {
const tree = shallow((
<EditCell
onValueChange={() => {}}
{...defaultProps}
>
<span className="test" />
</EditCell>
Expand All @@ -106,7 +110,7 @@ describe('EditCell', () => {
it('should pass the className prop to the root element', () => {
const tree = shallow((
<EditCell
onValueChange={() => {}}
{...defaultProps}
className="custom-class"
/>
));
Expand All @@ -120,12 +124,24 @@ describe('EditCell', () => {
it('should pass rest props to the root element', () => {
const tree = shallow((
<EditCell
onValueChange={() => {}}
{...defaultProps}
data={{ a: 1 }}
/>
));

expect(tree.props().data)
.toMatchObject({ a: 1 });
});

it('should render disabled editor if editing is not allowed', () => {
const tree = shallow((
<EditCell
{...defaultProps}
editingEnabled={false}
/>
));

expect(tree.find(Input).prop('disabled'))
.toBeTruthy();
});
});
6 changes: 6 additions & 0 deletions packages/dx-react-grid/docs/guides/editing.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,9 @@ In the [controlled mode](controlled-and-uncontrolled-modes.md), specify the foll
Note, you can also use the `onAddedRowsChange` event to initialize a created row with default property values.

.embedded-demo(grid-editing/edit-row-controlled)

### Disable Editing by a Column

You can prevent editing of a specific column using the [EditingState](../reference/editing-state.md) plugin's `columnExtensions` property.

.embedded-demo(grid-editing/disable-column-editing)
3 changes: 3 additions & 0 deletions packages/dx-react-grid/docs/reference/editing-state.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ none
Name | Type | Default | Description
-----|------|---------|------------
createRowChange? | (row: any, columnName: string, value: string &#124; number) => any | | A function that returns a row change object depending on row editor values. This function is called each time the row editor's value changes.
columnEditingEnabled? | boolean | true | Specifies whether editing is enabled for all columns.
columnExtensions? | Array&lt;[EditingState.ColumnExtension](#editingstatecolumnextension)&gt; | | Additional column properties that the plugin can handle.
editingRowIds? | Array&lt;number &#124; string&gt; | | IDs of the rows being edited.
defaultEditingRowIds? | Array&lt;number &#124; string&gt; | [] | IDs of the rows initially added to the `editingRowIds` array in uncontrolled mode.
Expand All @@ -37,6 +38,7 @@ Describes additional column properties that the plugin can handle.
Field | Type | Description
------|------|------------
columnName | string | The name of a column to extend.
editingEnabled? | boolean | Specifies whether editing is enabled for a column.
createRowChange? | (row: any, value: any, columnName: string) => any | A function that returns a value specifying row changes depending on the columns' editor values for the current row. This function is called each time the editor's value changes.

### ChangeSet
Expand Down Expand Up @@ -76,3 +78,4 @@ deleteRows | Action | ({ rowIds: Array&lt;number &#124; string&gt; }) => void |
cancelDeletedRows | Action | ({ rowIds: Array&lt;number &#124; string&gt; }) => void | Removes the specified rows from the `deletedRowIds` array.
commitDeletedRows | Action | ({ rowIds: Array&lt;number &#124; string&gt; }) => void | Fires the `onCommitChanges` event with the corresponding [ChangeSet](#changeset) and removes specified rows from the `deletedRowIds` array.
createRowChange | Getter | (row: any, value: any, columnName: string) => any | A function that returns a value that specifies row changes depending on the column's editor values for the current row. This function is called each time the editor's value changes.
isColumnEditingEnabled | Getter | (columnName: string) => boolean | A function that returns a value that specifies if editing by a column is enabled.
2 changes: 2 additions & 0 deletions packages/dx-react-grid/docs/reference/table-edit-row.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Field | Type | Description
row | any | A row to be edited.
column | [Column](grid.md#column) | A column.
value | any | A value to be edited.
editingEnabled | boolean | Specifies whether editing by a column is enabled.
onValueChange | (newValue: any) => void | Handles value changes.

### TableEditRow.RowProps
Expand Down Expand Up @@ -61,6 +62,7 @@ Name | Plugin | Type | Description
tableBodyRows | Getter | Array&lt;[TableRow](table.md#tablerow)&gt; | Table body rows.
editingRowIds | Getter | Array&lt;number &#124; string&gt; | IDs of the rows that are being edited.
addedRows | Getter | Array&lt;any&gt; | Created but not committed rows.
isColumnEditingEnabled | Getter | (columnName: string) => boolean | A function that returns a value that specifies if editing by a column is enabled.
changeAddedRow | Action | ({ rowId: number, change: any }) => void | Applies a change to a created but uncommitted row. Note: `rowId` is a row index within the `addedRows` array.
rowChanges | Getter | { [key: string]: any } | An associative array that stores changes made to existing rows. Each array item specifies changes made to a row. The item's key specifies the associated row's ID.
changeRow | Action | ({ rowId: number &#124; string, change: Object }) => void | Applies a change to an existing row.
Expand Down
14 changes: 13 additions & 1 deletion packages/dx-react-grid/src/plugins/editing-state.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,14 @@ import {

deleteRows,
cancelDeletedRows,

getColumnExtensionValueGetter,
} from '@devexpress/dx-grid-core';
import { createStateHelper } from '../utils/state-helper';

const columnExtensionValueGetter = (columnExtensions, defaultValue) =>
getColumnExtensionValueGetter(columnExtensions, 'editingEnabled', defaultValue);

export class EditingState extends React.PureComponent {
constructor(props) {
super(props);
Expand Down Expand Up @@ -113,7 +118,7 @@ export class EditingState extends React.PureComponent {
}
}
render() {
const { createRowChange, columnExtensions } = this.props;
const { createRowChange, columnExtensions, columnEditingEnabled } = this.props;
const {
editingRowIds, rowChanges, addedRows, deletedRowIds,
} = this.getState();
Expand Down Expand Up @@ -146,13 +151,19 @@ export class EditingState extends React.PureComponent {
<Action name="deleteRows" action={this.deleteRows} />
<Action name="cancelDeletedRows" action={this.cancelDeletedRows} />
<Action name="commitDeletedRows" action={this.commitDeletedRows} />

<Getter
name="isColumnEditingEnabled"
value={columnExtensionValueGetter(columnExtensions, columnEditingEnabled)}
/>
</Plugin>
);
}
}

EditingState.propTypes = {
createRowChange: PropTypes.func,
columnEditingEnabled: PropTypes.bool,
columnExtensions: PropTypes.array,

editingRowIds: PropTypes.array,
Expand All @@ -176,6 +187,7 @@ EditingState.propTypes = {

EditingState.defaultProps = {
createRowChange: undefined,
columnEditingEnabled: true,
columnExtensions: undefined,

editingRowIds: undefined,
Expand Down
Loading

0 comments on commit 3c88c4c

Please sign in to comment.