Skip to content
This repository was archived by the owner on Jun 4, 2024. It is now read-only.

Commit 8825b2c

Browse files
authored
Merge pull request #462 from plotly/columnEditable
Column editable props
2 parents aa17452 + 9de451a commit 8825b2c

File tree

13 files changed

+258
-40
lines changed

13 files changed

+258
-40
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
7878
Renamed `filter` to `filter_query`.
7979

8080
### Added
81+
[#320](https://github.com/plotly/dash-table/issues/320)
82+
- Ability to conditionally format columns if editing is disabled.
83+
8184
[#456](https://github.com/plotly/dash-table/issues/456)
8285
- Support for dash-table is now available for R users of Dash.
8386

src/dash-table/components/CellFactory.tsx

-2
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ export default class CellFactory {
3838
dropdown,
3939
data,
4040
dropdown_data,
41-
editable,
4241
is_focused,
4342
row_deletable,
4443
row_selectable,
@@ -106,7 +105,6 @@ export default class CellFactory {
106105
columns,
107106
virtualized.data,
108107
virtualized.offset,
109-
editable,
110108
!!is_focused,
111109
dropdowns
112110
);

src/dash-table/components/ControlledTable/index.tsx

+1-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import derivedTable from 'dash-table/derived/table';
2525
import derivedTableFragments from 'dash-table/derived/table/fragments';
2626
import derivedTableFragmentStyles from 'dash-table/derived/table/fragmentStyles';
2727
import derivedTooltips from 'dash-table/derived/table/tooltip';
28-
import isEditable from 'dash-table/derived/cell/isEditable';
2928
import { derivedTableStyle } from 'dash-table/derived/style';
3029
import { IStyle } from 'dash-table/derived/style/props';
3130
import TableTooltip from './fragments/TableTooltip';
@@ -460,7 +459,6 @@ export default class ControlledTable extends PureComponent<ControlledTableProps>
460459
const {
461460
columns,
462461
data,
463-
editable,
464462
selected_cells,
465463
setProps,
466464
viewport
@@ -476,7 +474,7 @@ export default class ControlledTable extends PureComponent<ControlledTableProps>
476474
);
477475

478476
realCells.forEach(cell => {
479-
if (isEditable(editable, columns[cell[1]].editable)) {
477+
if (columns[cell[1]].editable) {
480478
newData = R.set(
481479
R.lensPath([cell[0], columns[cell[1]].id]),
482480
'',

src/dash-table/components/Table/props.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ export interface IDatetimeColumn extends ITypeColumn {
155155

156156
export interface IBaseVisibleColumn {
157157
deletable?: boolean | boolean[];
158-
editable?: boolean;
158+
editable: boolean;
159159
renamable?: boolean | boolean[];
160160
sort_as_null: SortAsNull;
161161
id: ColumnId;

src/dash-table/conditional/index.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@ export interface ITypedElement {
2121
column_type?: ColumnType;
2222
}
2323

24+
export interface IEditableElement {
25+
column_editable?: boolean;
26+
}
27+
2428
export type ConditionalBasicFilter = INamedElement & ITypedElement;
25-
export type ConditionalDataCell = IConditionalElement & IIndexedRowElement & INamedElement & ITypedElement;
29+
export type ConditionalDataCell = IConditionalElement & IIndexedRowElement & INamedElement & ITypedElement & IEditableElement;
2630
export type ConditionalCell = INamedElement & ITypedElement;
2731
export type ConditionalHeader = IIndexedHeaderElement & INamedElement & ITypedElement;
2832

@@ -70,4 +74,12 @@ export function ifFilter(condition: IConditionalElement | undefined, datum: Datu
7074
return !condition ||
7175
condition.filter_query === undefined ||
7276
ifAstFilter(new QuerySyntaxTree(condition.filter_query), datum);
77+
}
78+
79+
export function ifEditable(condition: IEditableElement | undefined, isEditable: boolean) {
80+
if (!condition ||
81+
condition.column_editable === undefined) {
82+
return true;
83+
}
84+
return isEditable === condition.column_editable;
7385
}

src/dash-table/dash/DataTable.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import sanitizeProps from './sanitize';
1313
export default class DataTable extends Component {
1414
constructor(props) {
1515
super(props);
16-
1716
let id;
1817
this.getId = () => (id = id || genRandomId('table-'));
1918
}
@@ -959,7 +958,8 @@ export const propTypes = {
959958
row_index: PropTypes.oneOfType([
960959
PropTypes.number,
961960
PropTypes.oneOf(['odd', 'even'])
962-
])
961+
]),
962+
column_editable: PropTypes.bool
963963
})
964964
})),
965965

@@ -971,7 +971,8 @@ export const propTypes = {
971971
style_filter_conditional: PropTypes.arrayOf(PropTypes.shape({
972972
if: PropTypes.exact({
973973
column_id: PropTypes.string,
974-
column_type: PropTypes.oneOf(['any', 'numeric', 'text', 'datetime'])
974+
column_type: PropTypes.oneOf(['any', 'numeric', 'text', 'datetime']),
975+
column_editable: PropTypes.bool
975976
})
976977
})),
977978

@@ -987,7 +988,8 @@ export const propTypes = {
987988
header_index: PropTypes.oneOfType([
988989
PropTypes.number,
989990
PropTypes.oneOf(['odd', 'even'])
990-
])
991+
]),
992+
column_editable: PropTypes.bool
991993
})
992994
})),
993995

src/dash-table/dash/sanitize.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
TableAction
1616
} from 'dash-table/components/Table/props';
1717
import headerRows from 'dash-table/derived/header/headerRows';
18+
import isEditable from 'dash-table/derived/cell/isEditable';
1819

1920
const D3_DEFAULT_LOCALE: INumberLocale = {
2021
symbol: ['$', ''],
@@ -31,9 +32,9 @@ const DEFAULT_SPECIFIER = '';
3132
const applyDefaultToLocale = memoizeOne((locale: INumberLocale) => getLocale(locale));
3233

3334
const applyDefaultsToColumns = memoizeOne(
34-
(defaultLocale: INumberLocale, defaultSort: SortAsNull, columns: Columns) => R.map(column => {
35+
(defaultLocale: INumberLocale, defaultSort: SortAsNull, columns: Columns, editable: boolean ) => R.map(column => {
3536
const c = R.clone(column);
36-
37+
c.editable = isEditable(editable, column.editable);
3738
c.sort_as_null = c.sort_as_null || defaultSort;
3839

3940
if (c.type === ColumnType.Numeric && c.format) {
@@ -67,7 +68,7 @@ export default (props: PropsWithDefaults): SanitizedProps => {
6768
const locale_format = applyDefaultToLocale(props.locale_format);
6869

6970
return R.merge(props, {
70-
columns: applyDefaultsToColumns(locale_format, props.sort_as_null, props.columns),
71+
columns: applyDefaultsToColumns(locale_format, props.sort_as_null, props.columns, props.editable),
7172
fixed_columns: getFixedColumns(props.fixed_columns, props.row_deletable, props.row_selectable),
7273
fixed_rows: getFixedRows(props.fixed_rows, props.columns, props.filter_action),
7374
locale_format

src/dash-table/derived/cell/contents.tsx

+1-5
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import {
1616
import CellInput from 'dash-table/components/CellInput';
1717
import derivedCellEventHandlerProps, { Handler } from 'dash-table/derived/cell/eventHandlerProps';
1818
import isActiveCell from 'dash-table/derived/cell/isActive';
19-
import isCellEditable from './isEditable';
2019
import CellLabel from 'dash-table/components/CellLabel';
2120
import CellDropdown from 'dash-table/components/CellDropdown';
2221
import { memoizeOne } from 'core/memoizer';
@@ -62,7 +61,6 @@ class Contents {
6261
columns: VisibleColumns,
6362
data: Data,
6463
offset: IViewportOffset,
65-
editable: boolean,
6664
isFocused: boolean,
6765
dropdowns: (IDropdown | undefined)[][]
6866
): JSX.Element[][] => {
@@ -75,15 +73,13 @@ class Contents {
7573

7674
const dropdown = dropdowns[rowIndex][columnIndex];
7775

78-
const isEditable = isCellEditable(editable, column.editable);
79-
8076
const className = [
8177
...(active ? ['input-active'] : []),
8278
isFocused ? 'focused' : 'unfocused',
8379
'dash-cell-value'
8480
].join(' ');
8581

86-
switch (getCellType(active, isEditable, dropdown && dropdown.options, column.presentation)) {
82+
switch (getCellType(active, column.editable, dropdown && dropdown.options, column.presentation)) {
8783
case CellType.Dropdown:
8884
return (<CellDropdown
8985
key={`column-${columnIndex}`}

src/dash-table/derived/style/index.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ import {
2121
IIndexedRowElement,
2222
INamedElement,
2323
ITypedElement,
24+
IEditableElement,
2425
ifColumnId,
25-
ifColumnType
26+
ifColumnType,
27+
ifEditable
2628
} from 'dash-table/conditional';
2729
import { QuerySyntaxTree } from 'dash-table/syntax-tree';
2830

@@ -36,7 +38,7 @@ export interface IConvertedStyle {
3638
matchesFilter: (datum: Datum) => boolean;
3739
}
3840

39-
type GenericIf = Partial<IConditionalElement & IIndexedHeaderElement & IIndexedRowElement & INamedElement & ITypedElement>;
41+
type GenericIf = Partial<IConditionalElement & IIndexedHeaderElement & IIndexedRowElement & INamedElement & ITypedElement & IEditableElement>;
4042
type GenericStyle = Style & Partial<{ if: GenericIf }>;
4143

4244
function convertElement(style: GenericStyle): IConvertedStyle {
@@ -46,7 +48,8 @@ function convertElement(style: GenericStyle): IConvertedStyle {
4648
return {
4749
checksColumn: () => !R.isNil(style.if) && (
4850
!R.isNil(style.if.column_id) ||
49-
!R.isNil(style.if.column_type)
51+
!R.isNil(style.if.column_type) ||
52+
!R.isNil(style.if.column_editable)
5053
),
5154
checksRow: () => !R.isNil(indexFilter),
5255
checksFilter: () => !R.isNil(style.if) && !R.isNil(style.if.filter_query),
@@ -55,7 +58,8 @@ function convertElement(style: GenericStyle): IConvertedStyle {
5558
!style.if || (
5659
!R.isNil(column) &&
5760
ifColumnId(style.if, column && column.id) &&
58-
ifColumnType(style.if, column && column.type)
61+
ifColumnType(style.if, column && column.type) &&
62+
ifEditable (style.if, column && column.editable)
5963
),
6064
matchesRow: (index: number | undefined) =>
6165
indexFilter === undefined ?

src/dash-table/handlers/cellEvents.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,16 @@ export const handleClick = (propsFn: () => ICellFactoryProps, idx: number, i: nu
8080

8181
export const handleDoubleClick = (propsFn: () => ICellFactoryProps, idx: number, i: number, e: any) => {
8282
const {
83-
editable,
8483
is_focused,
8584
setProps,
8685
virtualized,
8786
columns,
8887
viewport
8988
} = propsFn();
9089

91-
if (!editable) {
90+
const c = columns[i];
91+
92+
if (!c.editable) {
9293
return;
9394
}
9495

@@ -115,15 +116,14 @@ export const handleChange = (propsFn: () => ICellFactoryProps, idx: number, i: n
115116
const {
116117
columns,
117118
data,
118-
editable,
119119
setProps,
120120
virtualized
121121
} = propsFn();
122122

123123
const c = columns[i];
124124
const realIdx = virtualized.indices[idx];
125125

126-
if (!editable) {
126+
if (!c.editable) {
127127
return;
128128
}
129129

src/dash-table/utils/applyClipboardToData.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import Logger from 'core/Logger';
44

55
import { ICellCoordinates, Columns, Data, ColumnType } from 'dash-table/components/Table/props';
66
import reconcile from 'dash-table/type/reconcile';
7-
import isEditable from 'dash-table/derived/cell/isEditable';
87

98
export default (
109
values: any[][],
@@ -38,7 +37,7 @@ export default (
3837
name: `Column ${i + 1}`,
3938
type: ColumnType.Any,
4039
sort_as_null: []
41-
});
40+
} as any);
4241
newData.forEach(row => (row[`Column ${i}`] = ''));
4342
}
4443
}
@@ -75,7 +74,7 @@ export default (
7574

7675
const jOffset = (activeCell as any).column + j;
7776
const col = newColumns[jOffset];
78-
if (!col || !isEditable(true, col.editable)) {
77+
if (!col || !col.editable) {
7978
continue;
8079
}
8180

tests/cypress/tests/unit/clipboard_test.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ describe('clipboard', () => {
99
R.range(0, 1).map(value => [`${value}`]),
1010
{row: 0, column: 0, column_id: ''},
1111
R.range(0, 1),
12-
['c1'].map(id => ({ id: id, name: id })),
12+
['c1'].map(id => ({ id: id, name: id, editable: true, sort_as_null: [] })),
1313
R.range(0, 1).map(() => ({ c1: 'c1' })),
1414
true,
1515
true
@@ -28,7 +28,7 @@ describe('clipboard', () => {
2828
R.range(0, 2).map(value => [`${value}`]),
2929
{row: 0, column: 0, column_id: ''},
3030
R.range(0, 1),
31-
['c1'].map(id => ({ id: id, name: id })),
31+
['c1'].map(id => ({ id: id, name: id, editable: true, sort_as_null: [] })),
3232
R.range(0, 1).map(() => ({ c1: 'c1' })),
3333
true,
3434
true
@@ -48,7 +48,7 @@ describe('clipboard', () => {
4848
R.range(0, 10).map(value => [`${value}`]),
4949
{row: 0, column: 0, column_id: ''},
5050
R.range(0, 3),
51-
['c1'].map(id => ({ id: id, name: id })),
51+
['c1'].map(id => ({ id: id, name: id, editable: true, sort_as_null: [] })),
5252
R.range(0, 3).map(() => ({ c1: 'c1' })),
5353
true,
5454
true
@@ -69,7 +69,7 @@ describe('clipboard', () => {
6969
R.range(0, 10).map(value => [`${value}`]),
7070
{row: 1, column: 0, column_id: ''},
7171
R.range(0, 3),
72-
['c1'].map(id => ({ id: id, name: id })),
72+
['c1'].map(id => ({ id: id, name: id, editable: true, sort_as_null: [] })),
7373
R.range(0, 3).map(() => ({ c1: 'c1' })),
7474
true,
7575
true
@@ -93,7 +93,7 @@ describe('clipboard', () => {
9393
R.range(0, 1).map(value => [`${value}`]),
9494
{row: 0, column: 0, column_id: ''},
9595
R.range(0, 1),
96-
['c1'].map(id => ({ id: id, name: id })),
96+
['c1'].map(id => ({ id: id, name: id, editable: true, sort_as_null: [] })),
9797
R.range(0, 1).map(() => ({ c1: 'c1' })),
9898
true,
9999
false
@@ -112,7 +112,7 @@ describe('clipboard', () => {
112112
R.range(0, 2).map(value => [`${value}`]),
113113
{row: 0, column: 0, column_id: ''},
114114
R.range(0, 1),
115-
['c1'].map(id => ({ id: id, name: id })),
115+
['c1'].map(id => ({ id: id, name: id, editable: true, sort_as_null: [] })),
116116
R.range(0, 1).map(() => ({ c1: 'c1' })),
117117
true,
118118
false
@@ -131,7 +131,7 @@ describe('clipboard', () => {
131131
R.range(0, 10).map(value => [`${value}`]),
132132
{row: 0, column: 0, column_id: ''},
133133
R.range(0, 3),
134-
['c1'].map(id => ({ id: id, name: id })),
134+
['c1'].map(id => ({ id: id, name: id, editable: true, sort_as_null: [] })),
135135
R.range(0, 3).map(() => ({ c1: 'c1' })),
136136
true,
137137
false
@@ -152,7 +152,7 @@ describe('clipboard', () => {
152152
R.range(0, 10).map(value => [`${value}`]),
153153
{row: 1, column: 0, column_id: ''},
154154
R.range(0, 3),
155-
['c1'].map(id => ({ id: id, name: id })),
155+
['c1'].map(id => ({ id: id, name: id, editable: true, sort_as_null: [] })),
156156
R.range(0, 3).map(() => ({ c1: 'c1' })),
157157
true,
158158
false

0 commit comments

Comments
 (0)