From 2314ad36ebc7615e127cdcfccf3b9d74764ba7c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Tue, 28 May 2019 16:32:56 -0400 Subject: [PATCH 01/46] - column id: remove number supprt - editable_name -> renamable - deletable & renamable: bool|number -> bool|bool[] - remove options --- src/dash-table/components/Table/props.ts | 9 ++-- src/dash-table/dash/DataTable.js | 53 ++++++----------------- src/dash-table/derived/cell/dropdowns.ts | 19 ++++---- src/dash-table/derived/header/content.tsx | 17 ++++---- 4 files changed, 35 insertions(+), 63 deletions(-) diff --git a/src/dash-table/components/Table/props.ts b/src/dash-table/components/Table/props.ts index 3b3fc3e15..d679e3792 100644 --- a/src/dash-table/components/Table/props.ts +++ b/src/dash-table/components/Table/props.ts @@ -60,7 +60,7 @@ export interface ICellCoordinates { column_id: ColumnId; } -export type ColumnId = string | number; +export type ColumnId = string; export type Columns = IColumn[]; export type Data = Datum[]; export type Datum = IDatumObject | any; @@ -156,12 +156,11 @@ export interface IDatetimeColumn extends ITypeColumn { export interface IBaseVisibleColumn { clearable?: boolean; - deletable?: boolean | number; + deletable?: boolean | boolean[]; editable?: boolean; - editable_name?: boolean | number; + renamable?: boolean | boolean[]; id: ColumnId; name: string | string[]; - options?: IDropdownValue[]; // legacy } export type IColumnType = INumberColumn | ITextColumn | IDatetimeColumn | IAnyColumn; @@ -187,7 +186,7 @@ interface IConditionalDropdown { dropdown: IDropdownValue[]; } -export interface IColumnDropdown { +export interface IColumnDropdown { id: string; dropdown: IDropdownValue[]; } diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index ac731aef3..c1ab836c7 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -92,7 +92,7 @@ export const propTypes = { row: PropTypes.number, column: PropTypes.number, row_id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - column_id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) + column_id: PropTypes.string }), /** @@ -125,7 +125,7 @@ export const propTypes = { */ deletable: PropTypes.oneOfType([ PropTypes.bool, - PropTypes.number + PropTypes.arrayOf(PropTypes.bool) ]), /** @@ -144,14 +144,14 @@ export const propTypes = { /** * If True, then the name of this column is editable. * If there are multiple column headers (if `name` is a list of strings), - * then `editable_name` can refer to _which_ column header should be + * then `renamable` can refer to _which_ column header should be * editable by setting it to the column header index. * Also, updating the name in a merged column header cell will * update the name of each column. */ - editable_name: PropTypes.oneOfType([ + renamable: PropTypes.oneOfType([ PropTypes.bool, - PropTypes.number + PropTypes.arrayOf(PropTypes.bool) ]), /** @@ -271,24 +271,6 @@ export const propTypes = { allow_YY: PropTypes.bool }), - /** - * DEPRECATED - * Please use `column_static_dropdown` instead. - * NOTE - Dropdown behavior will likely change in the future, - * subscribe to [https://github.com/plotly/dash-table/issues/168](https://github.com/plotly/dash-table/issues/168) - * for more information. - */ - options: PropTypes.arrayOf(PropTypes.shape({ - label: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string - ]).isRequired, - value: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string - ]).isRequired - })), - /** * The data-type of the column's data. * 'numeric': represents both floats and ints @@ -432,7 +414,7 @@ export const propTypes = { row: PropTypes.number, column: PropTypes.number, row_id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - column_id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) + column_id: PropTypes.string }), /** @@ -513,7 +495,7 @@ export const propTypes = { row: PropTypes.number, column: PropTypes.number, row_id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - column_id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) + column_id: PropTypes.string })), /** @@ -545,7 +527,7 @@ export const propTypes = { row: PropTypes.number, column: PropTypes.number, row_id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - column_id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) + column_id: PropTypes.string }), /** @@ -894,7 +876,7 @@ export const propTypes = { sort_by: PropTypes.arrayOf( // .exact PropTypes.shape({ - column_id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, + column_id: PropTypes.string.isRequired, direction: PropTypes.oneOf(['asc', 'desc']).isRequired })), @@ -952,7 +934,7 @@ export const propTypes = { style_cell_conditional: PropTypes.arrayOf(PropTypes.shape({ // .exact if: PropTypes.shape({ - column_id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + column_id: PropTypes.string, column_type: PropTypes.oneOf(['any', 'numeric', 'text', 'datetime']) }) })), @@ -965,7 +947,7 @@ export const propTypes = { style_data_conditional: PropTypes.arrayOf(PropTypes.shape({ // .exact if: PropTypes.shape({ - column_id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + column_id: PropTypes.string, column_type: PropTypes.oneOf(['any', 'numeric', 'text', 'datetime']), filter: PropTypes.string, row_index: PropTypes.oneOfType([ @@ -983,7 +965,7 @@ export const propTypes = { style_filter_conditional: PropTypes.arrayOf(PropTypes.shape({ // .exact if: PropTypes.shape({ - column_id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + column_id: PropTypes.string, column_type: PropTypes.oneOf(['any', 'numeric', 'text', 'datetime']) }) })), @@ -996,7 +978,7 @@ export const propTypes = { style_header_conditional: PropTypes.arrayOf(PropTypes.shape({ // .exact if: PropTypes.shape({ - column_id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + column_id: PropTypes.string, column_type: PropTypes.oneOf(['any', 'numeric', 'text', 'datetime']), header_index: PropTypes.oneOfType([ PropTypes.number, @@ -1121,14 +1103,7 @@ export const propTypes = { */ derived_virtual_selected_row_ids: PropTypes.arrayOf( PropTypes.oneOfType([PropTypes.string, PropTypes.number]) - ), - - /** - * DEPRECATED - * Subscribe to [https://github.com/plotly/dash-table/issues/168](https://github.com/plotly/dash-table/issues/168) - * for updates on the dropdown API. - */ - dropdown_properties: PropTypes.any + ) }; DataTable.defaultProps = defaultProps; diff --git a/src/dash-table/derived/cell/dropdowns.ts b/src/dash-table/derived/cell/dropdowns.ts index 64961c99f..870299e84 100644 --- a/src/dash-table/derived/cell/dropdowns.ts +++ b/src/dash-table/derived/cell/dropdowns.ts @@ -60,23 +60,20 @@ class Dropdowns { columnStaticDropdown: any, dropdown_properties: any ): [any, any] => { - let legacyDropdown = ( + const legacyDropdown = + dropdown_properties && + dropdown_properties[column.id] && ( - dropdown_properties && - dropdown_properties[column.id] && - ( - dropdown_properties[column.id].length > realIndex ? - dropdown_properties[column.id][realIndex] : - null - ) - ) || column - ).options; + dropdown_properties[column.id].length > realIndex ? + dropdown_properties[column.id][realIndex] : + null + ); const conditional = columnConditionalDropdown.find((cs: any) => cs.id === column.id); const base = columnStaticDropdown.find((ss: any) => ss.id === column.id); return [ - legacyDropdown || (base && base.dropdown), + (legacyDropdown && legacyDropdown.options) || (base && base.dropdown), (conditional && conditional.dropdowns) || [] ]; }); diff --git a/src/dash-table/derived/header/content.tsx b/src/dash-table/derived/header/content.tsx index fef77afed..d2ae2b61b 100644 --- a/src/dash-table/derived/header/content.tsx +++ b/src/dash-table/derived/header/content.tsx @@ -98,14 +98,15 @@ function getter( columnIndex => { const column = columns[columnIndex]; - const editable = (column.editable_name && R.type(column.editable_name) === 'Boolean') || - (R.type(column.editable_name) === 'Number' && column.editable_name === headerRowIndex); + const renamable: boolean = typeof column.renamable === 'boolean' ? + column.renamable : + !!column.renamable && column.renamable[headerRowIndex]; - const deletable = paginationMode !== 'be' && - ( - (column.deletable && R.type(column.deletable) === 'Boolean') || - (R.type(column.deletable) === 'Number' && column.deletable === headerRowIndex) - ); + const deletable = paginationMode !== 'be' && ( + typeof column.deletable === 'boolean' ? + column.deletable : + !!column.deletable && column.deletable[headerRowIndex] + ); return (
{sorting && isLastRow ? @@ -118,7 +119,7 @@ function getter( '' } - {editable ? + {renamable ? ( Date: Tue, 28 May 2019 20:55:34 -0400 Subject: [PATCH 02/46] refactor options --- tests/visual/percy-storybook/fixtures.ts | 148 +++++++++++++---------- 1 file changed, 82 insertions(+), 66 deletions(-) diff --git a/tests/visual/percy-storybook/fixtures.ts b/tests/visual/percy-storybook/fixtures.ts index 19dc6eac5..f1e317b43 100644 --- a/tests/visual/percy-storybook/fixtures.ts +++ b/tests/visual/percy-storybook/fixtures.ts @@ -10,19 +10,22 @@ export default [ name: 'Column 1', id: 'column-1', type: ColumnType.Text, - presentation: Presentation.Dropdown, - options: [ - { - label: 'Montréal', - value: 'mtl' - }, - { - label: 'San Francisco', - value: 'sf' - } - ] + presentation: Presentation.Dropdown } ], + column_static_dropdown: [{ + id: 'column-1', + dropdown: [ + { + label: 'Montréal', + value: 'mtl' + }, + { + label: 'San Francisco', + value: 'sf' + } + ] + }], data: [ {'column-1': 'mtl'}, {'column-1': 'sf'}, @@ -136,19 +139,22 @@ export default [ name: 'Column 1', id: 'column-1', type: ColumnType.Text, - presentation: Presentation.Dropdown, - options: [ - { - label: 'Montréal', - value: 'mtl' - }, - { - label: 'San Francisco', - value: 'sf' - } - ] + presentation: Presentation.Dropdown } ], + column_static_dropdown: [{ + id: 'column-1', + dropdown: [ + { + label: 'Montréal', + value: 'mtl' + }, + { + label: 'San Francisco', + value: 'sf' + } + ] + }], data: [ {'column-1': 'mtl'}, {'column-1': 'sf'}, @@ -173,51 +179,58 @@ export default [ name: 'Column 1', id: 'column-1', type: ColumnType.Text, - presentation: Presentation.Dropdown, - options: [ - { - label: 'Montréal', - value: 'mtl' - }, - { - label: 'San Francisco', - value: 'sf' - } - ] + presentation: Presentation.Dropdown }, { name: 'Column 2', id: 'column-2', type: ColumnType.Text, - presentation: Presentation.Dropdown, - options: [ - { - label: 'Montréal', - value: 'mtl' - }, - { - label: 'San Francisco', - value: 'sf' - } - ] + presentation: Presentation.Dropdown }, { name: 'Column 3', id: 'column-3', type: ColumnType.Text, - presentation: Presentation.Dropdown, - options: [ - { - label: 'Montréal', - value: 'mtl' - }, - { - label: 'San Francisco', - value: 'sf' - } - ] + presentation: Presentation.Dropdown } ], + column_static_dropdown: [{ + id: 'column-1', + dropdown: [ + { + label: 'Montréal', + value: 'mtl' + }, + { + label: 'San Francisco', + value: 'sf' + } + ] + }, { + id: 'column-2', + dropdown: [ + { + label: 'Montréal', + value: 'mtl' + }, + { + label: 'San Francisco', + value: 'sf' + } + ] + }, { + id: 'column-3', + dropdown: [ + { + label: 'Montréal', + value: 'mtl' + }, + { + label: 'San Francisco', + value: 'sf' + } + ] + }], data: [ {'column-1': 'mtl', 'column-2': 'mtl', 'column-3': 'mtl'}, {'column-1': 'mtl', 'column-2': 'mtl', 'column-3': 'mtl'}, @@ -256,19 +269,22 @@ export default [ name: ['Region', ''], id: 'region', type: ColumnType.Text, - presentation: Presentation.Dropdown, - options: [ - { - label: 'Hawaii', - value: 'hawaii' - }, - { - label: 'Costa Rica', - value: 'costa-rica' - } - ] + presentation: Presentation.Dropdown } ], + column_static_dropdown: [{ + id: 'region', + dropdown: [ + { + label: 'Hawaii', + value: 'hawaii' + }, + { + label: 'Costa Rica', + value: 'costa-rica' + } + ] + }], merge_duplicate_headers: true, data: [ { From 54be1603e5a22c23cee22e9e0c07272bd588f1e1 Mon Sep 17 00:00:00 2001 From: Marc-Andre-Rivet Date: Tue, 28 May 2019 21:16:55 -0400 Subject: [PATCH 03/46] update tests --- demo/AppMode.ts | 2 +- tests/visual/percy-storybook/fixtures.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/demo/AppMode.ts b/demo/AppMode.ts index 277419d9a..118d8a78a 100644 --- a/demo/AppMode.ts +++ b/demo/AppMode.ts @@ -46,7 +46,7 @@ function getBaseTableProps(mock: IDataMock) { on_change: { action: ChangeAction.None }, - editable_name: true, + renamable: true, deletable: true })), column_static_dropdown: [ diff --git a/tests/visual/percy-storybook/fixtures.ts b/tests/visual/percy-storybook/fixtures.ts index f1e317b43..4e5b59ff5 100644 --- a/tests/visual/percy-storybook/fixtures.ts +++ b/tests/visual/percy-storybook/fixtures.ts @@ -548,7 +548,7 @@ export default [ name: ['City', 'NYC'], id: 'city-nyc', deletable: true, - editable_name: true + renamable: true }, { name: ['City', 'SF'], @@ -558,7 +558,7 @@ export default [ { name: ['Weather', 'Rainy'], id: 'weather-rainy', - editable_name: true + renamable: true }, { name: ['Weather', 'Sunny'], @@ -568,7 +568,7 @@ export default [ name: ['Village', 'NYC'], id: 'village-nyc', deletable: true, - editable_name: 0 + renamable: [true, false] }, { name: ['Village', 'SF'], @@ -578,7 +578,7 @@ export default [ { name: ['Climate', 'Rainy'], id: 'climate-rainy', - editable_name: 1 + renamable: [false, true] }, { name: ['Climate', 'Sunny'], From 594748d05d72892ee0027372175558c34fcba496 Mon Sep 17 00:00:00 2001 From: Marc-Andre-Rivet Date: Tue, 28 May 2019 21:40:47 -0400 Subject: [PATCH 04/46] add back dropdown_properties --- src/dash-table/dash/DataTable.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index c1ab836c7..744ee4404 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -1103,7 +1103,14 @@ export const propTypes = { */ derived_virtual_selected_row_ids: PropTypes.arrayOf( PropTypes.oneOfType([PropTypes.string, PropTypes.number]) - ) + ), + + /** + * DEPRECATED + * Subscribe to [https://github.com/plotly/dash-table/issues/168](https://github.com/plotly/dash-table/issues/168) + * for updates on the dropdown API. + */ + dropdown_properties: PropTypes.any }; DataTable.defaultProps = defaultProps; From 2ea9806d6a9ad88114557d7757c27a18f796d091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Wed, 29 May 2019 10:38:21 -0400 Subject: [PATCH 05/46] move column.clerable into the dropdown props --- src/dash-table/components/CellFactory.tsx | 2 +- src/dash-table/components/Table/props.ts | 25 ++++++--- src/dash-table/dash/DataTable.js | 33 ++++++----- src/dash-table/derived/cell/contents.tsx | 11 ++-- src/dash-table/derived/cell/dropdowns.ts | 68 ++++++++++++----------- 5 files changed, 77 insertions(+), 62 deletions(-) diff --git a/src/dash-table/components/CellFactory.tsx b/src/dash-table/components/CellFactory.tsx index 6e3821e23..6e0b08938 100644 --- a/src/dash-table/components/CellFactory.tsx +++ b/src/dash-table/components/CellFactory.tsx @@ -37,7 +37,7 @@ export default class CellFactory { column_conditional_dropdowns, column_static_dropdown, data, - dropdown_properties, // legacy + dropdown_properties, editable, is_focused, row_deletable, diff --git a/src/dash-table/components/Table/props.ts b/src/dash-table/components/Table/props.ts index d679e3792..b117ffe3b 100644 --- a/src/dash-table/components/Table/props.ts +++ b/src/dash-table/components/Table/props.ts @@ -155,7 +155,6 @@ export interface IDatetimeColumn extends ITypeColumn { } export interface IBaseVisibleColumn { - clearable?: boolean; deletable?: boolean | boolean[]; editable?: boolean; renamable?: boolean | boolean[]; @@ -181,14 +180,22 @@ export interface IDropdownValue { export type DropdownValues = IDropdownValue[]; -interface IConditionalDropdown { +export interface IDropdown { + clearable?: boolean; + dropdown: DropdownValues; +} + +interface IConditionalDropdown extends IDropdown { condition: string; - dropdown: IDropdownValue[]; } -export interface IColumnDropdown { - id: string; - dropdown: IDropdownValue[]; +export interface IColumnDropdown extends IDropdown { + clearable?: boolean; + dropdown: DropdownValues; +} + +export interface IDataDropdowns { + [key: string]: (IDropdown | undefined)[]; } export interface IConditionalColumnDropdown { @@ -197,7 +204,7 @@ export interface IConditionalColumnDropdown { } export interface IDropdownProperties { - [key: string]: { options: IDropdownValue[] }[]; + [key: string]: { options: DropdownValues }[]; } export interface ITableTooltips { @@ -274,7 +281,7 @@ export interface IProps { content_style: ContentStyle; css?: IStylesheetRule[]; data?: Data; - dropdown_properties: any; // legacy + dropdown_properties: IDataDropdowns; editable?: boolean; filter?: string; filtering?: Filtering; @@ -397,7 +404,7 @@ export interface ICellFactoryProps { column_static_dropdown: IColumnDropdown[]; column_static_tooltip: ITableStaticTooltips; data: Data; - dropdown_properties: any; // legacy + dropdown_properties: IDataDropdowns; editable: boolean; id: string; is_focused?: boolean; diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index 744ee4404..77ee9188f 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -101,17 +101,6 @@ export const propTypes = { */ columns: PropTypes.arrayOf(PropTypes.shape({ - /** - * If the column is rendered as dropdowns, then the - * `clearable` property determines whether or not - * the dropdown value can be cleared or not. - * - * NOTE - The name of this property may change in the future, - * subscribe to [https://github.com/plotly/dash-table/issues/168](https://github.com/plotly/dash-table/issues/168) - * for more information. - */ - clearable: PropTypes.bool, - /** * If True, the user can delete the column by clicking on a little `x` * button on the column. @@ -597,6 +586,7 @@ export const propTypes = { id: PropTypes.string.isRequired, // .exact dropdowns: PropTypes.arrayOf(PropTypes.shape({ + clearable: PropTypes.bool, condition: PropTypes.string.isRequired, // .exact dropdown: PropTypes.arrayOf(PropTypes.shape({ @@ -620,6 +610,7 @@ export const propTypes = { * Tune in to [https://github.com/plotly/dash-table/issues/168](https://github.com/plotly/dash-table/issues/168) */ column_static_dropdown: PropTypes.arrayOf(PropTypes.shape({ + clearable: PropTypes.bool, id: PropTypes.string.isRequired, // .exact dropdown: PropTypes.arrayOf(PropTypes.shape({ @@ -1106,11 +1097,23 @@ export const propTypes = { ), /** - * DEPRECATED - * Subscribe to [https://github.com/plotly/dash-table/issues/168](https://github.com/plotly/dash-table/issues/168) - * for updates on the dropdown API. + * */ - dropdown_properties: PropTypes.any + dropdown_properties: PropTypes.objectOf( + PropTypes.arrayOf( + PropTypes.shape({ + clearable: PropTypes.bool, + // .exact + dropdown: PropTypes.arrayOf(PropTypes.shape({ + label: PropTypes.string.isRequired, + value: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired + })).isRequired + }) + ) + ) }; DataTable.defaultProps = defaultProps; diff --git a/src/dash-table/derived/cell/contents.tsx b/src/dash-table/derived/cell/contents.tsx index dfc1ebac5..1ca45f887 100644 --- a/src/dash-table/derived/cell/contents.tsx +++ b/src/dash-table/derived/cell/contents.tsx @@ -10,7 +10,8 @@ import { IViewportOffset, IVisibleColumn, Presentation, - VisibleColumns + VisibleColumns, + IDropdown } from 'dash-table/components/Table/props'; import CellInput from 'dash-table/components/CellInput'; import derivedCellEventHandlerProps, { Handler } from 'dash-table/derived/cell/eventHandlerProps'; @@ -63,7 +64,7 @@ class Contents { offset: IViewportOffset, editable: boolean, isFocused: boolean, - dropdowns: (DropdownValues | undefined)[][] + dropdowns: (IDropdown | undefined)[][] ): JSX.Element[][] => { const formatters = R.map(getFormatter, columns); @@ -82,13 +83,13 @@ class Contents { 'dash-cell-value' ].join(' '); - switch (getCellType(active, isEditable, dropdown, column.presentation)) { + switch (getCellType(active, isEditable, dropdown && dropdown.dropdown, column.presentation)) { case CellType.Dropdown: return (); diff --git a/src/dash-table/derived/cell/dropdowns.ts b/src/dash-table/derived/cell/dropdowns.ts index 870299e84..edaf6f097 100644 --- a/src/dash-table/derived/cell/dropdowns.ts +++ b/src/dash-table/derived/cell/dropdowns.ts @@ -13,13 +13,16 @@ import { VisibleColumns, ColumnId, Indices, - DropdownValues, IBaseVisibleColumn, - IVisibleColumn + IVisibleColumn, + IDataDropdowns, + IColumnDropdown, + IConditionalColumnDropdown, + IDropdown } from 'dash-table/components/Table/props'; import { QuerySyntaxTree } from 'dash-table/syntax-tree'; -const mapData = R.addIndex(R.map); +const mapData = R.addIndex(R.map); export default () => new Dropdowns().get; @@ -31,9 +34,9 @@ class Dropdowns { columns: VisibleColumns, data: Data, indices: Indices, - columnConditionalDropdown: any, - columnStaticDropdown: any, - dropdown_properties: any + columnConditionalDropdown: IConditionalColumnDropdown[], + columnStaticDropdown: IColumnDropdown[], + dropdown_properties: IDataDropdowns ) => mapData((datum, rowIndex) => R.map(column => { const applicable = this.applicable.get(column.id, rowIndex)( column, @@ -56,24 +59,22 @@ class Dropdowns { private readonly applicable = memoizerCache<[ColumnId, number]>()(( column: IBaseVisibleColumn, realIndex: number, - columnConditionalDropdown: any, - columnStaticDropdown: any, - dropdown_properties: any - ): [any, any] => { + columnConditionalDropdown: IConditionalColumnDropdown[], + columnStaticDropdown: IColumnDropdown[], + dropdown_properties: IDataDropdowns + ): [IDropdown | null, IConditionalDropdown[]] => { const legacyDropdown = dropdown_properties && dropdown_properties[column.id] && - ( - dropdown_properties[column.id].length > realIndex ? - dropdown_properties[column.id][realIndex] : - null - ); + dropdown_properties[column.id].length > realIndex && + dropdown_properties[column.id][realIndex] && + dropdown_properties[column.id][realIndex]; const conditional = columnConditionalDropdown.find((cs: any) => cs.id === column.id); const base = columnStaticDropdown.find((ss: any) => ss.id === column.id); return [ - (legacyDropdown && legacyDropdown.options) || (base && base.dropdown), + legacyDropdown || base || null, (conditional && conditional.dropdowns) || [] ]; }); @@ -83,27 +84,30 @@ class Dropdowns { * applicable dropdowns. */ private readonly dropdown = memoizerCache<[ColumnId, number]>()(( - applicableDropdowns: [any, any], + applicableDropdowns: [IDropdown | null, IConditionalDropdown[]], column: IVisibleColumn, datum: Datum ) => { const [staticDropdown, conditionalDropdowns] = applicableDropdowns; - const matches = [ - ...(staticDropdown ? [staticDropdown] : []), - ...R.map( - ([cd]) => cd.dropdown, - R.filter<[IConditionalDropdown, number]>( - ([cd, i]) => this.evaluation.get(column.id, i)( - this.ast.get(column.id, i)(cd.condition), - datum - ), - R.addIndex(R.map)( - (cd, i) => [cd, i], - conditionalDropdowns - )) - ) - ]; + const matches: IDropdown[] = []; + + if (staticDropdown) { + matches.push(staticDropdown); + } + + matches.push(...R.map( + ([cd]) => cd, + R.filter<[IConditionalDropdown, number]>( + ([cd, i]) => this.evaluation.get(column.id, i)( + this.ast.get(column.id, i)(cd.condition), + datum + ), + R.addIndex(R.map)( + (cd, i) => [cd, i], + conditionalDropdowns + )) + )); return matches.length ? matches.slice(-1)[0] : undefined; }); From bb1411fbef2ef69d50c012615d720d1f40f2b658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Wed, 29 May 2019 11:01:39 -0400 Subject: [PATCH 06/46] dropdown_properties usage: options -> dropdown --- tests/visual/percy-storybook/Dropdown.percy.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/visual/percy-storybook/Dropdown.percy.tsx b/tests/visual/percy-storybook/Dropdown.percy.tsx index 4770e5ada..40c423f4c 100644 --- a/tests/visual/percy-storybook/Dropdown.percy.tsx +++ b/tests/visual/percy-storybook/Dropdown.percy.tsx @@ -86,17 +86,17 @@ storiesOf('DashTable/Dropdown', module) editable={true} dropdown_properties={{ Neighborhood: [{ - options: R.map( + dropdown: R.map( i => ({ label: i, value: i }), ['Brooklyn', 'Queens', 'Staten Island'] ) }, { - options: R.map( + dropdown: R.map( i => ({ label: i, value: i }), ['Mile End', 'Plateau', 'Hochelaga'] ) }, { - options: R.map( + dropdown: R.map( i => ({ label: i, value: i }), ['Venice', 'Hollywood', 'Los Feliz'] ) From e854f89619d8bcf466367be0b4bd230bdf6f3023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Wed, 29 May 2019 12:02:34 -0400 Subject: [PATCH 07/46] remove `navigation` --- src/dash-table/components/ControlledTable/index.tsx | 10 ++++------ src/dash-table/components/Table/props.ts | 3 --- src/dash-table/dash/DataTable.js | 6 ------ 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/dash-table/components/ControlledTable/index.tsx b/src/dash-table/components/ControlledTable/index.tsx index 80d233dc7..f40fb6d4e 100644 --- a/src/dash-table/components/ControlledTable/index.tsx +++ b/src/dash-table/components/ControlledTable/index.tsx @@ -578,16 +578,14 @@ export default class ControlledTable extends PureComponent get displayPagination() { const { data, - navigation, pagination_mode, pagination_settings } = this.props; - return navigation === 'page' && - ( - (pagination_mode === 'fe' && pagination_settings.page_size < data.length) || - pagination_mode === 'be' - ); + return ( + pagination_mode === 'fe' && + pagination_settings.page_size < data.length + ) || pagination_mode === 'be'; } loadNext = () => { diff --git a/src/dash-table/components/Table/props.ts b/src/dash-table/components/Table/props.ts index b117ffe3b..2c4db2b1b 100644 --- a/src/dash-table/components/Table/props.ts +++ b/src/dash-table/components/Table/props.ts @@ -67,7 +67,6 @@ export type Datum = IDatumObject | any; export type Filtering = 'fe' | 'be' | boolean; export type Indices = number[]; export type RowId = string | number; -export type Navigation = 'page'; export type PaginationMode = 'fe' | 'be' | boolean; export type RowSelection = 'single' | 'multi' | false; export type SelectedCells = ICellCoordinates[]; @@ -289,7 +288,6 @@ export interface IProps { filtering_types?: FilteringType[]; locale_format: INumberLocale; merge_duplicate_headers?: boolean; - navigation?: Navigation; n_fixed_columns?: number; n_fixed_rows?: number; row_deletable?: boolean; @@ -332,7 +330,6 @@ interface IDefaultProps { filtering_type: FilteringType; filtering_types: FilteringType[]; merge_duplicate_headers: boolean; - navigation: Navigation; n_fixed_columns: number; n_fixed_rows: number; row_deletable: boolean; diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index 77ee9188f..f7266848b 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -36,7 +36,6 @@ export const defaultProps = { current_page: 0, page_size: 250 }, - navigation: 'page', content_style: 'grow', css: [], @@ -565,11 +564,6 @@ export const propTypes = { page_size: PropTypes.number.isRequired }), - /** - * DEPRECATED - */ - navigation: PropTypes.string, - /** * `column_conditional_dropdowns` specifies the available options * for dropdowns in various columns and cells. From 51cc1bc495bc3f59d559db96ec82dbf8a51683db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Wed, 29 May 2019 12:10:18 -0400 Subject: [PATCH 08/46] remove `navigation` from tests --- tests/cypress/dash/v_be_page.py | 1 - tests/cypress/dash/v_copy_paste.py | 1 - tests/cypress/dash/v_fe_page.py | 1 - 3 files changed, 3 deletions(-) diff --git a/tests/cypress/dash/v_be_page.py b/tests/cypress/dash/v_be_page.py index c5296d929..ffb1af28a 100644 --- a/tests/cypress/dash/v_be_page.py +++ b/tests/cypress/dash/v_be_page.py @@ -33,7 +33,6 @@ "current_page": 0, "page_size": 250, }, - navigation="page", columns=[ {"id": 0, "name": "Complaint ID"}, {"id": 1, "name": "Product"}, diff --git a/tests/cypress/dash/v_copy_paste.py b/tests/cypress/dash/v_copy_paste.py index 2330994cc..fb2c58c54 100644 --- a/tests/cypress/dash/v_copy_paste.py +++ b/tests/cypress/dash/v_copy_paste.py @@ -29,7 +29,6 @@ dash_table.DataTable( id="table", data=df[0:250], - navigation="page", columns=[ {"id": 0, "name": "Complaint ID"}, {"id": 1, "name": "Product"}, diff --git a/tests/cypress/dash/v_fe_page.py b/tests/cypress/dash/v_fe_page.py index 57fd34d8b..ce5fdd0c4 100644 --- a/tests/cypress/dash/v_fe_page.py +++ b/tests/cypress/dash/v_fe_page.py @@ -41,7 +41,6 @@ "current_page": 0, "page_size": 250, }, - navigation="page", columns=[ {"id": 0, "name": "Complaint ID"}, {"id": 1, "name": "Product"}, From ac44115f296044d61b7c03e5f229467f44b64f16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Wed, 29 May 2019 12:32:10 -0400 Subject: [PATCH 09/46] - rename dropdown props - column_dropdown_data `objectOf` - update usages and tests --- src/dash-table/components/CellFactory.tsx | 12 +-- src/dash-table/components/Table/props.ts | 27 +++--- src/dash-table/dash/DataTable.js | 87 ++++++++++--------- src/dash-table/derived/cell/dropdowns.ts | 24 ++--- .../visual/percy-storybook/Dropdown.percy.tsx | 31 +++---- tests/visual/percy-storybook/fixtures.ts | 8 +- 6 files changed, 95 insertions(+), 94 deletions(-) diff --git a/src/dash-table/components/CellFactory.tsx b/src/dash-table/components/CellFactory.tsx index 6e0b08938..46b36bbf6 100644 --- a/src/dash-table/components/CellFactory.tsx +++ b/src/dash-table/components/CellFactory.tsx @@ -34,10 +34,10 @@ export default class CellFactory { const { active_cell, columns, - column_conditional_dropdowns, - column_static_dropdown, + column_dropdown_conditional, + column_dropdown, data, - dropdown_properties, + column_dropdown_data, editable, is_focused, row_deletable, @@ -77,9 +77,9 @@ export default class CellFactory { columns, virtualized.data, virtualized.indices, - column_conditional_dropdowns, - column_static_dropdown, - dropdown_properties + column_dropdown_conditional, + column_dropdown, + column_dropdown_data ); const operations = this.cellOperations( diff --git a/src/dash-table/components/Table/props.ts b/src/dash-table/components/Table/props.ts index 2c4db2b1b..005d275ab 100644 --- a/src/dash-table/components/Table/props.ts +++ b/src/dash-table/components/Table/props.ts @@ -188,15 +188,14 @@ interface IConditionalDropdown extends IDropdown { condition: string; } -export interface IColumnDropdown extends IDropdown { - clearable?: boolean; - dropdown: DropdownValues; -} - export interface IDataDropdowns { [key: string]: (IDropdown | undefined)[]; } +export interface IColumnDropdowns { + [key: string]: IDropdown; +} + export interface IConditionalColumnDropdown { id: string; dropdowns: IConditionalDropdown[]; @@ -275,12 +274,12 @@ export interface IProps { active_cell?: ICellCoordinates; columns?: Columns; - column_conditional_dropdowns?: IConditionalColumnDropdown[]; - column_static_dropdown?: IColumnDropdown[]; + column_dropdown?: IColumnDropdowns; + column_dropdown_conditional?: IConditionalColumnDropdown[]; + column_dropdown_data: IDataDropdowns; content_style: ContentStyle; css?: IStylesheetRule[]; data?: Data; - dropdown_properties: IDataDropdowns; editable?: boolean; filter?: string; filtering?: Filtering; @@ -320,8 +319,9 @@ export interface IProps { interface IDefaultProps { active_cell: ICellCoordinates; columns: Columns; - column_conditional_dropdowns: IConditionalColumnDropdown[]; - column_static_dropdown: IColumnDropdown[]; + column_dropdown: IColumnDropdowns; + column_dropdown_conditional: IConditionalColumnDropdown[]; + column_dropdown_data: IDataDropdowns; css: IStylesheetRule[]; data: Data; editable: boolean; @@ -396,12 +396,11 @@ export type ControlledTableProps = PropsWithDefaults & IState & { export interface ICellFactoryProps { active_cell: ICellCoordinates; columns: VisibleColumns; - column_conditional_dropdowns: IConditionalColumnDropdown[]; - column_conditional_tooltips: ConditionalTooltip[]; - column_static_dropdown: IColumnDropdown[]; + column_dropdown: IColumnDropdowns; + column_dropdown_conditional: IConditionalColumnDropdown[]; + column_dropdown_data: IDataDropdowns; column_static_tooltip: ITableStaticTooltips; data: Data; - dropdown_properties: IDataDropdowns; editable: boolean; id: string; is_focused?: boolean; diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index f7266848b..04f6390a5 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -59,8 +59,9 @@ export const defaultProps = { derived_virtual_selected_rows: [], derived_virtual_selected_row_ids: [], - column_conditional_dropdowns: [], - column_static_dropdown: [], + column_dropdown: {}, + column_dropdown_conditional: [], + column_dropdown_data: {}, column_static_tooltip: {}, column_conditional_tooltips: [], @@ -565,7 +566,30 @@ export const propTypes = { }), /** - * `column_conditional_dropdowns` specifies the available options + * `column_dropdown` represents the available dropdown + * options for different columns. + * The `id` property refers to the column ID. + * The `dropdown` property refers to the `options` of the + * dropdown. + * + * NOTE: The naming and the behavior of this option may change + * in the future. + * Tune in to [https://github.com/plotly/dash-table/issues/168](https://github.com/plotly/dash-table/issues/168) + */ + column_dropdown: PropTypes.objectOf(PropTypes.shape({ + clearable: PropTypes.bool, + // .exact + dropdown: PropTypes.arrayOf(PropTypes.shape({ + label: PropTypes.string.isRequired, + value: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired + })).isRequired + })), + + /** + * `column_dropdown_conditional` specifies the available options * for dropdowns in various columns and cells. * This property allows you to specify different dropdowns * depending on certain conditions. For example, you may @@ -576,7 +600,7 @@ export const propTypes = { * in the future. * Tune in to [https://github.com/plotly/dash-table/issues/168](https://github.com/plotly/dash-table/issues/168) */ - column_conditional_dropdowns: PropTypes.arrayOf(PropTypes.shape({ + column_dropdown_conditional: PropTypes.arrayOf(PropTypes.shape({ id: PropTypes.string.isRequired, // .exact dropdowns: PropTypes.arrayOf(PropTypes.shape({ @@ -592,29 +616,25 @@ export const propTypes = { })).isRequired })).isRequired })), + /** - * `column_static_dropdown` represents the available dropdown - * options for different columns. - * The `id` property refers to the column ID. - * The `dropdown` property refers to the `options` of the - * dropdown. * - * NOTE: The naming and the behavior of this option may change - * in the future. - * Tune in to [https://github.com/plotly/dash-table/issues/168](https://github.com/plotly/dash-table/issues/168) */ - column_static_dropdown: PropTypes.arrayOf(PropTypes.shape({ - clearable: PropTypes.bool, - id: PropTypes.string.isRequired, - // .exact - dropdown: PropTypes.arrayOf(PropTypes.shape({ - label: PropTypes.string.isRequired, - value: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string - ]).isRequired - })).isRequired - })), + column_dropdown_data: PropTypes.objectOf( + PropTypes.arrayOf( + PropTypes.shape({ + clearable: PropTypes.bool, + // .exact + dropdown: PropTypes.arrayOf(PropTypes.shape({ + label: PropTypes.string.isRequired, + value: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired + })).isRequired + }) + ) + ), /** * `column_static_tooltip` represents the tooltip shown @@ -1088,25 +1108,6 @@ export const propTypes = { */ derived_virtual_selected_row_ids: PropTypes.arrayOf( PropTypes.oneOfType([PropTypes.string, PropTypes.number]) - ), - - /** - * - */ - dropdown_properties: PropTypes.objectOf( - PropTypes.arrayOf( - PropTypes.shape({ - clearable: PropTypes.bool, - // .exact - dropdown: PropTypes.arrayOf(PropTypes.shape({ - label: PropTypes.string.isRequired, - value: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string - ]).isRequired - })).isRequired - }) - ) ) }; diff --git a/src/dash-table/derived/cell/dropdowns.ts b/src/dash-table/derived/cell/dropdowns.ts index edaf6f097..d18d01415 100644 --- a/src/dash-table/derived/cell/dropdowns.ts +++ b/src/dash-table/derived/cell/dropdowns.ts @@ -16,7 +16,7 @@ import { IBaseVisibleColumn, IVisibleColumn, IDataDropdowns, - IColumnDropdown, + IColumnDropdowns, IConditionalColumnDropdown, IDropdown } from 'dash-table/components/Table/props'; @@ -35,15 +35,15 @@ class Dropdowns { data: Data, indices: Indices, columnConditionalDropdown: IConditionalColumnDropdown[], - columnStaticDropdown: IColumnDropdown[], - dropdown_properties: IDataDropdowns + columnStaticDropdown: IColumnDropdowns, + column_dropdown_data: IDataDropdowns ) => mapData((datum, rowIndex) => R.map(column => { const applicable = this.applicable.get(column.id, rowIndex)( column, indices[rowIndex], columnConditionalDropdown, columnStaticDropdown, - dropdown_properties + column_dropdown_data ); return this.dropdown.get(column.id, rowIndex)( @@ -60,18 +60,18 @@ class Dropdowns { column: IBaseVisibleColumn, realIndex: number, columnConditionalDropdown: IConditionalColumnDropdown[], - columnStaticDropdown: IColumnDropdown[], - dropdown_properties: IDataDropdowns + columnStaticDropdown: IColumnDropdowns, + column_dropdown_data: IDataDropdowns ): [IDropdown | null, IConditionalDropdown[]] => { const legacyDropdown = - dropdown_properties && - dropdown_properties[column.id] && - dropdown_properties[column.id].length > realIndex && - dropdown_properties[column.id][realIndex] && - dropdown_properties[column.id][realIndex]; + column_dropdown_data && + column_dropdown_data[column.id] && + column_dropdown_data[column.id].length > realIndex && + column_dropdown_data[column.id][realIndex] && + column_dropdown_data[column.id][realIndex]; const conditional = columnConditionalDropdown.find((cs: any) => cs.id === column.id); - const base = columnStaticDropdown.find((ss: any) => ss.id === column.id); + const base = columnStaticDropdown[column.id]; return [ legacyDropdown || base || null, diff --git a/tests/visual/percy-storybook/Dropdown.percy.tsx b/tests/visual/percy-storybook/Dropdown.percy.tsx index 40c423f4c..53dfb3e63 100644 --- a/tests/visual/percy-storybook/Dropdown.percy.tsx +++ b/tests/visual/percy-storybook/Dropdown.percy.tsx @@ -36,19 +36,20 @@ storiesOf('DashTable/Dropdown', module) data={data} columns={columns} editable={true} - column_static_dropdown={[{ - id: 'climate', - dropdown: R.map( - i => ({ label: i, value: i }), - ['Sunny', 'Snowy', 'Rainy'] - ) - }, { - id: 'city', - dropdown: R.map( - i => ({ label: i, value: i }), - ['NYC', 'Montreal', 'Miami'] - ) - }]} + column_dropdown={{ + climate: { + dropdown: R.map( + i => ({ label: i, value: i }), + ['Sunny', 'Snowy', 'Rainy'] + ) + }, + city: { + dropdown: R.map( + i => ({ label: i, value: i }), + ['NYC', 'Montreal', 'Miami'] + ) + } + }} />)) .add('dropdown by filtering', () => ( ({ label: i, value: i }), diff --git a/tests/visual/percy-storybook/fixtures.ts b/tests/visual/percy-storybook/fixtures.ts index 4e5b59ff5..4fd028dba 100644 --- a/tests/visual/percy-storybook/fixtures.ts +++ b/tests/visual/percy-storybook/fixtures.ts @@ -13,7 +13,7 @@ export default [ presentation: Presentation.Dropdown } ], - column_static_dropdown: [{ + column_dropdown: [{ id: 'column-1', dropdown: [ { @@ -142,7 +142,7 @@ export default [ presentation: Presentation.Dropdown } ], - column_static_dropdown: [{ + column_dropdown: [{ id: 'column-1', dropdown: [ { @@ -194,7 +194,7 @@ export default [ presentation: Presentation.Dropdown } ], - column_static_dropdown: [{ + column_dropdown: [{ id: 'column-1', dropdown: [ { @@ -272,7 +272,7 @@ export default [ presentation: Presentation.Dropdown } ], - column_static_dropdown: [{ + column_dropdown: [{ id: 'region', dropdown: [ { From 5deb109c3c624730977384113bf930ce754ba865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Wed, 29 May 2019 12:42:02 -0400 Subject: [PATCH 10/46] rename `column_dropdown_*` props to `dropdown_*` --- src/dash-table/components/CellFactory.tsx | 12 ++++++------ src/dash-table/components/Table/props.ts | 18 +++++++++--------- src/dash-table/dash/DataTable.js | 16 ++++++++-------- src/dash-table/derived/cell/dropdowns.ts | 16 ++++++++-------- .../visual/percy-storybook/Dropdown.percy.tsx | 6 +++--- tests/visual/percy-storybook/fixtures.ts | 8 ++++---- 6 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/dash-table/components/CellFactory.tsx b/src/dash-table/components/CellFactory.tsx index 46b36bbf6..128af4193 100644 --- a/src/dash-table/components/CellFactory.tsx +++ b/src/dash-table/components/CellFactory.tsx @@ -34,10 +34,10 @@ export default class CellFactory { const { active_cell, columns, - column_dropdown_conditional, - column_dropdown, + dropdown_conditional, + dropdown, data, - column_dropdown_data, + dropdown_data, editable, is_focused, row_deletable, @@ -77,9 +77,9 @@ export default class CellFactory { columns, virtualized.data, virtualized.indices, - column_dropdown_conditional, - column_dropdown, - column_dropdown_data + dropdown_conditional, + dropdown, + dropdown_data ); const operations = this.cellOperations( diff --git a/src/dash-table/components/Table/props.ts b/src/dash-table/components/Table/props.ts index 005d275ab..368cc1ef9 100644 --- a/src/dash-table/components/Table/props.ts +++ b/src/dash-table/components/Table/props.ts @@ -274,9 +274,9 @@ export interface IProps { active_cell?: ICellCoordinates; columns?: Columns; - column_dropdown?: IColumnDropdowns; - column_dropdown_conditional?: IConditionalColumnDropdown[]; - column_dropdown_data: IDataDropdowns; + dropdown?: IColumnDropdowns; + dropdown_conditional?: IConditionalColumnDropdown[]; + dropdown_data: IDataDropdowns; content_style: ContentStyle; css?: IStylesheetRule[]; data?: Data; @@ -319,9 +319,9 @@ export interface IProps { interface IDefaultProps { active_cell: ICellCoordinates; columns: Columns; - column_dropdown: IColumnDropdowns; - column_dropdown_conditional: IConditionalColumnDropdown[]; - column_dropdown_data: IDataDropdowns; + dropdown: IColumnDropdowns; + dropdown_conditional: IConditionalColumnDropdown[]; + dropdown_data: IDataDropdowns; css: IStylesheetRule[]; data: Data; editable: boolean; @@ -396,9 +396,9 @@ export type ControlledTableProps = PropsWithDefaults & IState & { export interface ICellFactoryProps { active_cell: ICellCoordinates; columns: VisibleColumns; - column_dropdown: IColumnDropdowns; - column_dropdown_conditional: IConditionalColumnDropdown[]; - column_dropdown_data: IDataDropdowns; + dropdown: IColumnDropdowns; + dropdown_conditional: IConditionalColumnDropdown[]; + dropdown_data: IDataDropdowns; column_static_tooltip: ITableStaticTooltips; data: Data; editable: boolean; diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index 04f6390a5..3341c32d2 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -59,9 +59,9 @@ export const defaultProps = { derived_virtual_selected_rows: [], derived_virtual_selected_row_ids: [], - column_dropdown: {}, - column_dropdown_conditional: [], - column_dropdown_data: {}, + dropdown: {}, + dropdown_conditional: [], + dropdown_data: {}, column_static_tooltip: {}, column_conditional_tooltips: [], @@ -566,7 +566,7 @@ export const propTypes = { }), /** - * `column_dropdown` represents the available dropdown + * `dropdown` represents the available dropdown * options for different columns. * The `id` property refers to the column ID. * The `dropdown` property refers to the `options` of the @@ -576,7 +576,7 @@ export const propTypes = { * in the future. * Tune in to [https://github.com/plotly/dash-table/issues/168](https://github.com/plotly/dash-table/issues/168) */ - column_dropdown: PropTypes.objectOf(PropTypes.shape({ + dropdown: PropTypes.objectOf(PropTypes.shape({ clearable: PropTypes.bool, // .exact dropdown: PropTypes.arrayOf(PropTypes.shape({ @@ -589,7 +589,7 @@ export const propTypes = { })), /** - * `column_dropdown_conditional` specifies the available options + * `dropdown_conditional` specifies the available options * for dropdowns in various columns and cells. * This property allows you to specify different dropdowns * depending on certain conditions. For example, you may @@ -600,7 +600,7 @@ export const propTypes = { * in the future. * Tune in to [https://github.com/plotly/dash-table/issues/168](https://github.com/plotly/dash-table/issues/168) */ - column_dropdown_conditional: PropTypes.arrayOf(PropTypes.shape({ + dropdown_conditional: PropTypes.arrayOf(PropTypes.shape({ id: PropTypes.string.isRequired, // .exact dropdowns: PropTypes.arrayOf(PropTypes.shape({ @@ -620,7 +620,7 @@ export const propTypes = { /** * */ - column_dropdown_data: PropTypes.objectOf( + dropdown_data: PropTypes.objectOf( PropTypes.arrayOf( PropTypes.shape({ clearable: PropTypes.bool, diff --git a/src/dash-table/derived/cell/dropdowns.ts b/src/dash-table/derived/cell/dropdowns.ts index d18d01415..38bd6aaa9 100644 --- a/src/dash-table/derived/cell/dropdowns.ts +++ b/src/dash-table/derived/cell/dropdowns.ts @@ -36,14 +36,14 @@ class Dropdowns { indices: Indices, columnConditionalDropdown: IConditionalColumnDropdown[], columnStaticDropdown: IColumnDropdowns, - column_dropdown_data: IDataDropdowns + dropdown_data: IDataDropdowns ) => mapData((datum, rowIndex) => R.map(column => { const applicable = this.applicable.get(column.id, rowIndex)( column, indices[rowIndex], columnConditionalDropdown, columnStaticDropdown, - column_dropdown_data + dropdown_data ); return this.dropdown.get(column.id, rowIndex)( @@ -61,14 +61,14 @@ class Dropdowns { realIndex: number, columnConditionalDropdown: IConditionalColumnDropdown[], columnStaticDropdown: IColumnDropdowns, - column_dropdown_data: IDataDropdowns + dropdown_data: IDataDropdowns ): [IDropdown | null, IConditionalDropdown[]] => { const legacyDropdown = - column_dropdown_data && - column_dropdown_data[column.id] && - column_dropdown_data[column.id].length > realIndex && - column_dropdown_data[column.id][realIndex] && - column_dropdown_data[column.id][realIndex]; + dropdown_data && + dropdown_data[column.id] && + dropdown_data[column.id].length > realIndex && + dropdown_data[column.id][realIndex] && + dropdown_data[column.id][realIndex]; const conditional = columnConditionalDropdown.find((cs: any) => cs.id === column.id); const base = columnStaticDropdown[column.id]; diff --git a/tests/visual/percy-storybook/Dropdown.percy.tsx b/tests/visual/percy-storybook/Dropdown.percy.tsx index 53dfb3e63..0bd36932f 100644 --- a/tests/visual/percy-storybook/Dropdown.percy.tsx +++ b/tests/visual/percy-storybook/Dropdown.percy.tsx @@ -36,7 +36,7 @@ storiesOf('DashTable/Dropdown', module) data={data} columns={columns} editable={true} - column_dropdown={{ + dropdown={{ climate: { dropdown: R.map( i => ({ label: i, value: i }), @@ -57,7 +57,7 @@ storiesOf('DashTable/Dropdown', module) data={data2} columns={columns2} editable={true} - column_dropdown_conditional={[{ + dropdown_conditional={[{ id: 'Neighborhood', dropdowns: [{ condition: '{City} eq "NYC"', @@ -85,7 +85,7 @@ storiesOf('DashTable/Dropdown', module) data={data2} columns={columns2} editable={true} - column_dropdown_data={{ + dropdown_data={{ Neighborhood: [{ dropdown: R.map( i => ({ label: i, value: i }), diff --git a/tests/visual/percy-storybook/fixtures.ts b/tests/visual/percy-storybook/fixtures.ts index 4fd028dba..a4368e109 100644 --- a/tests/visual/percy-storybook/fixtures.ts +++ b/tests/visual/percy-storybook/fixtures.ts @@ -13,7 +13,7 @@ export default [ presentation: Presentation.Dropdown } ], - column_dropdown: [{ + dropdown: [{ id: 'column-1', dropdown: [ { @@ -142,7 +142,7 @@ export default [ presentation: Presentation.Dropdown } ], - column_dropdown: [{ + dropdown: [{ id: 'column-1', dropdown: [ { @@ -194,7 +194,7 @@ export default [ presentation: Presentation.Dropdown } ], - column_dropdown: [{ + dropdown: [{ id: 'column-1', dropdown: [ { @@ -272,7 +272,7 @@ export default [ presentation: Presentation.Dropdown } ], - column_dropdown: [{ + dropdown: [{ id: 'region', dropdown: [ { From fefa090e8b48f2802f8a050e14ba58378ca49949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Wed, 29 May 2019 13:03:42 -0400 Subject: [PATCH 11/46] update standalone tests --- demo/AppMode.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/demo/AppMode.ts b/demo/AppMode.ts index 118d8a78a..5f8d7b9be 100644 --- a/demo/AppMode.ts +++ b/demo/AppMode.ts @@ -49,15 +49,15 @@ function getBaseTableProps(mock: IDataMock) { renamable: true, deletable: true })), - column_static_dropdown: [ - { - id: 'bbb', + dropdown: { + bbb: { + clearable: true, dropdown: ['Humid', 'Wet', 'Snowy', 'Tropical Beaches'].map(i => ({ label: i, value: i })) } - ], + }, pagination_mode: false, style_table: { max_height: '800px', From 9735a69af7c6e67fbd98b64768d554b183511c09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Wed, 29 May 2019 14:29:35 -0400 Subject: [PATCH 12/46] - column_id and filter on dropdown_conditional --- .../components/CellDropdown/types.ts | 6 - src/dash-table/components/Table/props.ts | 30 ++--- src/dash-table/dash/DataTable.js | 51 ++++----- src/dash-table/derived/cell/dropdowns.ts | 103 +++++++----------- .../visual/percy-storybook/Dropdown.percy.tsx | 47 ++++---- 5 files changed, 97 insertions(+), 140 deletions(-) delete mode 100644 src/dash-table/components/CellDropdown/types.ts diff --git a/src/dash-table/components/CellDropdown/types.ts b/src/dash-table/components/CellDropdown/types.ts deleted file mode 100644 index 3e3489c50..000000000 --- a/src/dash-table/components/CellDropdown/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { DropdownValues } from '../Table/props'; - -export interface IConditionalDropdown { - condition: string; - dropdown: DropdownValues; -} \ No newline at end of file diff --git a/src/dash-table/components/Table/props.ts b/src/dash-table/components/Table/props.ts index 368cc1ef9..9882d82b3 100644 --- a/src/dash-table/components/Table/props.ts +++ b/src/dash-table/components/Table/props.ts @@ -13,6 +13,7 @@ import { Tooltip } from 'dash-table/tooltips/props'; import { SingleColumnSyntaxTree } from 'dash-table/syntax-tree'; +import { IConditionalElement, INamedElement } from 'dash-table/conditional'; export enum ColumnType { Any = 'any', @@ -184,27 +185,20 @@ export interface IDropdown { dropdown: DropdownValues; } -interface IConditionalDropdown extends IDropdown { - condition: string; +export interface IConditionalDropdown extends IDropdown { + if: Partial; } +export type ConditionalDropdowns = IConditionalDropdown[]; + export interface IDataDropdowns { [key: string]: (IDropdown | undefined)[]; } -export interface IColumnDropdowns { +export interface IStaticDropdowns { [key: string]: IDropdown; } -export interface IConditionalColumnDropdown { - id: string; - dropdowns: IConditionalDropdown[]; -} - -export interface IDropdownProperties { - [key: string]: { options: DropdownValues }[]; -} - export interface ITableTooltips { [key: string]: Tooltip[]; } @@ -274,8 +268,8 @@ export interface IProps { active_cell?: ICellCoordinates; columns?: Columns; - dropdown?: IColumnDropdowns; - dropdown_conditional?: IConditionalColumnDropdown[]; + dropdown?: IStaticDropdowns; + dropdown_conditional?: ConditionalDropdowns; dropdown_data: IDataDropdowns; content_style: ContentStyle; css?: IStylesheetRule[]; @@ -319,8 +313,8 @@ export interface IProps { interface IDefaultProps { active_cell: ICellCoordinates; columns: Columns; - dropdown: IColumnDropdowns; - dropdown_conditional: IConditionalColumnDropdown[]; + dropdown: IStaticDropdowns; + dropdown_conditional: ConditionalDropdowns; dropdown_data: IDataDropdowns; css: IStylesheetRule[]; data: Data; @@ -396,8 +390,8 @@ export type ControlledTableProps = PropsWithDefaults & IState & { export interface ICellFactoryProps { active_cell: ICellCoordinates; columns: VisibleColumns; - dropdown: IColumnDropdowns; - dropdown_conditional: IConditionalColumnDropdown[]; + dropdown: IStaticDropdowns; + dropdown_conditional: ConditionalDropdowns; dropdown_data: IDataDropdowns; column_static_tooltip: ITableStaticTooltips; data: Data; diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index 3341c32d2..0032a5019 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -576,10 +576,9 @@ export const propTypes = { * in the future. * Tune in to [https://github.com/plotly/dash-table/issues/168](https://github.com/plotly/dash-table/issues/168) */ - dropdown: PropTypes.objectOf(PropTypes.shape({ + dropdown: PropTypes.objectOf(PropTypes.exact({ clearable: PropTypes.bool, - // .exact - dropdown: PropTypes.arrayOf(PropTypes.shape({ + dropdown: PropTypes.arrayOf(PropTypes.exact({ label: PropTypes.string.isRequired, value: PropTypes.oneOfType([ PropTypes.number, @@ -600,20 +599,18 @@ export const propTypes = { * in the future. * Tune in to [https://github.com/plotly/dash-table/issues/168](https://github.com/plotly/dash-table/issues/168) */ - dropdown_conditional: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.string.isRequired, - // .exact - dropdowns: PropTypes.arrayOf(PropTypes.shape({ - clearable: PropTypes.bool, - condition: PropTypes.string.isRequired, - // .exact - dropdown: PropTypes.arrayOf(PropTypes.shape({ - label: PropTypes.string.isRequired, - value: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string - ]).isRequired - })).isRequired + dropdown_conditional: PropTypes.arrayOf(PropTypes.exact({ + clearable: PropTypes.bool, + if: PropTypes.exact({ + column_id: PropTypes.string, + filter: PropTypes.string, + }), + dropdown: PropTypes.arrayOf(PropTypes.exact({ + label: PropTypes.string.isRequired, + value: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]).isRequired })).isRequired })), @@ -622,10 +619,9 @@ export const propTypes = { */ dropdown_data: PropTypes.objectOf( PropTypes.arrayOf( - PropTypes.shape({ + PropTypes.exact({ clearable: PropTypes.bool, - // .exact - dropdown: PropTypes.arrayOf(PropTypes.shape({ + dropdown: PropTypes.arrayOf(PropTypes.exact({ label: PropTypes.string.isRequired, value: PropTypes.oneOfType([ PropTypes.number, @@ -879,8 +875,7 @@ export const propTypes = { * clicked. */ sort_by: PropTypes.arrayOf( - // .exact - PropTypes.shape({ + PropTypes.exact({ column_id: PropTypes.string.isRequired, direction: PropTypes.oneOf(['asc', 'desc']).isRequired })), @@ -937,8 +932,7 @@ export const propTypes = { * This can be used to apply styles to cells on a per-column basis. */ style_cell_conditional: PropTypes.arrayOf(PropTypes.shape({ - // .exact - if: PropTypes.shape({ + if: PropTypes.exact({ column_id: PropTypes.string, column_type: PropTypes.oneOf(['any', 'numeric', 'text', 'datetime']) }) @@ -950,8 +944,7 @@ export const propTypes = { * This can be used to apply styles to data cells on a per-column basis. */ style_data_conditional: PropTypes.arrayOf(PropTypes.shape({ - // .exact - if: PropTypes.shape({ + if: PropTypes.exact({ column_id: PropTypes.string, column_type: PropTypes.oneOf(['any', 'numeric', 'text', 'datetime']), filter: PropTypes.string, @@ -968,8 +961,7 @@ export const propTypes = { * This can be used to apply styles to filter cells on a per-column basis. */ style_filter_conditional: PropTypes.arrayOf(PropTypes.shape({ - // .exact - if: PropTypes.shape({ + if: PropTypes.exact({ column_id: PropTypes.string, column_type: PropTypes.oneOf(['any', 'numeric', 'text', 'datetime']) }) @@ -981,8 +973,7 @@ export const propTypes = { * This can be used to apply styles to header cells on a per-column basis. */ style_header_conditional: PropTypes.arrayOf(PropTypes.shape({ - // .exact - if: PropTypes.shape({ + if: PropTypes.exact({ column_id: PropTypes.string, column_type: PropTypes.oneOf(['any', 'numeric', 'text', 'datetime']), header_index: PropTypes.oneOfType([ diff --git a/src/dash-table/derived/cell/dropdowns.ts b/src/dash-table/derived/cell/dropdowns.ts index 38bd6aaa9..08b79b2fc 100644 --- a/src/dash-table/derived/cell/dropdowns.ts +++ b/src/dash-table/derived/cell/dropdowns.ts @@ -3,24 +3,21 @@ import * as R from 'ramda'; import { memoizeOne } from 'core/memoizer'; import memoizerCache from 'core/cache/memoizer'; -import { - IConditionalDropdown -} from 'dash-table/components/CellDropdown/types'; - import { Data, Datum, VisibleColumns, ColumnId, Indices, - IBaseVisibleColumn, IVisibleColumn, IDataDropdowns, - IColumnDropdowns, - IConditionalColumnDropdown, - IDropdown + IStaticDropdowns, + ConditionalDropdowns, + IDropdown, + IConditionalDropdown } from 'dash-table/components/Table/props'; import { QuerySyntaxTree } from 'dash-table/syntax-tree'; +import { ifColumnId } from 'dash-table/conditional'; const mapData = R.addIndex(R.map); @@ -34,82 +31,56 @@ class Dropdowns { columns: VisibleColumns, data: Data, indices: Indices, - columnConditionalDropdown: IConditionalColumnDropdown[], - columnStaticDropdown: IColumnDropdowns, - dropdown_data: IDataDropdowns + conditionalDropdowns: ConditionalDropdowns, + staticDropdowns: IStaticDropdowns, + dataDropdowns: IDataDropdowns ) => mapData((datum, rowIndex) => R.map(column => { - const applicable = this.applicable.get(column.id, rowIndex)( - column, - indices[rowIndex], - columnConditionalDropdown, - columnStaticDropdown, - dropdown_data - ); + const realIndex = indices[rowIndex]; + + const appliedStaticDropdown = ( + dataDropdowns && + dataDropdowns[column.id] && + dataDropdowns[column.id].length > realIndex && + dataDropdowns[column.id][realIndex] && + dataDropdowns[column.id][realIndex] + ) || staticDropdowns[column.id] || null; return this.dropdown.get(column.id, rowIndex)( - applicable, + appliedStaticDropdown, + conditionalDropdowns, column, datum ); }, columns), data)); - /** - * Returns the list of applicable dropdowns for a cell. - */ - private readonly applicable = memoizerCache<[ColumnId, number]>()(( - column: IBaseVisibleColumn, - realIndex: number, - columnConditionalDropdown: IConditionalColumnDropdown[], - columnStaticDropdown: IColumnDropdowns, - dropdown_data: IDataDropdowns - ): [IDropdown | null, IConditionalDropdown[]] => { - const legacyDropdown = - dropdown_data && - dropdown_data[column.id] && - dropdown_data[column.id].length > realIndex && - dropdown_data[column.id][realIndex] && - dropdown_data[column.id][realIndex]; - - const conditional = columnConditionalDropdown.find((cs: any) => cs.id === column.id); - const base = columnStaticDropdown[column.id]; - - return [ - legacyDropdown || base || null, - (conditional && conditional.dropdowns) || [] - ]; - }); - /** * Returns the highest priority dropdown from the * applicable dropdowns. */ private readonly dropdown = memoizerCache<[ColumnId, number]>()(( - applicableDropdowns: [IDropdown | null, IConditionalDropdown[]], + base: IDropdown | null, + conditionals: ConditionalDropdowns, column: IVisibleColumn, datum: Datum ) => { - const [staticDropdown, conditionalDropdowns] = applicableDropdowns; - - const matches: IDropdown[] = []; - - if (staticDropdown) { - matches.push(staticDropdown); - } - - matches.push(...R.map( - ([cd]) => cd, - R.filter<[IConditionalDropdown, number]>( - ([cd, i]) => this.evaluation.get(column.id, i)( - this.ast.get(column.id, i)(cd.condition), - datum + const conditional = R.findLast( + ([cd, i]) => + ifColumnId(cd.if, column.id) && + ( + R.isNil(cd.if) || + R.isNil(cd.if.filter) || + this.evaluation.get(column.id, i)( + this.ast.get(column.id, i)(cd.if.filter), + datum + ) ), - R.addIndex(R.map)( - (cd, i) => [cd, i], - conditionalDropdowns - )) - )); + R.addIndex(R.map)( + (cd, i) => [cd, i], + conditionals + ) + ); - return matches.length ? matches.slice(-1)[0] : undefined; + return (conditional && conditional[0]) || base || undefined; }); /** diff --git a/tests/visual/percy-storybook/Dropdown.percy.tsx b/tests/visual/percy-storybook/Dropdown.percy.tsx index 0bd36932f..29d735ad3 100644 --- a/tests/visual/percy-storybook/Dropdown.percy.tsx +++ b/tests/visual/percy-storybook/Dropdown.percy.tsx @@ -58,26 +58,33 @@ storiesOf('DashTable/Dropdown', module) columns={columns2} editable={true} dropdown_conditional={[{ - id: 'Neighborhood', - dropdowns: [{ - condition: '{City} eq "NYC"', - dropdown: R.map( - i => ({ label: i, value: i }), - ['Brooklyn', 'Queens', 'Staten Island'] - ) - }, { - condition: '{City} eq "Montreal"', - dropdown: R.map( - i => ({ label: i, value: i }), - ['Mile End', 'Plateau', 'Hochelaga'] - ) - }, { - condition: '{City} eq "Los Angeles"', - dropdown: R.map( - i => ({ label: i, value: i }), - ['Venice', 'Hollywood', 'Los Feliz'] - ) - }] + if: { + column_id: 'Neighborhood', + filter: '{City} eq "NYC"' + }, + dropdown: R.map( + i => ({ label: i, value: i }), + ['Brooklyn', 'Queens', 'Staten Island'] + ) + }, { + if: { + column_id: 'Neighborhood', + filter: '{City} eq "Montreal"' + }, + dropdown: R.map( + i => ({ label: i, value: i }), + ['Mile End', 'Plateau', 'Hochelaga'] + ) + }, + { + if: { + column_id: 'Neighborhood', + filter: '{City} eq "Los Angeles"' + }, + dropdown: R.map( + i => ({ label: i, value: i }), + ['Venice', 'Hollywood', 'Los Feliz'] + ) }]} />)).add('dropdown by cell (deprecated)', () => ( Date: Wed, 29 May 2019 14:32:54 -0400 Subject: [PATCH 13/46] lint --- src/dash-table/dash/DataTable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index 0032a5019..2263f974b 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -603,7 +603,7 @@ export const propTypes = { clearable: PropTypes.bool, if: PropTypes.exact({ column_id: PropTypes.string, - filter: PropTypes.string, + filter: PropTypes.string }), dropdown: PropTypes.arrayOf(PropTypes.exact({ label: PropTypes.string.isRequired, From 1ff1394841cd9e2e0c111258a4c1eb6e0c16e99e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Wed, 29 May 2019 15:05:13 -0400 Subject: [PATCH 14/46] update fixtures --- tests/visual/percy-storybook/fixtures.ts | 100 +++++++---------------- 1 file changed, 28 insertions(+), 72 deletions(-) diff --git a/tests/visual/percy-storybook/fixtures.ts b/tests/visual/percy-storybook/fixtures.ts index a4368e109..3c6d5ce16 100644 --- a/tests/visual/percy-storybook/fixtures.ts +++ b/tests/visual/percy-storybook/fixtures.ts @@ -13,19 +13,12 @@ export default [ presentation: Presentation.Dropdown } ], - dropdown: [{ - id: 'column-1', - dropdown: [ - { - label: 'Montréal', - value: 'mtl' - }, - { - label: 'San Francisco', - value: 'sf' - } + dropdown: { + 'column-1': [ + { label: 'Montréal', value: 'mtl' }, + { label: 'San Francisco', value: 'sf' } ] - }], + }, data: [ {'column-1': 'mtl'}, {'column-1': 'sf'}, @@ -142,19 +135,12 @@ export default [ presentation: Presentation.Dropdown } ], - dropdown: [{ - id: 'column-1', - dropdown: [ - { - label: 'Montréal', - value: 'mtl' - }, - { - label: 'San Francisco', - value: 'sf' - } + dropdown: { + 'column-1': [ + { label: 'Montréal', value: 'mtl' }, + { label: 'San Francisco', value: 'sf' } ] - }], + }, data: [ {'column-1': 'mtl'}, {'column-1': 'sf'}, @@ -194,43 +180,20 @@ export default [ presentation: Presentation.Dropdown } ], - dropdown: [{ - id: 'column-1', - dropdown: [ - { - label: 'Montréal', - value: 'mtl' - }, - { - label: 'San Francisco', - value: 'sf' - } - ] - }, { - id: 'column-2', - dropdown: [ - { - label: 'Montréal', - value: 'mtl' - }, - { - label: 'San Francisco', - value: 'sf' - } + dropdown: { + 'column-1': [ + { label: 'Montréal', value: 'mtl' }, + { label: 'San Francisco', value: 'sf' } + ], + 'column-2': [ + { label: 'Montréal', value: 'mtl' }, + { label: 'San Francisco', value: 'sf' } + ], + 'column-3': [ + { label: 'Montréal', value: 'mtl' }, + { label: 'San Francisco', value: 'sf' } ] - }, { - id: 'column-3', - dropdown: [ - { - label: 'Montréal', - value: 'mtl' - }, - { - label: 'San Francisco', - value: 'sf' - } - ] - }], + }, data: [ {'column-1': 'mtl', 'column-2': 'mtl', 'column-3': 'mtl'}, {'column-1': 'mtl', 'column-2': 'mtl', 'column-3': 'mtl'}, @@ -272,19 +235,12 @@ export default [ presentation: Presentation.Dropdown } ], - dropdown: [{ - id: 'region', - dropdown: [ - { - label: 'Hawaii', - value: 'hawaii' - }, - { - label: 'Costa Rica', - value: 'costa-rica' - } + dropdown: { + region: [ + { label: 'Hawaii', value: 'hawaii' }, + { label: 'Costa Rica', value: 'costa-rica' } ] - }], + }, merge_duplicate_headers: true, data: [ { From dfff80f2c2376d1191024a55b44dce6caf34f013 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 30 May 2019 08:19:32 -0400 Subject: [PATCH 15/46] fix dropdown usage --- tests/visual/percy-storybook/fixtures.ts | 60 ++++++++++++++---------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/tests/visual/percy-storybook/fixtures.ts b/tests/visual/percy-storybook/fixtures.ts index 3c6d5ce16..a810b0cff 100644 --- a/tests/visual/percy-storybook/fixtures.ts +++ b/tests/visual/percy-storybook/fixtures.ts @@ -14,10 +14,12 @@ export default [ } ], dropdown: { - 'column-1': [ - { label: 'Montréal', value: 'mtl' }, - { label: 'San Francisco', value: 'sf' } - ] + 'column-1': { + dropdown: [ + { label: 'Montréal', value: 'mtl' }, + { label: 'San Francisco', value: 'sf' } + ] + } }, data: [ {'column-1': 'mtl'}, @@ -136,10 +138,12 @@ export default [ } ], dropdown: { - 'column-1': [ - { label: 'Montréal', value: 'mtl' }, - { label: 'San Francisco', value: 'sf' } - ] + 'column-1': { + dropdown: [ + { label: 'Montréal', value: 'mtl' }, + { label: 'San Francisco', value: 'sf' } + ] + } }, data: [ {'column-1': 'mtl'}, @@ -181,18 +185,24 @@ export default [ } ], dropdown: { - 'column-1': [ - { label: 'Montréal', value: 'mtl' }, - { label: 'San Francisco', value: 'sf' } - ], - 'column-2': [ - { label: 'Montréal', value: 'mtl' }, - { label: 'San Francisco', value: 'sf' } - ], - 'column-3': [ - { label: 'Montréal', value: 'mtl' }, - { label: 'San Francisco', value: 'sf' } - ] + 'column-1': { + dropdown: [ + { label: 'Montréal', value: 'mtl' }, + { label: 'San Francisco', value: 'sf' } + ] + }, + 'column-2': { + dropdown: [ + { label: 'Montréal', value: 'mtl' }, + { label: 'San Francisco', value: 'sf' } + ] + }, + 'column-3': { + dropdown: [ + { label: 'Montréal', value: 'mtl' }, + { label: 'San Francisco', value: 'sf' } + ] + } }, data: [ {'column-1': 'mtl', 'column-2': 'mtl', 'column-3': 'mtl'}, @@ -236,10 +246,12 @@ export default [ } ], dropdown: { - region: [ - { label: 'Hawaii', value: 'hawaii' }, - { label: 'Costa Rica', value: 'costa-rica' } - ] + region: { + dropdown: [ + { label: 'Hawaii', value: 'hawaii' }, + { label: 'Costa Rica', value: 'costa-rica' } + ] + } }, merge_duplicate_headers: true, data: [ From 0dcfcdc4d12d8ab46512b60a6fe2916ff4f8f268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 30 May 2019 08:40:21 -0400 Subject: [PATCH 16/46] rename tooltip props --- demo/AppMode.ts | 6 +-- .../components/ControlledTable/index.tsx | 22 +++++----- src/dash-table/components/Table/props.ts | 26 ++++++------ src/dash-table/dash/DataTable.js | 16 ++++---- src/dash-table/derived/table/tooltip.ts | 40 +++++++++---------- src/dash-table/handlers/cellEvents.ts | 12 +++--- 6 files changed, 61 insertions(+), 61 deletions(-) diff --git a/demo/AppMode.ts b/demo/AppMode.ts index 5f8d7b9be..78722b9b1 100644 --- a/demo/AppMode.ts +++ b/demo/AppMode.ts @@ -132,7 +132,7 @@ function getTooltipsState() { state.tableProps.tooltip_delay = 250; state.tableProps.tooltip_duration = 1000; - state.tableProps.tooltips = { + state.tableProps.tooltip_data = { ccc: [ { type: TooltipSyntax.Markdown, value: `### Go Proverb\nThe enemy's key point is yours` }, { type: TooltipSyntax.Markdown, value: `### Go Proverb\nPlay on the point of symmetry` }, @@ -142,12 +142,12 @@ function getTooltipsState() { `People in glass houses shouldn't throw stones` ] }; - state.tableProps.column_static_tooltip = { + state.tableProps.tooltip = { ccc: { type: TooltipSyntax.Text, value: `There is death in the hane` }, ddd: { type: TooltipSyntax.Markdown, value: `Hane, Cut, Placement` }, rows: `Learn the eyestealing tesuji` }; - state.tableProps.column_conditional_tooltips = [{ + state.tableProps.tooltip_conditional = [{ if: { column_id: 'aaa-readonly', filter: `{aaa} is prime` diff --git a/src/dash-table/components/ControlledTable/index.tsx b/src/dash-table/components/ControlledTable/index.tsx index f40fb6d4e..70f18b4e5 100644 --- a/src/dash-table/components/ControlledTable/index.tsx +++ b/src/dash-table/components/ControlledTable/index.tsx @@ -682,19 +682,19 @@ export default class ControlledTable extends PureComponent const { id, columns, - column_conditional_tooltips, - column_static_tooltip, + tooltip_conditional, + tooltip, content_style, + currentTooltip, filtering, n_fixed_columns, n_fixed_rows, scrollbarWidth, style_as_list_view, style_table, - tooltip, tooltip_delay, tooltip_duration, - tooltips, + tooltip_data, uiCell, uiHeaders, uiViewport, @@ -752,10 +752,10 @@ export default class ControlledTable extends PureComponent /* Tooltip */ let tableTooltip = derivedTooltips( + currentTooltip, + tooltip_data, + tooltip_conditional, tooltip, - tooltips, - column_conditional_tooltips, - column_static_tooltip, virtualized, tooltip_delay, tooltip_duration @@ -807,14 +807,14 @@ export default class ControlledTable extends PureComponent } private adjustTooltipPosition() { - const { tooltip, virtualized } = this.props; + const { currentTooltip, virtualized } = this.props; - if (!tooltip) { + if (!currentTooltip) { return; } - const id = tooltip.id; - const row = tooltip.row - virtualized.offset.rows; + const id = currentTooltip.id; + const row = currentTooltip.row - virtualized.offset.rows; const { table, tooltip: t } = this.refs as { [key: string]: any }; diff --git a/src/dash-table/components/Table/props.ts b/src/dash-table/components/Table/props.ts index 9882d82b3..87b1e17bd 100644 --- a/src/dash-table/components/Table/props.ts +++ b/src/dash-table/components/Table/props.ts @@ -236,17 +236,17 @@ export interface IUSerInterfaceTooltip { } export interface IState { + currentTooltip?: IUSerInterfaceTooltip; forcedResizeOnly: boolean; - workFilter: { - value: string, - map: Map - }; rawFilterQuery: string; scrollbarWidth: number; - tooltip?: IUSerInterfaceTooltip; - uiViewport?: IUserInterfaceViewport; uiCell?: IUserInterfaceCell; uiHeaders?: IUserInterfaceCell[]; + uiViewport?: IUserInterfaceViewport; + workFilter: { + value: string, + map: Map + }; } export type StandaloneState = IState & Partial; @@ -260,11 +260,11 @@ export interface IProps { id: string; - tooltips?: ITableTooltips; + tooltip_data?: ITableTooltips; tooltip_delay: number | null; tooltip_duration: number | null; - column_static_tooltip: ITableStaticTooltips; - column_conditional_tooltips: ConditionalTooltip[]; + tooltip: ITableStaticTooltips; + tooltip_conditional: ConditionalTooltip[]; active_cell?: ICellCoordinates; columns?: Columns; @@ -378,8 +378,8 @@ export type ControlledTableProps = PropsWithDefaults & IState & { setState: SetState; columns: VisibleColumns; + currentTooltip: IUSerInterfaceTooltip; paginator: IPaginator; - tooltip: IUSerInterfaceTooltip; viewport: IDerivedData; viewport_selected_rows: Indices; virtual: IDerivedData; @@ -393,7 +393,8 @@ export interface ICellFactoryProps { dropdown: IStaticDropdowns; dropdown_conditional: ConditionalDropdowns; dropdown_data: IDataDropdowns; - column_static_tooltip: ITableStaticTooltips; + tooltip: ITableStaticTooltips; + currentTooltip: IUSerInterfaceTooltip; data: Data; editable: boolean; id: string; @@ -418,8 +419,7 @@ export interface ICellFactoryProps { style_filter_conditional: BasicFilters; style_header_conditional: Headers; style_table: Table; - tooltip: IUSerInterfaceTooltip; - tooltips?: ITableTooltips; + tooltip_data?: ITableTooltips; uiCell?: IUserInterfaceCell; uiViewport?: IUserInterfaceViewport; viewport: IDerivedData; diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index 2263f974b..f94f69634 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -63,8 +63,8 @@ export const defaultProps = { dropdown_conditional: [], dropdown_data: {}, - column_static_tooltip: {}, - column_conditional_tooltips: [], + tooltip: {}, + tooltip_conditional: [], tooltip_delay: 350, tooltip_duration: 2000, @@ -633,7 +633,7 @@ export const propTypes = { ), /** - * `column_static_tooltip` represents the tooltip shown + * `tooltip` represents the tooltip shown * for different columns. * The `property` name refers to the column ID. * The `type` refers to the type of tooltip syntax used @@ -654,7 +654,7 @@ export const propTypes = { * a plain string. The `text` syntax will be used in * that case. */ - column_static_tooltip: PropTypes.objectOf( + tooltip: PropTypes.objectOf( PropTypes.oneOfType([ PropTypes.shape({ delay: PropTypes.number, @@ -670,7 +670,7 @@ export const propTypes = { ), /** - * `column_conditional_tooltips` represents the tooltip shown + * `tooltip_conditional` represents the tooltip shown * for different columns and cells. * * This property allows you to specify different tooltips for @@ -708,7 +708,7 @@ export const propTypes = { * This overrides the table's `tooltip_duration` property. * If set to `null`, the tooltip will not disappear. */ - column_conditional_tooltips: PropTypes.arrayOf(PropTypes.shape({ + tooltip_conditional: PropTypes.arrayOf(PropTypes.shape({ if: PropTypes.shape({ filter: PropTypes.string, row_index: PropTypes.oneOfType([ @@ -730,7 +730,7 @@ export const propTypes = { })), /** - * `tooltips` represents the tooltip shown + * `tooltip_data` represents the tooltip shown * for different columns and cells. * The `property` name refers to the column ID. Each property * contains a list of tooltips mapped to the source `data` @@ -754,7 +754,7 @@ export const propTypes = { * a plain string. The `text` syntax will be used in * that case. */ - tooltips: PropTypes.objectOf(PropTypes.arrayOf( + tooltip_data: PropTypes.objectOf(PropTypes.arrayOf( PropTypes.oneOfType([ PropTypes.string, PropTypes.shape({ diff --git a/src/dash-table/derived/table/tooltip.ts b/src/dash-table/derived/table/tooltip.ts index 9d5a3b11d..9e42f7288 100644 --- a/src/dash-table/derived/table/tooltip.ts +++ b/src/dash-table/derived/table/tooltip.ts @@ -9,31 +9,31 @@ import { memoizeOne } from 'core/memoizer'; export const MAX_32BITS = 2147483647; function getSelectedTooltip( - tooltip: IUSerInterfaceTooltip, - tooltips: ITableTooltips | undefined, - column_conditional_tooltips: ConditionalTooltip[], - column_static_tooltip: ITableStaticTooltips, + currentTooltip: IUSerInterfaceTooltip, + tooltip_data: ITableTooltips | undefined, + tooltip_conditional: ConditionalTooltip[], + tooltip_static: ITableStaticTooltips, virtualized: IVirtualizedDerivedData ) { - if (!tooltip) { + if (!currentTooltip) { return undefined; } - const { id, row } = tooltip; + const { id, row } = currentTooltip; if (id === undefined || row === undefined) { return undefined; } - const legacyTooltip = tooltips && - tooltips[id] && + const legacyTooltip = tooltip_data && + tooltip_data[id] && ( - tooltips[id].length > row ? - tooltips[id][row] : + tooltip_data[id].length > row ? + tooltip_data[id][row] : null ); - const staticTooltip = column_static_tooltip[id]; + const staticTooltip = tooltip_static[id]; const conditionalTooltips = R.filter(tt => { return !tt.if || @@ -42,7 +42,7 @@ function getSelectedTooltip( ifRowIndex(tt.if, row) && ifFilter(tt.if, virtualized.data[row - virtualized.offset.rows]) ); - }, column_conditional_tooltips); + }, tooltip_conditional); return conditionalTooltips.length ? conditionalTooltips.slice(-1)[0] : @@ -74,19 +74,19 @@ function getDuration(duration: number | null | undefined, defaultTo: number) { } export default memoizeOne(( - tooltip: IUSerInterfaceTooltip, - tooltips: ITableTooltips | undefined, - column_conditional_tooltips: ConditionalTooltip[], - column_static_tooltip: ITableStaticTooltips, + currentTooltip: IUSerInterfaceTooltip, + tooltip_data: ITableTooltips | undefined, + tooltip_conditional: ConditionalTooltip[], + tooltip_static: ITableStaticTooltips, virtualized: IVirtualizedDerivedData, defaultDelay: number | null, defaultDuration: number | null ) => { const selectedTooltip = getSelectedTooltip( - tooltip, - tooltips, - column_conditional_tooltips, - column_static_tooltip, + currentTooltip, + tooltip_data, + tooltip_conditional, + tooltip_static, virtualized ); diff --git a/src/dash-table/handlers/cellEvents.ts b/src/dash-table/handlers/cellEvents.ts index c61f4a03b..a50092bf3 100644 --- a/src/dash-table/handlers/cellEvents.ts +++ b/src/dash-table/handlers/cellEvents.ts @@ -155,7 +155,7 @@ export const handleEnter = (propsFn: () => ICellFactoryProps, idx: number, i: nu const realIdx = virtualized.indices[idx]; setState({ - tooltip: { + currentTooltip: { id: c.id, row: realIdx } @@ -167,26 +167,26 @@ export const handleLeave = (propsFn: () => ICellFactoryProps, _idx: number, _i: setState } = propsFn(); - setState({ tooltip: undefined }); + setState({ currentTooltip: undefined }); }; export const handleMove = (propsFn: () => ICellFactoryProps, idx: number, i: number) => { const { columns, + currentTooltip, virtualized, - setState, - tooltip + setState } = propsFn(); const c = columns[i]; const realIdx = virtualized.indices[idx]; - if (tooltip && tooltip.id === c.id && tooltip.row === realIdx) { + if (currentTooltip && currentTooltip.id === c.id && currentTooltip.row === realIdx) { return; } setState({ - tooltip: { + currentTooltip: { id: c.id, row: realIdx } From abc552b678d9c285421f5d07eddddc3c8da922fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 30 May 2019 08:47:06 -0400 Subject: [PATCH 17/46] tighten tooltip props proptypes --- src/dash-table/dash/DataTable.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index f94f69634..94df37069 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -656,7 +656,7 @@ export const propTypes = { */ tooltip: PropTypes.objectOf( PropTypes.oneOfType([ - PropTypes.shape({ + PropTypes.exact({ delay: PropTypes.number, duration: PropTypes.number, type: PropTypes.oneOf([ @@ -708,8 +708,11 @@ export const propTypes = { * This overrides the table's `tooltip_duration` property. * If set to `null`, the tooltip will not disappear. */ - tooltip_conditional: PropTypes.arrayOf(PropTypes.shape({ - if: PropTypes.shape({ + tooltip_conditional: PropTypes.arrayOf(PropTypes.exact({ + delay: PropTypes.number, + duration: PropTypes.number, + if: PropTypes.exact({ + column_id: PropTypes.string, filter: PropTypes.string, row_index: PropTypes.oneOfType([ PropTypes.number, @@ -717,11 +720,8 @@ export const propTypes = { 'odd', 'even' ]) - ]), - column_id: PropTypes.string + ]) }).isRequired, - delay: PropTypes.number, - duration: PropTypes.number, type: PropTypes.oneOf([ 'text', 'markdown' @@ -757,7 +757,7 @@ export const propTypes = { tooltip_data: PropTypes.objectOf(PropTypes.arrayOf( PropTypes.oneOfType([ PropTypes.string, - PropTypes.shape({ + PropTypes.exact({ delay: PropTypes.number, duration: PropTypes.number, type: PropTypes.oneOf([ From c3b2e40288b2f601de8421c767081057d960760a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 30 May 2019 09:50:40 -0400 Subject: [PATCH 18/46] remove unused filter_type(s) --- src/dash-table/components/FilterFactory.tsx | 114 +++++++++--------- src/dash-table/components/Table/props.ts | 9 -- src/dash-table/dash/DataTable.js | 25 ---- .../percy-storybook/Width.empty.percy.tsx | 2 - 4 files changed, 54 insertions(+), 96 deletions(-) diff --git a/src/dash-table/components/FilterFactory.tsx b/src/dash-table/components/FilterFactory.tsx index 4c774f4f8..0390f7a39 100644 --- a/src/dash-table/components/FilterFactory.tsx +++ b/src/dash-table/components/FilterFactory.tsx @@ -7,7 +7,7 @@ import memoizerCache from 'core/cache/memoizer'; import { memoizeOne } from 'core/memoizer'; import ColumnFilter from 'dash-table/components/Filter/Column'; -import { ColumnId, Filtering, FilteringType, IVisibleColumn, VisibleColumns, RowSelection } from 'dash-table/components/Table/props'; +import { ColumnId, Filtering, IVisibleColumn, VisibleColumns, RowSelection } from 'dash-table/components/Table/props'; import derivedFilterStyles, { derivedFilterOpStyles } from 'dash-table/derived/filter/wrapperStyles'; import derivedHeaderOperations from 'dash-table/derived/header/operations'; import { derivedRelevantFilterStyles } from 'dash-table/derived/style'; @@ -27,7 +27,6 @@ export interface IFilterOptions { columns: VisibleColumns; filter: string; filtering: Filtering; - filtering_type: FilteringType; id: string; map: Map; rawFilterQuery: string; @@ -108,7 +107,6 @@ export default class FilterFactory { const { columns, filtering, - filtering_type, map, row_deletable, row_selectable, @@ -123,63 +121,59 @@ export default class FilterFactory { return []; } - if (filtering_type === FilteringType.Basic) { - const relevantStyles = this.relevantStyles( - style_cell, - style_filter, - style_cell_conditional, - style_filter_conditional - ); - - const wrapperStyles = this.wrapperStyles( - this.filterStyles(columns, relevantStyles), - filterEdges - ); - - const opStyles = this.filterOpStyles( - 1, - (row_selectable ? 1 : 0) + (row_deletable ? 1 : 0), - relevantStyles - )[0]; - - const filters = R.addIndex(R.map)((column, index) => { - return this.filter.get(column.id, index)( - column, - index, - map, - setFilter - ); - }, columns); - - const styledFilters = arrayMap2( - filters, - wrapperStyles, - (f, s) => React.cloneElement(f, { - style: s - }) - ); - - const operations = this.headerOperations( - 1, - row_selectable, - row_deletable - )[0]; - - const operators = arrayMap2( - operations, - opStyles, - (o, s, j) => React.cloneElement(o, { - style: R.mergeAll([ - filterOpEdges && filterOpEdges.getStyle(0, j), - s, - o.props.style - ]) - }) + const relevantStyles = this.relevantStyles( + style_cell, + style_filter, + style_cell_conditional, + style_filter_conditional + ); + + const wrapperStyles = this.wrapperStyles( + this.filterStyles(columns, relevantStyles), + filterEdges + ); + + const opStyles = this.filterOpStyles( + 1, + (row_selectable ? 1 : 0) + (row_deletable ? 1 : 0), + relevantStyles + )[0]; + + const filters = R.addIndex(R.map)((column, index) => { + return this.filter.get(column.id, index)( + column, + index, + map, + setFilter ); - - return [operators.concat(styledFilters)]; - } else { - return [[]]; - } + }, columns); + + const styledFilters = arrayMap2( + filters, + wrapperStyles, + (f, s) => React.cloneElement(f, { + style: s + }) + ); + + const operations = this.headerOperations( + 1, + row_selectable, + row_deletable + )[0]; + + const operators = arrayMap2( + operations, + opStyles, + (o, s, j) => React.cloneElement(o, { + style: R.mergeAll([ + filterOpEdges && filterOpEdges.getStyle(0, j), + s, + o.props.style + ]) + }) + ); + + return [operators.concat(styledFilters)]; } } \ No newline at end of file diff --git a/src/dash-table/components/Table/props.ts b/src/dash-table/components/Table/props.ts index 87b1e17bd..79d11cdaa 100644 --- a/src/dash-table/components/Table/props.ts +++ b/src/dash-table/components/Table/props.ts @@ -22,11 +22,6 @@ export enum ColumnType { Datetime = 'datetime' } -export enum FilteringType { - Advanced = 'advanced', - Basic = 'basic' -} - export interface IDerivedData { data: Data; indices: Indices; @@ -277,8 +272,6 @@ export interface IProps { editable?: boolean; filter?: string; filtering?: Filtering; - filtering_type?: FilteringType; - filtering_types?: FilteringType[]; locale_format: INumberLocale; merge_duplicate_headers?: boolean; n_fixed_columns?: number; @@ -321,8 +314,6 @@ interface IDefaultProps { editable: boolean; filter: string; filtering: Filtering; - filtering_type: FilteringType; - filtering_types: FilteringType[]; merge_duplicate_headers: boolean; n_fixed_columns: number; n_fixed_rows: number; diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index 94df37069..148ce26d8 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -41,8 +41,6 @@ export const defaultProps = { css: [], filter: '', filtering: false, - filtering_type: 'basic', - filtering_types: ['basic'], sorting: false, sorting_type: 'single', sort_by: [], @@ -814,29 +812,6 @@ export const propTypes = { */ filtering: PropTypes.oneOf(['fe', 'be', true, false]), - /** - * UNSTABLE - * In the future, there may be several modes of the - * filtering UI like `basic`, `advanced`, etc. - * Currently, we only `basic`. - * NOTE - This will likely change in the future, - * subscribe to changes here: - * [https://github.com/plotly/dash-table/issues/169](https://github.com/plotly/dash-table/issues/169) - */ - filtering_type: PropTypes.oneOf(['basic']), - - /** - * UNSTABLE - * In the future, there may be several modes of the - * filtering UI like `basic`, `advanced`, etc - * NOTE - This will likely change in the future, - * subscribe to changes here: - * [https://github.com/plotly/dash-table/issues/169](https://github.com/plotly/dash-table/issues/169) - */ - filtering_types: PropTypes.arrayOf(PropTypes.oneOf([ - 'basic' - ])), - /** * The `sorting` property enables data to be * sorted on a per-column basis. diff --git a/tests/visual/percy-storybook/Width.empty.percy.tsx b/tests/visual/percy-storybook/Width.empty.percy.tsx index 2b59a5fc5..a4685f626 100644 --- a/tests/visual/percy-storybook/Width.empty.percy.tsx +++ b/tests/visual/percy-storybook/Width.empty.percy.tsx @@ -4,7 +4,6 @@ import { storiesOf } from '@storybook/react'; import random from 'core/math/random'; import DataTable from 'dash-table/dash/DataTable'; -import { FilteringType } from 'dash-table/components/Table/props'; const setProps = () => { }; @@ -27,7 +26,6 @@ const baseProps = { content_style: 'fit', data, filtering: 'fe', - filtering_type: FilteringType.Basic, style_cell: { width: 100, max_width: 100, min_width: 100 } }; From bdd61eb6314ffb5128f98de9dce6b127d2dbf63c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 30 May 2019 12:16:27 -0400 Subject: [PATCH 19/46] - fixed_rows, fixed_columns rename + improved typing --- demo/AppMode.ts | 12 ++--- .../components/ControlledTable/index.tsx | 26 +++++------ src/dash-table/components/EdgeFactory.tsx | 18 ++++---- src/dash-table/components/Table/index.tsx | 14 +++--- src/dash-table/components/Table/props.ts | 25 +++++++---- src/dash-table/dash/DataTable.js | 45 +++++++++++++++---- src/dash-table/dash/sanitize.ts | 29 ++++++++++-- src/dash-table/derived/header/headerRows.ts | 10 +++-- tests/cypress/dash/v_be_page.py | 4 +- tests/cypress/dash/v_fe_page.py | 4 +- tests/dash/app_dataframe_updating_graph_fe.py | 2 +- .../app_editor_with_configurable_options.py | 12 ++--- .../percy-storybook/Border.defaults.percy.tsx | 32 ++++++------- .../percy-storybook/Border.style.percy.tsx | 8 ++-- .../percy-storybook/DashTable.percy.tsx | 36 +++++++-------- .../percy-storybook/Virtualization.percy.tsx | 2 +- .../percy-storybook/Width.all.percy.tsx | 8 ++-- .../percy-storybook/Width.defaults.percy.tsx | 8 ++-- .../percy-storybook/Width.max.percy.tsx | 8 ++-- .../percy-storybook/Width.min.percy.tsx | 8 ++-- .../Width.percentages.percy.tsx | 8 ++-- .../percy-storybook/Width.width.percy.tsx | 8 ++-- tests/visual/percy-storybook/fixtures.ts | 2 +- 23 files changed, 195 insertions(+), 134 deletions(-) diff --git a/demo/AppMode.ts b/demo/AppMode.ts index 78722b9b1..4bae7fd08 100644 --- a/demo/AppMode.ts +++ b/demo/AppMode.ts @@ -88,8 +88,8 @@ function getDefaultState( data: mock.data, editable: true, sorting: true, - n_fixed_rows: 3, - n_fixed_columns: 2, + fixed_rows: { headers: true }, + fixed_columns: { headers: true }, merge_duplicate_headers: false, row_deletable: true, row_selectable: 'single', @@ -121,8 +121,8 @@ function getSpaceInColumn() { function getFixedTooltipsState() { const state = getTooltipsState(); - state.tableProps.n_fixed_columns = 3; - state.tableProps.n_fixed_rows = 4; + state.tableProps.fixed_columns = { headers: true, data: 1 }; + state.tableProps.fixed_rows = { headers: true, data: 1 }; return state; } @@ -250,8 +250,8 @@ function getFixedVirtualizedState() { data: mock.data, editable: true, sorting: true, - n_fixed_rows: 3, - n_fixed_columns: 2, + fixed_rows: { headers: true }, + fixed_columns: { headers: true }, merge_duplicate_headers: false, row_deletable: true, row_selectable: 'single', diff --git a/src/dash-table/components/ControlledTable/index.tsx b/src/dash-table/components/ControlledTable/index.tsx index 70f18b4e5..f4deb71bb 100644 --- a/src/dash-table/components/ControlledTable/index.tsx +++ b/src/dash-table/components/ControlledTable/index.tsx @@ -602,8 +602,8 @@ export default class ControlledTable extends PureComponent applyStyle = () => { const { - n_fixed_columns, - n_fixed_rows, + fixed_columns, + fixed_rows, row_deletable, row_selectable } = this.props; @@ -635,7 +635,7 @@ export default class ControlledTable extends PureComponent } // Adjust the width of the fixed row header - if (n_fixed_rows) { + if (fixed_rows) { Array.from(r1c1.querySelectorAll('tr:first-of-type td, tr:first-of-type th')).forEach((td, index) => { const style = getComputedStyle(td); const width = style.width; @@ -648,7 +648,7 @@ export default class ControlledTable extends PureComponent } // Adjust the width of the fixed row / fixed columns header - if (n_fixed_columns && n_fixed_rows) { + if (fixed_columns && fixed_rows) { Array.from(r1c0.querySelectorAll('tr:first-of-type td, tr:first-of-type th')).forEach((td, index) => { const style = getComputedStyle(td); const width = style.width; @@ -687,8 +687,8 @@ export default class ControlledTable extends PureComponent content_style, currentTooltip, filtering, - n_fixed_columns, - n_fixed_rows, + fixed_columns, + fixed_rows, scrollbarWidth, style_as_list_view, style_table, @@ -705,19 +705,19 @@ export default class ControlledTable extends PureComponent const fragmentClasses = [ [ - n_fixed_rows && n_fixed_columns ? 'dash-fixed-row dash-fixed-column' : '', - n_fixed_rows ? 'dash-fixed-row' : '' + fixed_rows && fixed_columns ? 'dash-fixed-row dash-fixed-column' : '', + fixed_rows ? 'dash-fixed-row' : '' ], [ - n_fixed_columns ? 'dash-fixed-column' : '', + fixed_columns ? 'dash-fixed-column' : '', 'dash-fixed-content' ] ]; const rawTable = this.tableFn(); const { grid, empty } = derivedTableFragments( - n_fixed_columns, - n_fixed_rows, + fixed_columns, + fixed_rows, rawTable, virtualized.offset.rows ); @@ -725,8 +725,8 @@ export default class ControlledTable extends PureComponent const classes = [ 'dash-spreadsheet', ...(virtualization ? ['dash-virtualized'] : []), - ...(n_fixed_rows ? ['dash-freeze-top'] : []), - ...(n_fixed_columns ? ['dash-freeze-left'] : []), + ...(fixed_rows ? ['dash-freeze-top'] : []), + ...(fixed_columns ? ['dash-freeze-left'] : []), ...(style_as_list_view ? ['dash-list-view'] : []), ...(empty[0][1] ? ['dash-empty-01'] : []), ...(empty[1][1] ? ['dash-empty-11'] : []), diff --git a/src/dash-table/components/EdgeFactory.tsx b/src/dash-table/components/EdgeFactory.tsx index 3918e129f..1624073b6 100644 --- a/src/dash-table/components/EdgeFactory.tsx +++ b/src/dash-table/components/EdgeFactory.tsx @@ -142,8 +142,8 @@ export default class EdgeFactory { columns, filtering, workFilter, - n_fixed_columns, - n_fixed_rows, + fixed_columns, + fixed_rows, row_deletable, row_selectable, style_as_list_view, @@ -164,8 +164,8 @@ export default class EdgeFactory { (row_deletable ? 1 : 0) + (row_selectable ? 1 : 0), !!filtering, workFilter.map, - n_fixed_columns, - n_fixed_rows, + fixed_columns, + fixed_rows, style_as_list_view, style_cell, style_cell_conditional, @@ -186,8 +186,8 @@ export default class EdgeFactory { operations: number, filtering: boolean, filterMap: Map, - _n_fixed_columns: number, - n_fixed_rows: number, + _fixed_columns: number, + fixed_rows: number, style_as_list_view: boolean, style_cell: Style, style_cell_conditional: Cells, @@ -287,7 +287,7 @@ export default class EdgeFactory { this.vReconcile(filterOpEdges, filterEdges, cutoffWeight); this.vReconcile(dataOpEdges, dataEdges, cutoffWeight); - if (n_fixed_rows === headerRows) { + if (fixed_rows === headerRows) { if (filtering) { this.hOverride(headerEdges, filterEdges, cutoffWeight); this.hOverride(headerOpEdges, filterOpEdges, cutoffWeight); @@ -295,12 +295,12 @@ export default class EdgeFactory { this.hOverride(headerEdges, dataEdges, cutoffWeight); this.hOverride(headerOpEdges, dataOpEdges, cutoffWeight); } - } else if (filtering && n_fixed_rows === headerRows + 1) { + } else if (filtering && fixed_rows === headerRows + 1) { this.hOverride(filterEdges, dataEdges, cutoffWeight); this.hOverride(filterOpEdges, dataOpEdges, cutoffWeight); } - if (_n_fixed_columns === operations) { + if (_fixed_columns === operations) { this.vOverride(headerOpEdges, headerEdges, cutoffWeight); this.vOverride(filterOpEdges, filterEdges, cutoffWeight); this.vOverride(dataOpEdges, dataEdges, cutoffWeight); diff --git a/src/dash-table/components/Table/index.tsx b/src/dash-table/components/Table/index.tsx index aa105baf3..6ac30638f 100644 --- a/src/dash-table/components/Table/index.tsx +++ b/src/dash-table/components/Table/index.tsx @@ -20,11 +20,11 @@ import QuerySyntaxTree from 'dash-table/syntax-tree/QuerySyntaxTree'; import { ControlledTableProps, - PropsWithDefaultsAndDerived, SetProps, IState, StandaloneState, - PropsWithDefaults + PropsWithDefaults, + SanitizedAndDerivedProps } from './props'; import 'react-select/dist/react-select.css'; @@ -36,8 +36,8 @@ import derivedFilterMap from 'dash-table/derived/filter/map'; const DERIVED_REGEX = /^derived_/; -export default class Table extends Component { - constructor(props: PropsWithDefaultsAndDerived) { +export default class Table extends Component { + constructor(props: SanitizedAndDerivedProps) { super(props); this.state = { @@ -55,7 +55,7 @@ export default class Table extends Component = {}; + let newProps: Partial = {}; if (!derivedStructureCache.cached) { newProps.derived_filter_structure = derivedStructureCache.result; @@ -285,7 +285,7 @@ export default class Table extends Component) => { + } : (newProps: Partial) => { /*#if DEV*/ const props: any = this.state; R.forEach( diff --git a/src/dash-table/components/Table/props.ts b/src/dash-table/components/Table/props.ts index 79d11cdaa..518421559 100644 --- a/src/dash-table/components/Table/props.ts +++ b/src/dash-table/components/Table/props.ts @@ -157,6 +157,7 @@ export interface IBaseVisibleColumn { name: string | string[]; } +export type Fixed = { headers: false, data?: 0 } | { headers: true, data?: number }; export type IColumnType = INumberColumn | ITextColumn | IDatetimeColumn | IAnyColumn; export type IVisibleColumn = IBaseVisibleColumn & IColumnType; @@ -244,7 +245,7 @@ export interface IState { }; } -export type StandaloneState = IState & Partial; +export type StandaloneState = IState & Partial; export interface IProps { data_previous?: any[]; @@ -274,8 +275,8 @@ export interface IProps { filtering?: Filtering; locale_format: INumberLocale; merge_duplicate_headers?: boolean; - n_fixed_columns?: number; - n_fixed_rows?: number; + fixed_columns?: Fixed; + fixed_rows?: Fixed; row_deletable?: boolean; row_selectable?: RowSelection; selected_cells?: SelectedCells; @@ -315,8 +316,8 @@ interface IDefaultProps { filter: string; filtering: Filtering; merge_duplicate_headers: boolean; - n_fixed_columns: number; - n_fixed_rows: number; + fixed_columns: Fixed; + fixed_rows: Fixed; row_deletable: boolean; row_selectable: RowSelection; selected_cells: SelectedCells; @@ -362,9 +363,15 @@ interface IDerivedProps { } export type PropsWithDefaults = IProps & IDefaultProps; -export type PropsWithDefaultsAndDerived = PropsWithDefaults & IDerivedProps; -export type ControlledTableProps = PropsWithDefaults & IState & { +export type SanitizedProps = PropsWithDefaults & { + fixed_columns: number; + fixed_rows: number; +}; + +export type SanitizedAndDerivedProps = SanitizedProps & IDerivedProps; + +export type ControlledTableProps = SanitizedProps & IState & { setProps: SetProps; setState: SetState; @@ -390,8 +397,8 @@ export interface ICellFactoryProps { editable: boolean; id: string; is_focused?: boolean; - n_fixed_columns: number; - n_fixed_rows: number; + fixed_columns: number; + fixed_rows: number; paginator: IPaginator; row_deletable: boolean; row_selectable: RowSelection; diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index 148ce26d8..8acb460e8 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -61,6 +61,15 @@ export const defaultProps = { dropdown_conditional: [], dropdown_data: {}, + fixed_columns: { + headers: false, + data: 0 + }, + fixed_rows: { + headers: false, + data: 0 + }, + tooltip: {}, tooltip_conditional: [], tooltip_delay: 350, @@ -423,10 +432,10 @@ export const propTypes = { merge_duplicate_headers: PropTypes.bool, /** - * `n_fixed_columns` will "fix" the set of columns so that + * `fixed_columns` will "fix" the set of columns so that * they remain visible when scrolling horizontally across - * the unfixed columns. `n_fixed_columns` fixes columns - * from left-to-right, so `n_fixed_columns=3` will fix + * the unfixed columns. `fixed_columns` fixes columns + * from left-to-right, so `fixed_columns=3` will fix * the first 3 columns. * * Note that fixing columns introduces some changes to the @@ -434,15 +443,24 @@ export const propTypes = { * way that your columns are rendered or sized. * View the documentation examples to learn more. */ - n_fixed_columns: PropTypes.number, + fixed_columns: PropTypes.oneOfType([ + PropTypes.exact({ + headers: PropTypes.oneOf([false]), + data: PropTypes.oneOf([0]) + }), + PropTypes.exact({ + headers: PropTypes.oneOf([true]), + data: PropTypes.number + }) + ]), /** - * `n_fixed_rows` will "fix" the set of rows so that + * `fixed_rows` will "fix" the set of rows so that * they remain visible when scrolling vertically down - * the table. `n_fixed_rows` fixes rows + * the table. `fixed_rows` fixes rows * from top-to-bottom, starting from the headers, - * so `n_fixed_rows=1` will fix the header row, - * `n_fixed_rows=2` will fix the header row and the first row, + * so `fixed_rows=1` will fix the header row, + * `fixed_rows=2` will fix the header row and the first row, * or the first two header rows (if there are multiple headers). * * Note that fixing rows introduces some changes to the @@ -450,7 +468,16 @@ export const propTypes = { * way that your columns are rendered or sized. * View the documentation examples to learn more. */ - n_fixed_rows: PropTypes.number, + fixed_rows: PropTypes.oneOfType([ + PropTypes.exact({ + headers: PropTypes.oneOf([false]), + data: PropTypes.oneOf([0]) + }), + PropTypes.exact({ + headers: PropTypes.oneOf([true]), + data: PropTypes.number + }) + ]), /** * If True, then a `x` will appear next to each `row` diff --git a/src/dash-table/dash/sanitize.ts b/src/dash-table/dash/sanitize.ts index 78f2f2d98..ff654ae5b 100644 --- a/src/dash-table/dash/sanitize.ts +++ b/src/dash-table/dash/sanitize.ts @@ -2,7 +2,8 @@ import * as R from 'ramda'; import { memoizeOne } from 'core/memoizer'; -import { Columns, ColumnType, INumberLocale } from 'dash-table/components/Table/props'; +import { Columns, ColumnType, INumberLocale, IColumn, PropsWithDefaults, Filtering, RowSelection, SanitizedProps, Fixed } from 'dash-table/components/Table/props'; +import headerRows from 'dash-table/derived/header/headerRows'; const D3_DEFAULT_LOCALE: INumberLocale = { symbol: ['$', ''], @@ -31,16 +32,38 @@ const applyDefaultsToColumns = memoizeOne( }, columns) ); -export default (props: any) => { +const getFixedColumns = ( + fixed: Fixed, + row_deletable: boolean, + row_selectable: RowSelection +) => (fixed.headers === false && 0) || ( + (row_deletable ? 1 : 0) + + (row_selectable ? 1 : 0) + + (typeof fixed.data === 'number' ? fixed.data : 0) +); + +const getFixedRows = ( + fixed: Fixed, + columns: IColumn[], + filter: Filtering +) => (fixed.headers === false && 0) || ( + headerRows(columns) + + (filter ? 1 : 0) + + (typeof fixed.data === 'number' ? fixed.data : 0) +); + +export default (props: PropsWithDefaults): SanitizedProps => { const locale_format = applyDefaultToLocale(props.locale_format); return R.mergeAll([ props, { columns: applyDefaultsToColumns(locale_format, props.columns), + fixed_columns: getFixedColumns(props.fixed_columns, props.row_deletable, props.row_selectable), + fixed_rows: getFixedRows(props.fixed_rows, props.columns, props.filtering), locale_format } - ]); + ]) as any; }; export const getLocale = (...locales: Partial[]): INumberLocale => diff --git a/src/dash-table/derived/header/headerRows.ts b/src/dash-table/derived/header/headerRows.ts index 50c7e36c1..45e881085 100644 --- a/src/dash-table/derived/header/headerRows.ts +++ b/src/dash-table/derived/header/headerRows.ts @@ -1,7 +1,11 @@ -import { VisibleColumns } from 'dash-table/components/Table/props'; +import { IColumn } from 'dash-table/components/Table/props'; -const getColLength = (c: any) => (Array.isArray(c.name) ? c.name.length : 1); +const getColLength = (c: IColumn) => c.hidden ? + 0 : + Array.isArray(c.name) ? + c.name.length : + 1; export default ( - columns: VisibleColumns + columns: IColumn[] ): number => Math.max(...columns.map(getColLength)); \ No newline at end of file diff --git a/tests/cypress/dash/v_be_page.py b/tests/cypress/dash/v_be_page.py index ffb1af28a..fdb59f598 100644 --- a/tests/cypress/dash/v_be_page.py +++ b/tests/cypress/dash/v_be_page.py @@ -49,8 +49,8 @@ {"id": 12, "name": "Timely response?"}, {"id": 13, "name": "Consumer disputed?"}, ], - n_fixed_columns=2, - n_fixed_rows=1, + fixed_columns={ 'headers': True, 'data': -1 }, + fixed_rows={ 'headers': True, 'data': -1 }, row_selectable=True, row_deletable=True, sorting="be", diff --git a/tests/cypress/dash/v_fe_page.py b/tests/cypress/dash/v_fe_page.py index ce5fdd0c4..324f7daa5 100644 --- a/tests/cypress/dash/v_fe_page.py +++ b/tests/cypress/dash/v_fe_page.py @@ -57,8 +57,8 @@ {"id": 12, "name": "Timely response?"}, {"id": 13, "name": "Consumer disputed?"}, ], - n_fixed_columns=2, - n_fixed_rows=1, + fixed_columns={ 'headers': True }, + fixed_rows={ 'headers': True }, row_selectable=True, row_deletable=True, sorting="fe", diff --git a/tests/dash/app_dataframe_updating_graph_fe.py b/tests/dash/app_dataframe_updating_graph_fe.py index beb8de26c..5877e8549 100644 --- a/tests/dash/app_dataframe_updating_graph_fe.py +++ b/tests/dash/app_dataframe_updating_graph_fe.py @@ -30,7 +30,7 @@ def layout(): row_selectable="multi", row_deletable=True, selected_rows=[], - n_fixed_rows=1, + fixed_rows={ 'headers': True }, ), style={"height": 300, "overflowY": "scroll"}, ), diff --git a/tests/dash/app_editor_with_configurable_options.py b/tests/dash/app_editor_with_configurable_options.py index a3e0faede..915d22829 100644 --- a/tests/dash/app_editor_with_configurable_options.py +++ b/tests/dash/app_editor_with_configurable_options.py @@ -58,9 +58,9 @@ value="fe", ), html.Label("Number of Fixed Rows"), - html.Div(dcc.Input(id="n_fixed_rows", type="number", value="0")), + html.Div(dcc.Input(id="fixed_rows", type="number", value="0")), html.Label("Number of Fixed Columns"), - html.Div(dcc.Input(id="n_fixed_columns", type="number", value="0")), + html.Div(dcc.Input(id="fixed_columns", type="number", value="0")), ] @@ -101,8 +101,8 @@ def layout(): "sorting_treat_empty_string_as_none", # 3 "row_selectable", # 4 "pagination_mode", # 5 - "n_fixed_rows", # 6 - "n_fixed_columns", # 7 + "fixed_rows", # 6 + "fixed_columns", # 7 "dataset", ] ], @@ -122,8 +122,8 @@ def update_table(*args): sorting_treat_empty_string_as_none=args[3], row_selectable=args[4], pagination_mode=args[5], - n_fixed_rows=args[6], - n_fixed_columns=args[7], + fixed_rows=args[6], + fixed_columns=args[7], ) diff --git a/tests/visual/percy-storybook/Border.defaults.percy.tsx b/tests/visual/percy-storybook/Border.defaults.percy.tsx index 55fdf3c4f..3652cf5d1 100644 --- a/tests/visual/percy-storybook/Border.defaults.percy.tsx +++ b/tests/visual/percy-storybook/Border.defaults.percy.tsx @@ -72,16 +72,16 @@ storiesOf('DashTable/Border (available space not filled)', module) />)) .add('with frozen rows and no frozen columns', () => ()) .add('with no frozen rows and frozen columns', () => ()) .add('with frozen rows and frozen columns', () => ()); storiesOf('DashTable/Border (available space filled)', module) @@ -90,16 +90,16 @@ storiesOf('DashTable/Border (available space filled)', module) />)) .add('with frozen rows and no frozen columns', () => ()) .add('with no frozen rows and frozen columns', () => ()) .add('with frozen rows and frozen columns', () => ()); let props3 = Object.assign({}, BORDER_PROPS_DEFAULTS, { @@ -116,16 +116,16 @@ storiesOf('DashTable/ListView style, Border (available space not filled)', modul />)) .add('with frozen rows and no frozen columns', () => ()) .add('with no frozen rows and frozen columns', () => ()) .add('with frozen rows and frozen columns', () => ()); storiesOf('DashTable/ListView style, Border (available space filled)', module) @@ -134,14 +134,14 @@ storiesOf('DashTable/ListView style, Border (available space filled)', module) />)) .add('with frozen rows and no frozen columns', () => ()) .add('with no frozen rows and frozen columns', () => ()) .add('with frozen rows and frozen columns', () => ()); \ No newline at end of file diff --git a/tests/visual/percy-storybook/Border.style.percy.tsx b/tests/visual/percy-storybook/Border.style.percy.tsx index 50e3c3e6a..4378bec14 100644 --- a/tests/visual/percy-storybook/Border.style.percy.tsx +++ b/tests/visual/percy-storybook/Border.style.percy.tsx @@ -6,10 +6,10 @@ import { BORDER_PROPS_DEFAULTS } from './Border.defaults.percy'; const OPS_VARIANTS: ITest[] = [ { name: 'with ops', props: { row_deletable: true, row_selectable: 'single' } }, - { name: 'fixed columns', props: { n_fixed_columns: 2, row_deletable: true, row_selectable: 'single' } }, - { name: 'fixed rows', props: { n_fixed_rows: 1, row_deletable: true, row_selectable: 'single' } }, - { name: 'fixed columns & rows', props: { n_fixed_columns: 2, n_fixed_rows: 1, row_deletable: true, row_selectable: 'single' } }, - { name: 'fixed columns & rows inside fragments', props: { n_fixed_columns: 3, n_fixed_rows: 2, row_deletable: true, row_selectable: 'single' } } + { name: 'fixed columns', props: { fixed_columns: { headers: true }, row_deletable: true, row_selectable: 'single' } }, + { name: 'fixed rows', props: { fixed_rows: { headers: true }, row_deletable: true, row_selectable: 'single' } }, + { name: 'fixed columns & rows', props: { fixed_columns: { headers: true }, fixed_rows: { headers: true }, row_deletable: true, row_selectable: 'single' } }, + { name: 'fixed columns & rows inside fragments', props: { fixed_columns: { headers: true, data: 1 }, fixed_rows: { headers: true , data: 1}, row_deletable: true, row_selectable: 'single' } } ]; interface ITest { diff --git a/tests/visual/percy-storybook/DashTable.percy.tsx b/tests/visual/percy-storybook/DashTable.percy.tsx index a39afdddb..8c9ae7189 100644 --- a/tests/visual/percy-storybook/DashTable.percy.tsx +++ b/tests/visual/percy-storybook/DashTable.percy.tsx @@ -136,8 +136,8 @@ storiesOf('DashTable/Fixed Rows & Columns', module) id='table' data={dataA2J} columns={columnsA2J} - n_fixed_columns={2} - n_fixed_rows={1} + fixed_columns={{ headers: true }} + fixed_rows={{ headers: true }} row_deletable={true} row_selectable={true} style_data_conditional={style_data_conditional} @@ -147,7 +147,7 @@ storiesOf('DashTable/Fixed Rows & Columns', module) id='table' data={dataA2J} columns={columnsA2J} - n_fixed_rows={1} + fixed_rows={{ headers: true }} row_deletable={true} row_selectable={true} style_data_conditional={style_data_conditional} @@ -157,7 +157,7 @@ storiesOf('DashTable/Fixed Rows & Columns', module) id='table' data={dataA2J} columns={columnsA2J} - n_fixed_columns={2} + fixed_columns={{ headers: true }} row_deletable={true} row_selectable={true} style_data_conditional={style_data_conditional} @@ -168,8 +168,8 @@ storiesOf('DashTable/Fixed Rows & Columns', module) data={dataA2J} columns={mergedColumns} merge_duplicate_headers={true} - n_fixed_columns={4} - n_fixed_rows={2} + fixed_columns={{ headers: true, data: 4 }} + fixed_rows={{ headers: true, data: 1 }} style_data_conditional={style_data_conditional} />)) .add('with 2 fixed rows, 3 fixed columns, hidden columns and merged cells', () => { @@ -182,8 +182,8 @@ storiesOf('DashTable/Fixed Rows & Columns', module) data={dataA2J} columns={mergedColumns} merge_duplicate_headers={true} - n_fixed_columns={3} - n_fixed_rows={2} + fixed_columns={{ headers: true, data: 3 }} + fixed_rows={{ headers: true, data: 1 }} style_data_conditional={style_data_conditional} />); }); @@ -277,8 +277,8 @@ storiesOf('DashTable/Without id', module) setProps={setProps} data={dataA2J} columns={columnsA2J} - n_fixed_columns={2} - n_fixed_rows={1} + fixed_columns={{ headers: true }} + fixed_rows={{ headers: true }} row_deletable={true} row_selectable={true} style_data_conditional={style_data_conditional} @@ -287,8 +287,8 @@ storiesOf('DashTable/Without id', module) setProps={setProps} data={dataA2J} columns={columnsA2J} - n_fixed_columns={2} - n_fixed_rows={1} + fixed_columns={{ headers: true }} + fixed_rows={{ headers: true }} row_deletable={true} row_selectable={true} style_table={{height: 500, width: 200}} @@ -298,8 +298,8 @@ storiesOf('DashTable/Without id', module) setProps={setProps} data={dataA2J} columns={columnsA2J} - n_fixed_columns={2} - n_fixed_rows={1} + fixed_columns={{ headers: true }} + fixed_rows={{ headers: true }} row_deletable={true} row_selectable={true} style_table={{height: 500, width: 200}} @@ -313,8 +313,8 @@ storiesOf('DashTable/Without id', module) setProps={setProps} data={dataA2J} columns={columnsA2J} - n_fixed_columns={2} - n_fixed_rows={1} + fixed_columns={{ headers: true }} + fixed_rows={{ headers: true }} row_deletable={true} row_selectable={true} style_table={{height: 500, width: 400}} @@ -327,8 +327,8 @@ storiesOf('DashTable/Without id', module) setProps={setProps} data={dataA2J} columns={columnsA2J} - n_fixed_columns={2} - n_fixed_rows={1} + fixed_columns={{ headers: true }} + fixed_rows={{ headers: true }} row_deletable={true} row_selectable={true} style_table={{height: 500, width: 400}} diff --git a/tests/visual/percy-storybook/Virtualization.percy.tsx b/tests/visual/percy-storybook/Virtualization.percy.tsx index d197da826..2500199a4 100644 --- a/tests/visual/percy-storybook/Virtualization.percy.tsx +++ b/tests/visual/percy-storybook/Virtualization.percy.tsx @@ -21,7 +21,7 @@ storiesOf('DashTable/Virtualization', module) pagination_mode={false} virtualization={true} editable={true} - n_fixed_rows={1} + fixed_rows={{ headers: true }} style_table={{ height: 800, max_height: 800, diff --git a/tests/visual/percy-storybook/Width.all.percy.tsx b/tests/visual/percy-storybook/Width.all.percy.tsx index bc90d6c6b..34ceb6f24 100644 --- a/tests/visual/percy-storybook/Width.all.percy.tsx +++ b/tests/visual/percy-storybook/Width.all.percy.tsx @@ -39,14 +39,14 @@ storiesOf('DashTable/Width width, minWidth, maxWidth', module) />)) .add('with frozen rows', () => ()) .add('with frozen columns', () => ()) .add('with frozen rows and frozen columns', () => ()); \ No newline at end of file diff --git a/tests/visual/percy-storybook/Width.defaults.percy.tsx b/tests/visual/percy-storybook/Width.defaults.percy.tsx index fe050ddcd..9e8ec201a 100644 --- a/tests/visual/percy-storybook/Width.defaults.percy.tsx +++ b/tests/visual/percy-storybook/Width.defaults.percy.tsx @@ -36,14 +36,14 @@ storiesOf('DashTable/Width defaults', module) />)) .add('with frozen rows', () => ()) .add('with frozen columns', () => ()) .add('with frozen rows and frozen columns', () => ()); \ No newline at end of file diff --git a/tests/visual/percy-storybook/Width.max.percy.tsx b/tests/visual/percy-storybook/Width.max.percy.tsx index de4157f70..fa2a4529a 100644 --- a/tests/visual/percy-storybook/Width.max.percy.tsx +++ b/tests/visual/percy-storybook/Width.max.percy.tsx @@ -37,14 +37,14 @@ storiesOf('DashTable/Width maxWidth only', module) />)) .add('with frozen rows', () => ()) .add('with frozen columns', () => ()) .add('with frozen rows and frozen columns', () => ()); \ No newline at end of file diff --git a/tests/visual/percy-storybook/Width.min.percy.tsx b/tests/visual/percy-storybook/Width.min.percy.tsx index ad9525100..03b4be077 100644 --- a/tests/visual/percy-storybook/Width.min.percy.tsx +++ b/tests/visual/percy-storybook/Width.min.percy.tsx @@ -37,14 +37,14 @@ storiesOf('DashTable/Width minWidth only', module) />)) .add('with frozen rows', () => ()) .add('with frozen columns', () => ()) .add('with frozen rows and frozen columns', () => ()); \ No newline at end of file diff --git a/tests/visual/percy-storybook/Width.percentages.percy.tsx b/tests/visual/percy-storybook/Width.percentages.percy.tsx index 0bdea6f0f..3f9264e37 100644 --- a/tests/visual/percy-storybook/Width.percentages.percy.tsx +++ b/tests/visual/percy-storybook/Width.percentages.percy.tsx @@ -48,14 +48,14 @@ storiesOf('DashTable/Width percentages', module) />)) .add('with frozen rows', () => ()) .add('with frozen columns', () => ()) .add('with frozen rows and frozen columns', () => ()); \ No newline at end of file diff --git a/tests/visual/percy-storybook/Width.width.percy.tsx b/tests/visual/percy-storybook/Width.width.percy.tsx index 2957f66cd..c2b9aac1b 100644 --- a/tests/visual/percy-storybook/Width.width.percy.tsx +++ b/tests/visual/percy-storybook/Width.width.percy.tsx @@ -36,14 +36,14 @@ storiesOf('DashTable/Width width only', module) />)) .add('with frozen rows', () => ()) .add('with frozen columns', () => ()) .add('with frozen rows and frozen columns', () => ()); \ No newline at end of file diff --git a/tests/visual/percy-storybook/fixtures.ts b/tests/visual/percy-storybook/fixtures.ts index a810b0cff..22ba8821f 100644 --- a/tests/visual/percy-storybook/fixtures.ts +++ b/tests/visual/percy-storybook/fixtures.ts @@ -27,7 +27,7 @@ export default [ {'column-1': 'mtl'}, {'column-1': 'boston'} ], - n_fixed_rows: 1, + fixed_rows: { headers: true }, editable: true, css: [{ selector: '.dash-spreadsheet.dash-freeze-top', From 8a225d6c528a25c00a5122027588cd9d6940697a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 30 May 2019 12:19:01 -0400 Subject: [PATCH 20/46] lint --- src/dash-table/dash/sanitize.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dash-table/dash/sanitize.ts b/src/dash-table/dash/sanitize.ts index ff654ae5b..31eed4f50 100644 --- a/src/dash-table/dash/sanitize.ts +++ b/src/dash-table/dash/sanitize.ts @@ -39,7 +39,7 @@ const getFixedColumns = ( ) => (fixed.headers === false && 0) || ( (row_deletable ? 1 : 0) + (row_selectable ? 1 : 0) + - (typeof fixed.data === 'number' ? fixed.data : 0) + (typeof fixed.data === 'number' ? fixed.data : 0) ); const getFixedRows = ( @@ -49,7 +49,7 @@ const getFixedRows = ( ) => (fixed.headers === false && 0) || ( headerRows(columns) + (filter ? 1 : 0) + - (typeof fixed.data === 'number' ? fixed.data : 0) + (typeof fixed.data === 'number' ? fixed.data : 0) ); export default (props: PropsWithDefaults): SanitizedProps => { From 7ffab103d305df49aac353a9ead665138ad750a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 30 May 2019 12:24:05 -0400 Subject: [PATCH 21/46] typo --- tests/visual/percy-storybook/Border.defaults.percy.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/visual/percy-storybook/Border.defaults.percy.tsx b/tests/visual/percy-storybook/Border.defaults.percy.tsx index 3652cf5d1..7ce3688ab 100644 --- a/tests/visual/percy-storybook/Border.defaults.percy.tsx +++ b/tests/visual/percy-storybook/Border.defaults.percy.tsx @@ -94,7 +94,7 @@ storiesOf('DashTable/Border (available space filled)', module) />)) .add('with no frozen rows and frozen columns', () => ()) .add('with frozen rows and frozen columns', () => ( Date: Thu, 30 May 2019 13:50:49 -0400 Subject: [PATCH 22/46] typing --- src/core/type/index.ts | 5 ++++- src/dash-table/components/Table/index.tsx | 3 +-- src/dash-table/components/Table/props.ts | 5 +++-- src/dash-table/dash/sanitize.ts | 27 ++++++++++++++--------- 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/core/type/index.ts b/src/core/type/index.ts index b9e54c774..85d2cbfd3 100644 --- a/src/core/type/index.ts +++ b/src/core/type/index.ts @@ -7,4 +7,7 @@ export type OptionalPluck = { [r in R]?: T[r] }; export type RequiredProp = T[R]; export type OptionalProp = T[R] | undefined; -export type PropOf = R; \ No newline at end of file +export type PropOf = R; + +export type Merge = Omit> & N; +export type Omit = Pick>; \ No newline at end of file diff --git a/src/dash-table/components/Table/index.tsx b/src/dash-table/components/Table/index.tsx index 6ac30638f..0f794a2af 100644 --- a/src/dash-table/components/Table/index.tsx +++ b/src/dash-table/components/Table/index.tsx @@ -23,7 +23,6 @@ import { SetProps, IState, StandaloneState, - PropsWithDefaults, SanitizedAndDerivedProps } from './props'; @@ -115,7 +114,7 @@ export default class Table extends Component; export type SanitizedAndDerivedProps = SanitizedProps & IDerivedProps; diff --git a/src/dash-table/dash/sanitize.ts b/src/dash-table/dash/sanitize.ts index 31eed4f50..bc72926a5 100644 --- a/src/dash-table/dash/sanitize.ts +++ b/src/dash-table/dash/sanitize.ts @@ -2,7 +2,17 @@ import * as R from 'ramda'; import { memoizeOne } from 'core/memoizer'; -import { Columns, ColumnType, INumberLocale, IColumn, PropsWithDefaults, Filtering, RowSelection, SanitizedProps, Fixed } from 'dash-table/components/Table/props'; +import { + Columns, + ColumnType, + Filtering, + Fixed, + IColumn, + INumberLocale, + PropsWithDefaults, + RowSelection, + SanitizedProps +} from 'dash-table/components/Table/props'; import headerRows from 'dash-table/derived/header/headerRows'; const D3_DEFAULT_LOCALE: INumberLocale = { @@ -55,15 +65,12 @@ const getFixedRows = ( export default (props: PropsWithDefaults): SanitizedProps => { const locale_format = applyDefaultToLocale(props.locale_format); - return R.mergeAll([ - props, - { - columns: applyDefaultsToColumns(locale_format, props.columns), - fixed_columns: getFixedColumns(props.fixed_columns, props.row_deletable, props.row_selectable), - fixed_rows: getFixedRows(props.fixed_rows, props.columns, props.filtering), - locale_format - } - ]) as any; + return R.merge(props, { + columns: applyDefaultsToColumns(locale_format, props.columns), + fixed_columns: getFixedColumns(props.fixed_columns, props.row_deletable, props.row_selectable), + fixed_rows: getFixedRows(props.fixed_rows, props.columns, props.filtering), + locale_format + }); }; export const getLocale = (...locales: Partial[]): INumberLocale => From e05f6256331e835dbe34ca26af72cae1cc67c07c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 30 May 2019 14:23:13 -0400 Subject: [PATCH 23/46] typo + wrong sanitation logic --- src/dash-table/components/EdgeFactory.tsx | 4 ++-- src/dash-table/dash/sanitize.ts | 18 ++++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/dash-table/components/EdgeFactory.tsx b/src/dash-table/components/EdgeFactory.tsx index 1624073b6..1bef05835 100644 --- a/src/dash-table/components/EdgeFactory.tsx +++ b/src/dash-table/components/EdgeFactory.tsx @@ -186,7 +186,7 @@ export default class EdgeFactory { operations: number, filtering: boolean, filterMap: Map, - _fixed_columns: number, + fixed_columns: number, fixed_rows: number, style_as_list_view: boolean, style_cell: Style, @@ -300,7 +300,7 @@ export default class EdgeFactory { this.hOverride(filterOpEdges, dataOpEdges, cutoffWeight); } - if (_fixed_columns === operations) { + if (fixed_columns === operations) { this.vOverride(headerOpEdges, headerEdges, cutoffWeight); this.vOverride(filterOpEdges, filterEdges, cutoffWeight); this.vOverride(dataOpEdges, dataEdges, cutoffWeight); diff --git a/src/dash-table/dash/sanitize.ts b/src/dash-table/dash/sanitize.ts index bc72926a5..330b3a622 100644 --- a/src/dash-table/dash/sanitize.ts +++ b/src/dash-table/dash/sanitize.ts @@ -42,25 +42,23 @@ const applyDefaultsToColumns = memoizeOne( }, columns) ); +const data2number = (data?: any) => +data || 0; + const getFixedColumns = ( fixed: Fixed, row_deletable: boolean, row_selectable: RowSelection -) => (fixed.headers === false && 0) || ( - (row_deletable ? 1 : 0) + - (row_selectable ? 1 : 0) + - (typeof fixed.data === 'number' ? fixed.data : 0) -); +) => !fixed.headers ? + 0 : + (row_deletable ? 1 : 0) + (row_selectable ? 1 : 0) + data2number(fixed.data); const getFixedRows = ( fixed: Fixed, columns: IColumn[], filter: Filtering -) => (fixed.headers === false && 0) || ( - headerRows(columns) + - (filter ? 1 : 0) + - (typeof fixed.data === 'number' ? fixed.data : 0) -); +) => !fixed.headers ? + 0 : + headerRows(columns) + (filter ? 1 : 0) + data2number(fixed.data); export default (props: PropsWithDefaults): SanitizedProps => { const locale_format = applyDefaultToLocale(props.locale_format); From 65211922d0ee58dae8eccb0d521eeec0c42257a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Fri, 31 May 2019 09:06:45 -0400 Subject: [PATCH 24/46] fix hidden borders in FF --- src/dash-table/components/Table/Table.less | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dash-table/components/Table/Table.less b/src/dash-table/components/Table/Table.less index 4d919b91e..c5673a200 100644 --- a/src/dash-table/components/Table/Table.less +++ b/src/dash-table/components/Table/Table.less @@ -330,6 +330,7 @@ // cell content styling td, th { + background-clip: padding-box; padding: 2px; white-space: nowrap; overflow-x: hidden; From c8ef334aa1516219de9f48a5340c80d4ab94e5cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Rivet?= Date: Fri, 31 May 2019 14:34:51 -0400 Subject: [PATCH 25/46] Remove content_style (as part of API refactoring) (#453) --- demo/AppMode.ts | 4 ---- src/dash-table/components/ControlledTable/index.tsx | 4 +--- src/dash-table/components/Table/Table.less | 11 ----------- src/dash-table/components/Table/props.ts | 6 ------ src/dash-table/dash/DataTable.js | 12 ------------ tests/visual/percy-storybook/Sizing.percy.tsx | 2 -- tests/visual/percy-storybook/Style.percy.tsx | 3 --- tests/visual/percy-storybook/Width.all.percy.tsx | 1 - .../visual/percy-storybook/Width.defaults.percy.tsx | 1 - tests/visual/percy-storybook/Width.empty.percy.tsx | 1 - tests/visual/percy-storybook/Width.max.percy.tsx | 1 - tests/visual/percy-storybook/Width.min.percy.tsx | 1 - .../percy-storybook/Width.percentages.percy.tsx | 2 -- tests/visual/percy-storybook/Width.width.percy.tsx | 1 - tests/visual/percy-storybook/fixtures.ts | 2 -- 15 files changed, 1 insertion(+), 51 deletions(-) diff --git a/demo/AppMode.ts b/demo/AppMode.ts index 4bae7fd08..ac9f98bec 100644 --- a/demo/AppMode.ts +++ b/demo/AppMode.ts @@ -4,7 +4,6 @@ import Environment from 'core/environment'; import { generateMockData, IDataMock, generateSpaceMockData } from './data'; import { - ContentStyle, PropsWithDefaults, ChangeAction, ChangeFailure, @@ -93,7 +92,6 @@ function getDefaultState( merge_duplicate_headers: false, row_deletable: true, row_selectable: 'single', - content_style: ContentStyle.Fit, pagination_mode: 'fe' }) as Partial }; @@ -235,7 +233,6 @@ function getVirtualizedState() { merge_duplicate_headers: false, row_deletable: true, row_selectable: 'single', - content_style: 'fit', virtualization: true }) }; @@ -255,7 +252,6 @@ function getFixedVirtualizedState() { merge_duplicate_headers: false, row_deletable: true, row_selectable: 'single', - content_style: 'fit', virtualization: true }) }; diff --git a/src/dash-table/components/ControlledTable/index.tsx b/src/dash-table/components/ControlledTable/index.tsx index f4deb71bb..59ce6fa68 100644 --- a/src/dash-table/components/ControlledTable/index.tsx +++ b/src/dash-table/components/ControlledTable/index.tsx @@ -684,7 +684,6 @@ export default class ControlledTable extends PureComponent columns, tooltip_conditional, tooltip, - content_style, currentTooltip, filtering, fixed_columns, @@ -732,8 +731,7 @@ export default class ControlledTable extends PureComponent ...(empty[1][1] ? ['dash-empty-11'] : []), ...(columns.length ? [] : ['dash-no-columns']), ...(virtualized.data.length ? [] : ['dash-no-data']), - ...(filtering ? [] : ['dash-no-filter']), - [`dash-${content_style}`] + ...(filtering ? [] : ['dash-no-filter']) ]; const containerClasses = ['dash-spreadsheet-container', ...classes]; diff --git a/src/dash-table/components/Table/Table.less b/src/dash-table/components/Table/Table.less index c5673a200..9188b1dc8 100644 --- a/src/dash-table/components/Table/Table.less +++ b/src/dash-table/components/Table/Table.less @@ -233,17 +233,6 @@ } } - &.dash-grow { - .cell-0-1, - .cell-1-1 { - flex: 1 0 auto; - } - - table { - width: 100%; - } - } - .selected-row { td, th { background-color: var(--selected-row); diff --git a/src/dash-table/components/Table/props.ts b/src/dash-table/components/Table/props.ts index 6dcc6ca6b..eede3b77b 100644 --- a/src/dash-table/components/Table/props.ts +++ b/src/dash-table/components/Table/props.ts @@ -45,11 +45,6 @@ export interface IVirtualizedDerivedData extends IDerivedData { }; } -export enum ContentStyle { - Fit = 'fit', - Grow = 'grow' -} - export interface ICellCoordinates { row: number; column: number; @@ -268,7 +263,6 @@ export interface IProps { dropdown?: IStaticDropdowns; dropdown_conditional?: ConditionalDropdowns; dropdown_data: IDataDropdowns; - content_style: ContentStyle; css?: IStylesheetRule[]; data?: Data; editable?: boolean; diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index 8acb460e8..ec43cf6c4 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -37,7 +37,6 @@ export const defaultProps = { page_size: 250 }, - content_style: 'grow', css: [], filter: '', filtering: false, @@ -323,17 +322,6 @@ export const propTypes = { separate_4digits: PropTypes.bool }), - /** - * `content_style` toggles between a set of CSS styles for - * two common behaviors: - * - `fit`: The table container's width be equal to the width of its content. - * - `grow`: The table container's width will grow to be the size of the container. - * - * NOTE - This property will likely change in the future, - * subscribe to [https://github.com/plotly/dash-table/issues/176](https://github.com/plotly/dash-table/issues/176) - * for more details. - */ - content_style: PropTypes.oneOf(['fit', 'grow']), /** * The `css` property is a way to embed CSS selectors and rules * onto the page. diff --git a/tests/visual/percy-storybook/Sizing.percy.tsx b/tests/visual/percy-storybook/Sizing.percy.tsx index 338ced890..c7dd959f6 100644 --- a/tests/visual/percy-storybook/Sizing.percy.tsx +++ b/tests/visual/percy-storybook/Sizing.percy.tsx @@ -29,7 +29,6 @@ storiesOf('DashTable/Sizing', module) />)) .add('padding', () => ()) .add('single column width by percentage', () => ( ({ name: i, id: i }), R.keysIn(data[0])) } - content_style='grow' style_table={{ width: '100%' }} @@ -102,7 +101,6 @@ storiesOf('DashTable/Style type condition', module) i => ({ name: i, id: i }), R.keysIn(data[0])) } - content_style='grow' style_table={{ width: '100%' }} @@ -123,7 +121,6 @@ storiesOf('DashTable/Style type condition', module) i => ({ name: i, id: i }), R.keysIn(data[0])) } - content_style='grow' style_table={{ width: '100%' }} diff --git a/tests/visual/percy-storybook/Width.all.percy.tsx b/tests/visual/percy-storybook/Width.all.percy.tsx index 34ceb6f24..f9a524108 100644 --- a/tests/visual/percy-storybook/Width.all.percy.tsx +++ b/tests/visual/percy-storybook/Width.all.percy.tsx @@ -23,7 +23,6 @@ const baseProps = { setProps, id: 'table', data, - content_style: 'fit', style_data_conditional: [ { width: '20px', min_width: '20px', max_width: '20px' } ] diff --git a/tests/visual/percy-storybook/Width.defaults.percy.tsx b/tests/visual/percy-storybook/Width.defaults.percy.tsx index 9e8ec201a..dea879c54 100644 --- a/tests/visual/percy-storybook/Width.defaults.percy.tsx +++ b/tests/visual/percy-storybook/Width.defaults.percy.tsx @@ -22,7 +22,6 @@ const data = (() => { const baseProps = { setProps, id: 'table', - content_style: 'fit', data }; diff --git a/tests/visual/percy-storybook/Width.empty.percy.tsx b/tests/visual/percy-storybook/Width.empty.percy.tsx index a4685f626..f2d0429e0 100644 --- a/tests/visual/percy-storybook/Width.empty.percy.tsx +++ b/tests/visual/percy-storybook/Width.empty.percy.tsx @@ -23,7 +23,6 @@ const data = (() => { const baseProps = { setProps, id: 'table', - content_style: 'fit', data, filtering: 'fe', style_cell: { width: 100, max_width: 100, min_width: 100 } diff --git a/tests/visual/percy-storybook/Width.max.percy.tsx b/tests/visual/percy-storybook/Width.max.percy.tsx index fa2a4529a..bf75fdf34 100644 --- a/tests/visual/percy-storybook/Width.max.percy.tsx +++ b/tests/visual/percy-storybook/Width.max.percy.tsx @@ -22,7 +22,6 @@ const data = (() => { const baseProps = { setProps, id: 'table', - content_style: 'fit', data, style_data_conditional: [{ max_width: 10 }] }; diff --git a/tests/visual/percy-storybook/Width.min.percy.tsx b/tests/visual/percy-storybook/Width.min.percy.tsx index 03b4be077..ba229e20d 100644 --- a/tests/visual/percy-storybook/Width.min.percy.tsx +++ b/tests/visual/percy-storybook/Width.min.percy.tsx @@ -22,7 +22,6 @@ const data = (() => { const baseProps = { setProps, id: 'table', - content_style: 'fit', data }; diff --git a/tests/visual/percy-storybook/Width.percentages.percy.tsx b/tests/visual/percy-storybook/Width.percentages.percy.tsx index 3f9264e37..609a4989c 100644 --- a/tests/visual/percy-storybook/Width.percentages.percy.tsx +++ b/tests/visual/percy-storybook/Width.percentages.percy.tsx @@ -21,12 +21,10 @@ const data = (() => { const baseProps = { setProps, id: 'table', - content_style: 'grow', data }; const props = Object.assign({}, baseProps, { - content_style: 'grow', columns: columns.map((id => ({ id: id, name: id.toUpperCase() }))), style_cell: { width: '33%' diff --git a/tests/visual/percy-storybook/Width.width.percy.tsx b/tests/visual/percy-storybook/Width.width.percy.tsx index c2b9aac1b..b67cfc643 100644 --- a/tests/visual/percy-storybook/Width.width.percy.tsx +++ b/tests/visual/percy-storybook/Width.width.percy.tsx @@ -22,7 +22,6 @@ const data = (() => { const baseProps = { setProps, id: 'table', - content_style: 'fit', data }; diff --git a/tests/visual/percy-storybook/fixtures.ts b/tests/visual/percy-storybook/fixtures.ts index 22ba8821f..c08e618f5 100644 --- a/tests/visual/percy-storybook/fixtures.ts +++ b/tests/visual/percy-storybook/fixtures.ts @@ -159,7 +159,6 @@ export default [ { name: 'dropdown with column widths', props: { - content_style: 'fit', style_data_conditional: [ { if: { column_id: 'column-2' }, width: 400 }, { if: { column_id: 'column-3' }, width: 80 } @@ -281,7 +280,6 @@ export default [ name: 'mixed percentage and pixel column widths', props: { id: 'table', - content_style: 'fit', style_data_conditional: [ { if: { column_id: 'column-2' }, width: 400 }, { if: { column_id: 'column-3' }, width: '30%' } From 973d94ed623d7a90cb0e63b60ac202a1693fb03a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Rivet?= Date: Mon, 3 Jun 2019 12:22:14 -0400 Subject: [PATCH 26/46] Refactor sorting (#454) --- src/core/sorting/index.ts | 12 +- src/core/type/index.ts | 3 +- src/dash-table/components/Table/index.tsx | 5 +- src/dash-table/components/Table/props.ts | 17 +- src/dash-table/dash/DataTable.js | 41 ++- src/dash-table/dash/sanitize.ts | 9 +- src/dash-table/derived/data/virtual.ts | 26 +- src/dash-table/utils/applyClipboardToData.ts | 3 +- tests/cypress/tests/unit/sorting_test.ts | 242 ++++++++++-------- .../app_editor_with_configurable_options.py | 152 ----------- .../percy-storybook/DashTable.percy.tsx | 24 +- 11 files changed, 231 insertions(+), 303 deletions(-) delete mode 100644 tests/dash/app_editor_with_configurable_options.py diff --git a/src/core/sorting/index.ts b/src/core/sorting/index.ts index f0bdd2c94..c90b0a413 100644 --- a/src/core/sorting/index.ts +++ b/src/core/sorting/index.ts @@ -14,8 +14,8 @@ export enum SortDirection { } export type SortSettings = ISortSetting[]; -type IsNullyFn = (value: any) => boolean; -export const defaultIsNully: IsNullyFn = (value: any) => value === undefined || value === null; +type IsNullyFn = (value: any, id: string) => boolean; +export const defaultIsNully: IsNullyFn = (value: any, _: string) => R.isNil(value); export default (data: any[], settings: SortSettings, isNully: IsNullyFn = defaultIsNully): any[] => { if (!settings.length) { return data; @@ -30,9 +30,9 @@ export default (data: any[], settings: SortSettings, isNully: IsNullyFn = defaul const prop1 = d1[id]; const prop2 = d2[id]; - if (isNully(prop1)) { + if (isNully(prop1, setting.column_id)) { return false; - } else if (isNully(prop2)) { + } else if (isNully(prop2, setting.column_id)) { return true; } @@ -44,9 +44,9 @@ export default (data: any[], settings: SortSettings, isNully: IsNullyFn = defaul const prop1 = d1[id]; const prop2 = d2[id]; - if (isNully(prop1)) { + if (isNully(prop1, setting.column_id)) { return false; - } else if (isNully(prop2)) { + } else if (isNully(prop2, setting.column_id)) { return true; } diff --git a/src/core/type/index.ts b/src/core/type/index.ts index 85d2cbfd3..fa2f45c5b 100644 --- a/src/core/type/index.ts +++ b/src/core/type/index.ts @@ -9,5 +9,4 @@ export type OptionalProp = T[R] | undefined; export type PropOf = R; -export type Merge = Omit> & N; -export type Omit = Pick>; \ No newline at end of file +export type Merge = Omit> & N; \ No newline at end of file diff --git a/src/dash-table/components/Table/index.tsx b/src/dash-table/components/Table/index.tsx index 0f794a2af..a81e79c59 100644 --- a/src/dash-table/components/Table/index.tsx +++ b/src/dash-table/components/Table/index.tsx @@ -109,7 +109,6 @@ export default class Table extends Component void; export type SetState = (state: Partial) => void; +export type SortAsNone = (string | number | boolean)[]; export type Sorting = 'fe' | 'be' | boolean; export type SortingType = 'multi' | 'single'; export type VisibleColumns = IVisibleColumn[]; @@ -149,6 +150,7 @@ export interface IBaseVisibleColumn { deletable?: boolean | boolean[]; editable?: boolean; renamable?: boolean | boolean[]; + sort_as_none: SortAsNone; id: ColumnId; name: string | string[]; } @@ -281,7 +283,7 @@ export interface IProps { sorting?: Sorting; sort_by?: SortSettings; sorting_type?: SortingType; - sorting_treat_empty_string_as_none?: boolean; + sort_as_none?: SortAsNone; style_as_list_view?: boolean; pagination_mode?: PaginationMode; pagination_settings?: IPaginationSettings; @@ -323,7 +325,7 @@ interface IDefaultProps { sorting: Sorting; sort_by: SortSettings; sorting_type: SortingType; - sorting_treat_empty_string_as_none: boolean; + sort_as_none: SortAsNone; style_as_list_view: boolean; pagination_mode: PaginationMode; @@ -359,10 +361,13 @@ interface IDerivedProps { export type PropsWithDefaults = IProps & IDefaultProps; -export type SanitizedProps = Merge; +export type SanitizedProps = Omit< + Omit< + Merge, + 'locale_format' + >, + 'sort_as_none' +>; export type SanitizedAndDerivedProps = SanitizedProps & IDerivedProps; diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index ec43cf6c4..e2712d8dd 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -40,6 +40,7 @@ export const defaultProps = { css: [], filter: '', filtering: false, + sort_as_none: [], sorting: false, sorting_type: 'single', sort_by: [], @@ -105,7 +106,7 @@ export const propTypes = { * Columns describes various aspects about each individual column. * `name` and `id` are the only required parameters. */ - columns: PropTypes.arrayOf(PropTypes.shape({ + columns: PropTypes.arrayOf(PropTypes.exact({ /** * If True, the user can delete the column by clicking on a little `x` @@ -178,8 +179,8 @@ export const propTypes = { * dash_table.FormatTemplate contains helper functions to rapidly use certain * typical number formats. */ - format: PropTypes.shape({ - locale: PropTypes.shape({ + format: PropTypes.exact({ + locale: PropTypes.exact({ symbol: PropTypes.arrayOf(PropTypes.string), decimal: PropTypes.string, group: PropTypes.string, @@ -238,7 +239,7 @@ export const propTypes = { * default: replace the provided value with `validation.default` * reject: do not modify the existing value */ - on_change: PropTypes.shape({ + on_change: PropTypes.exact({ action: PropTypes.oneOf([ 'coerce', 'none', @@ -251,6 +252,16 @@ export const propTypes = { ]) }), + /** + * An array of string, number and boolean values that are treated as `None` + * when sorting is applied to the column. + */ + sort_as_none: PropTypes.arrayOf(PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number, + PropTypes.bool + ])), + /** * The `validation` options. * 'allow_null': Allow the use of nully values (undefined, null, NaN) (default: false) @@ -260,7 +271,7 @@ export const propTypes = { * this is 1949 to 2048 but in 2020 it will be different. If used with * `action: 'coerce'`, will convert user input to a 4-digit year. */ - validation: PropTypes.shape({ + validation: PropTypes.exact({ allow_null: PropTypes.bool, default: PropTypes.any, allow_YY: PropTypes.bool @@ -312,7 +323,7 @@ export const propTypes = { * 'percent': (default: '%') the string used for the percentage symbol * 'separate_4digits': (default: True) separate integers with 4-digits or less */ - locale_format: PropTypes.shape({ + locale_format: PropTypes.exact({ symbol: PropTypes.arrayOf(PropTypes.string), decimal: PropTypes.string, group: PropTypes.string, @@ -334,7 +345,7 @@ export const propTypes = { * ] * */ - css: PropTypes.arrayOf(PropTypes.shape({ + css: PropTypes.arrayOf(PropTypes.exact({ selector: PropTypes.string.isRequired, rule: PropTypes.string.isRequired })), @@ -573,7 +584,7 @@ export const propTypes = { * Use this property to index through data in your callbacks with * backend paging. */ - pagination_settings: PropTypes.shape({ + pagination_settings: PropTypes.exact({ current_page: PropTypes.number.isRequired, page_size: PropTypes.number.isRequired }), @@ -871,12 +882,16 @@ export const propTypes = { })), /** - * If False, then empty strings (`''`) are considered - * valid values (they will appear first when sorting ascending). - * If True, empty strings will be ignored, causing these cells to always - * appear last. + * An array of string, number and boolean values that are treated as `None` + * when sorting. This value will be used by columns without `sort_as_none`. + * + * Defaults to `[]`. */ - sorting_treat_empty_string_as_none: PropTypes.bool, + sort_as_none: PropTypes.arrayOf(PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number, + PropTypes.bool + ])), /** * CSS styles to be applied to the outer `table` container. diff --git a/src/dash-table/dash/sanitize.ts b/src/dash-table/dash/sanitize.ts index 330b3a622..fe9f235e5 100644 --- a/src/dash-table/dash/sanitize.ts +++ b/src/dash-table/dash/sanitize.ts @@ -11,7 +11,8 @@ import { INumberLocale, PropsWithDefaults, RowSelection, - SanitizedProps + SanitizedProps, + SortAsNone } from 'dash-table/components/Table/props'; import headerRows from 'dash-table/derived/header/headerRows'; @@ -30,9 +31,11 @@ const DEFAULT_SPECIFIER = ''; const applyDefaultToLocale = memoizeOne((locale: INumberLocale) => getLocale(locale)); const applyDefaultsToColumns = memoizeOne( - (defaultLocale: INumberLocale, columns: Columns) => R.map(column => { + (defaultLocale: INumberLocale, defaultSort: SortAsNone, columns: Columns) => R.map(column => { const c = R.clone(column); + c.sort_as_none = c.sort_as_none || defaultSort; + if (c.type === ColumnType.Numeric && c.format) { c.format.locale = getLocale(defaultLocale, c.format.locale); c.format.nully = getNully(c.format.nully); @@ -64,7 +67,7 @@ export default (props: PropsWithDefaults): SanitizedProps => { const locale_format = applyDefaultToLocale(props.locale_format); return R.merge(props, { - columns: applyDefaultsToColumns(locale_format, props.columns), + columns: applyDefaultsToColumns(locale_format, props.sort_as_none, props.columns), fixed_columns: getFixedColumns(props.fixed_columns, props.row_deletable, props.row_selectable), fixed_rows: getFixedRows(props.fixed_rows, props.columns, props.filtering), locale_format diff --git a/src/dash-table/derived/data/virtual.ts b/src/dash-table/derived/data/virtual.ts index ca14879d0..93ddd8f99 100644 --- a/src/dash-table/derived/data/virtual.ts +++ b/src/dash-table/derived/data/virtual.ts @@ -1,23 +1,26 @@ import * as R from 'ramda'; import { memoizeOneFactory } from 'core/memoizer'; -import sort, { defaultIsNully, SortSettings } from 'core/sorting'; +import sort, { SortSettings } from 'core/sorting'; import { + ColumnId, Data, Datum, Filtering, IDerivedData, - Sorting + SortAsNone, + Sorting, + VisibleColumns } from 'dash-table/components/Table/props'; import { QuerySyntaxTree } from 'dash-table/syntax-tree'; const getter = ( + columns: VisibleColumns, data: Data, filtering: Filtering, filter: string, sorting: Sorting, - sort_by: SortSettings = [], - sorting_treat_empty_string_as_none: boolean + sort_by: SortSettings = [] ): IDerivedData => { const map = new Map(); R.addIndex(R.forEach)((datum, index) => { @@ -32,9 +35,18 @@ const getter = ( data; } - const isNully = sorting_treat_empty_string_as_none ? - (value: any) => value === '' || defaultIsNully(value) : - undefined; + const getNullyCases = ( + columnId: ColumnId + ): SortAsNone => { + const column = R.find(c => c.id === columnId, columns); + + return (column && column.sort_as_none) || []; + }; + + const isNully = ( + value: any, + columnId: ColumnId + ) => R.isNil(value) || R.contains(value, getNullyCases(columnId)); if (sorting === 'fe' || sorting === true) { data = sort(data, sort_by, isNully); diff --git a/src/dash-table/utils/applyClipboardToData.ts b/src/dash-table/utils/applyClipboardToData.ts index b1bbb9df1..6a46f3001 100644 --- a/src/dash-table/utils/applyClipboardToData.ts +++ b/src/dash-table/utils/applyClipboardToData.ts @@ -36,7 +36,8 @@ export default ( newColumns.push({ id: `Column ${i + 1}`, name: `Column ${i + 1}`, - type: ColumnType.Any + type: ColumnType.Any, + sort_as_none: [] }); newData.forEach(row => (row[`Column ${i}`] = '')); } diff --git a/tests/cypress/tests/unit/sorting_test.ts b/tests/cypress/tests/unit/sorting_test.ts index 8b0a981d7..395e8b044 100644 --- a/tests/cypress/tests/unit/sorting_test.ts +++ b/tests/cypress/tests/unit/sorting_test.ts @@ -1,180 +1,206 @@ +import * as R from 'ramda'; + import sort, { SortDirection, SortSettings } from 'core/sorting'; import multiUpdateSettings from 'core/sorting/multi'; import singleUpdateSettings from 'core/sorting/single'; describe('sort', () => { it('sorts', () => { - const data = [[1], [3], [4], [2]]; + const data = [1, 3, 4, 2].map(v => ({ a: v })); const sorted = sort( data, - [{ column_id: 0, direction: SortDirection.Descending }] + [{ column_id: 'a', direction: SortDirection.Descending }] ); expect(sorted.length).to.equal(data.length); - expect(sorted[0][0]).to.equal(4); - expect(sorted[1][0]).to.equal(3); - expect(sorted[2][0]).to.equal(2); - expect(sorted[3][0]).to.equal(1); + expect(sorted[0].a).to.equal(4); + expect(sorted[1].a).to.equal(3); + expect(sorted[2].a).to.equal(2); + expect(sorted[3].a).to.equal(1); }); it('sorts undefined after when descending', () => { - const data = [[1], [undefined], [3], [undefined], [4], [2], [undefined]]; - + const data = [1, undefined, 3, undefined, 4, 2, undefined].map(v => ({ a: v })); const sorted = sort( data, - [{ column_id: 0, direction: SortDirection.Descending }] + [{ column_id: 'a', direction: SortDirection.Descending }] ); expect(sorted.length).to.equal(data.length); - expect(sorted[0][0]).to.equal(4); - expect(sorted[1][0]).to.equal(3); - expect(sorted[2][0]).to.equal(2); - expect(sorted[3][0]).to.equal(1); - expect(sorted[4][0]).to.equal(undefined); - expect(sorted[5][0]).to.equal(undefined); - expect(sorted[6][0]).to.equal(undefined); + expect(sorted[0].a).to.equal(4); + expect(sorted[1].a).to.equal(3); + expect(sorted[2].a).to.equal(2); + expect(sorted[3].a).to.equal(1); + expect(sorted[4].a).to.equal(undefined); + expect(sorted[5].a).to.equal(undefined); + expect(sorted[6].a).to.equal(undefined); }); it('sorts undefined after when ascending', () => { - const data = [[1], [undefined], [3], [undefined], [4], [2], [undefined]]; - + const data = [1, undefined, 3, undefined, 4, 2, undefined].map(v => ({ a: v })); const sorted = sort( data, - [{ column_id: 0, direction: SortDirection.Ascending }] + [{ column_id: 'a', direction: SortDirection.Ascending }] ); expect(sorted.length).to.equal(data.length); - expect(sorted[0][0]).to.equal(1); - expect(sorted[1][0]).to.equal(2); - expect(sorted[2][0]).to.equal(3); - expect(sorted[3][0]).to.equal(4); - expect(sorted[4][0]).to.equal(undefined); - expect(sorted[5][0]).to.equal(undefined); - expect(sorted[6][0]).to.equal(undefined); + expect(sorted[0].a).to.equal(1); + expect(sorted[1].a).to.equal(2); + expect(sorted[2].a).to.equal(3); + expect(sorted[3].a).to.equal(4); + expect(sorted[4].a).to.equal(undefined); + expect(sorted[5].a).to.equal(undefined); + expect(sorted[6].a).to.equal(undefined); }); it('sorts null after when descending', () => { - const data = [[1], [null], [3], [null], [4], [2], [null]]; - + const data = [1, null, 3, null, 4, 2, null].map(v => ({ a: v })); const sorted = sort( data, - [{ column_id: 0, direction: SortDirection.Descending }] + [{ column_id: 'a', direction: SortDirection.Descending }] ); expect(sorted.length).to.equal(data.length); - expect(sorted[0][0]).to.equal(4); - expect(sorted[1][0]).to.equal(3); - expect(sorted[2][0]).to.equal(2); - expect(sorted[3][0]).to.equal(1); - expect(sorted[4][0]).to.equal(null); - expect(sorted[5][0]).to.equal(null); - expect(sorted[6][0]).to.equal(null); + expect(sorted[0].a).to.equal(4); + expect(sorted[1].a).to.equal(3); + expect(sorted[2].a).to.equal(2); + expect(sorted[3].a).to.equal(1); + expect(sorted[4].a).to.equal(null); + expect(sorted[5].a).to.equal(null); + expect(sorted[6].a).to.equal(null); }); it('sorts null after when ascending', () => { - const data = [[1], [null], [3], [null], [4], [2], [null]]; + const data = [1, null, 3, null, 4, 2, null].map(v => ({ a: v })); + const sorted = sort( + data, + [{ column_id: 'a', direction: SortDirection.Ascending }] + ); + + expect(sorted.length).to.equal(data.length); + expect(sorted[0].a).to.equal(1); + expect(sorted[1].a).to.equal(2); + expect(sorted[2].a).to.equal(3); + expect(sorted[3].a).to.equal(4); + expect(sorted[4].a).to.equal(null); + expect(sorted[5].a).to.equal(null); + expect(sorted[6].a).to.equal(null); + }); + + it('sorts nully (undefined, null, 0, 1) after when descending', () => { + const data = [1, 0, 3, undefined, 4, 2, null].map(v => ({ a: v })); + const sorted = sort( + data, + [{ column_id: 'a', direction: SortDirection.Ascending }], + value => R.isNil(value) || value === 0 || value === 1 + ); + + expect(sorted.length).to.equal(data.length); + expect(sorted[0].a).to.equal(2); + expect(sorted[1].a).to.equal(3); + expect(sorted[2].a).to.equal(4); + }); + it('sorts nully (undefined, null, 0, 1) after when ascending', () => { + const data = [1, 0, 3, undefined, 4, 2, null].map(v => ({ a: v })); const sorted = sort( data, - [{ column_id: 0, direction: SortDirection.Ascending }] + [{ column_id: 'a', direction: SortDirection.Descending }], + value => R.isNil(value) || value === 0 || value === 1 ); expect(sorted.length).to.equal(data.length); - expect(sorted[0][0]).to.equal(1); - expect(sorted[1][0]).to.equal(2); - expect(sorted[2][0]).to.equal(3); - expect(sorted[3][0]).to.equal(4); - expect(sorted[4][0]).to.equal(null); - expect(sorted[5][0]).to.equal(null); - expect(sorted[6][0]).to.equal(null); + expect(sorted[0].a).to.equal(4); + expect(sorted[1].a).to.equal(3); + expect(sorted[2].a).to.equal(2); }); it('respects sort order - 1', () => { const data = [ - [1, 3], - [2, 3], - [0, 0], - [0, 3], - [0, 1], - [2, 1], - [1, 0], - [1, 1], - [2, 0] + { a: 1, b: 3 }, + { a: 2, b: 3 }, + { a: 0, b: 0 }, + { a: 0, b: 3 }, + { a: 0, b: 1 }, + { a: 2, b: 1 }, + { a: 1, b: 0 }, + { a: 1, b: 1 }, + { a: 2, b: 0 } ]; const sorted = sort( data, [ - { column_id: 0, direction: SortDirection.Descending }, - { column_id: 1, direction: SortDirection.Descending } + { column_id: 'a', direction: SortDirection.Descending }, + { column_id: 'b', direction: SortDirection.Descending } ] ); expect(sorted.length).to.equal(data.length); - expect(sorted[0][0]).to.equal(2); - expect(sorted[0][1]).to.equal(3); - expect(sorted[1][0]).to.equal(2); - expect(sorted[1][1]).to.equal(1); - expect(sorted[2][0]).to.equal(2); - expect(sorted[2][1]).to.equal(0); + expect(sorted[0].a).to.equal(2); + expect(sorted[0].b).to.equal(3); + expect(sorted[1].a).to.equal(2); + expect(sorted[1].b).to.equal(1); + expect(sorted[2].a).to.equal(2); + expect(sorted[2].b).to.equal(0); }); it('respects sort order - 2', () => { const data = [ - [1, 3], - [2, 3], - [0, 0], - [0, 3], - [0, 1], - [2, 1], - [1, 0], - [1, 1], - [2, 0] + { a: 1, b: 3 }, + { a: 2, b: 3 }, + { a: 0, b: 0 }, + { a: 0, b: 3 }, + { a: 0, b: 1 }, + { a: 2, b: 1 }, + { a: 1, b: 0 }, + { a: 1, b: 1 }, + { a: 2, b: 0 } ]; const sorted = sort( data, [ - { column_id: 1, direction: SortDirection.Descending }, - { column_id: 0, direction: SortDirection.Ascending } + { column_id: 'b', direction: SortDirection.Descending }, + { column_id: 'a', direction: SortDirection.Ascending } ] ); expect(sorted.length).to.equal(data.length); - expect(sorted[0][0]).to.equal(0); - expect(sorted[0][1]).to.equal(3); - expect(sorted[1][0]).to.equal(1); - expect(sorted[1][1]).to.equal(3); - expect(sorted[2][0]).to.equal(2); - expect(sorted[2][1]).to.equal(3); + expect(sorted[0].a).to.equal(0); + expect(sorted[0].b).to.equal(3); + expect(sorted[1].a).to.equal(1); + expect(sorted[1].b).to.equal(3); + expect(sorted[2].a).to.equal(2); + expect(sorted[2].b).to.equal(3); }); }); describe('sorting settings', () => { describe('single column sorting', () => { it('new descending', () => { - const settings = singleUpdateSettings([], { column_id: 0, direction: SortDirection.Descending }); + const settings = singleUpdateSettings([], { column_id: 'a', direction: SortDirection.Descending }); expect(settings.length).to.equal(1); - expect(settings[0].column_id).to.equal(0); + expect(settings[0].column_id).to.equal('a'); expect(settings[0].direction).to.equal(SortDirection.Descending); }); it('update to descending', () => { const settings = singleUpdateSettings( - [{ column_id: 0, direction: SortDirection.Ascending }], - { column_id: 0, direction: SortDirection.Descending } + [{ column_id: 'a', direction: SortDirection.Ascending }], + { column_id: 'a', direction: SortDirection.Descending } ); expect(settings.length).to.equal(1); - expect(settings[0].column_id).to.equal(0); + expect(settings[0].column_id).to.equal('a'); expect(settings[0].direction).to.equal(SortDirection.Descending); }); it('remove by setting to None', () => { const settings = singleUpdateSettings( - [{ column_id: 0, direction: SortDirection.Ascending }], - { column_id: 0, direction: SortDirection.None } + [{ column_id: 'a', direction: SortDirection.Ascending }], + { column_id: 'a', direction: SortDirection.None } ); expect(settings.length).to.equal(0); @@ -182,19 +208,19 @@ describe('sorting settings', () => { it('replace with other', () => { const settings = singleUpdateSettings( - [{ column_id: 0, direction: SortDirection.Ascending }], - { column_id: 1, direction: SortDirection.Ascending } + [{ column_id: 'a', direction: SortDirection.Ascending }], + { column_id: 'b', direction: SortDirection.Ascending } ); expect(settings.length).to.equal(1); - expect(settings[0].column_id).to.equal(1); + expect(settings[0].column_id).to.equal('b'); expect(settings[0].direction).to.equal(SortDirection.Ascending); }); it('replace with None', () => { const settings = singleUpdateSettings( - [{ column_id: 0, direction: SortDirection.Ascending }], - { column_id: 1, direction: SortDirection.None } + [{ column_id: 'a', direction: SortDirection.Ascending }], + { column_id: 'b', direction: SortDirection.None } ); expect(settings.length).to.equal(0); @@ -203,28 +229,28 @@ describe('sorting settings', () => { describe('multi columns sorting', () => { it('new descending', () => { - const settings = multiUpdateSettings([], { column_id: 0, direction: SortDirection.Descending }); + const settings = multiUpdateSettings([], { column_id: 'a', direction: SortDirection.Descending }); expect(settings.length).to.equal(1); - expect(settings[0].column_id).to.equal(0); + expect(settings[0].column_id).to.equal('a'); expect(settings[0].direction).to.equal(SortDirection.Descending); }); it('update to descending', () => { const settings = multiUpdateSettings( - [{ column_id: 0, direction: SortDirection.Ascending }], - { column_id: 0, direction: SortDirection.Descending } + [{ column_id: 'a', direction: SortDirection.Ascending }], + { column_id: 'a', direction: SortDirection.Descending } ); expect(settings.length).to.equal(1); - expect(settings[0].column_id).to.equal(0); + expect(settings[0].column_id).to.equal('a'); expect(settings[0].direction).to.equal(SortDirection.Descending); }); it('remove by setting to None', () => { const settings = multiUpdateSettings( - [{ column_id: 0, direction: SortDirection.Ascending }], - { column_id: 0, direction: SortDirection.None } + [{ column_id: 'a', direction: SortDirection.Ascending }], + { column_id: 'a', direction: SortDirection.None } ); expect(settings.length).to.equal(0); @@ -232,36 +258,36 @@ describe('sorting settings', () => { it('respects order', () => { const settings = multiUpdateSettings( - [{ column_id: 0, direction: SortDirection.Ascending }], - { column_id: 1, direction: SortDirection.Ascending } + [{ column_id: 'a', direction: SortDirection.Ascending }], + { column_id: 'b', direction: SortDirection.Ascending } ); expect(settings.length).to.equal(2); - expect(settings[0].column_id).to.equal(0); - expect(settings[1].column_id).to.equal(1); + expect(settings[0].column_id).to.equal('a'); + expect(settings[1].column_id).to.equal('b'); }); it('respects order when removed and added back', () => { - let settings: SortSettings = [{ column_id: 0, direction: SortDirection.Ascending }]; + let settings: SortSettings = [{ column_id: 'a', direction: SortDirection.Ascending }]; settings = multiUpdateSettings( settings, - { column_id: 1, direction: SortDirection.Ascending } + { column_id: 'b', direction: SortDirection.Ascending } ); settings = multiUpdateSettings( settings, - { column_id: 0, direction: SortDirection.None } + { column_id: 'a', direction: SortDirection.None } ); settings = multiUpdateSettings( settings, - { column_id: 0, direction: SortDirection.Ascending } + { column_id: 'a', direction: SortDirection.Ascending } ); expect(settings.length).to.equal(2); - expect(settings[0].column_id).to.equal(1); - expect(settings[1].column_id).to.equal(0); + expect(settings[0].column_id).to.equal('b'); + expect(settings[1].column_id).to.equal('a'); }); }); }); \ No newline at end of file diff --git a/tests/dash/app_editor_with_configurable_options.py b/tests/dash/app_editor_with_configurable_options.py deleted file mode 100644 index 915d22829..000000000 --- a/tests/dash/app_editor_with_configurable_options.py +++ /dev/null @@ -1,152 +0,0 @@ -import dash -import dash_core_components as dcc -import dash_html_components as html -from dash.dependencies import Input, Output -import pandas as pd -import pprint - -import dash_table -from index import app - -sanitized_name = __name__.replace(".", "-") - -df = pd.read_csv("./datasets/gapminder.csv") -df_small = pd.read_csv("./datasets/gapminder-small.csv") - - -CONTROLS = [ - html.Label("Dataset"), - dcc.RadioItems( - id="dataset", - options=[{"label": i, "value": i} for i in ["1700 Rows", "13 Rows"]], - value="13 Rows", - ), - html.Label("Editable"), - dcc.RadioItems( - id="editable", - options=[{"label": str(i), "value": i} for i in [True, False]], - value=True, - ), - html.Label("Sorting"), - dcc.RadioItems( - id="sorting", - options=[{"label": str(i), "value": i} for i in ["fe", True, False]], - value=True, - ), - html.Label("Sorting Type"), - dcc.RadioItems( - id="sorting_type", - options=[{"label": str(i), "value": i} for i in ["single", "multi"]], - value="single", - ), - html.Label("Sorting Treat Empty String As None"), - dcc.RadioItems( - id="sorting_treat_empty_string_as_none", - options=[{"label": str(i), "value": i} for i in [True, False]], - value=True, - ), - html.Label("Row Selectable"), - dcc.RadioItems( - id="row_selectable", - options=[{"label": str(i), "value": i} for i in ["single", "multi", False]], - value="single", - ), - html.Label("Pagination Mode"), - dcc.RadioItems( - id="pagination_mode", - options=[{"label": str(i), "value": i} for i in ["fe", True, False]], - value="fe", - ), - html.Label("Number of Fixed Rows"), - html.Div(dcc.Input(id="fixed_rows", type="number", value="0")), - html.Label("Number of Fixed Columns"), - html.Div(dcc.Input(id="fixed_columns", type="number", value="0")), -] - - -def layout(): - return html.Div( - className="row", - children=[ - html.Div(className="four columns", children=html.Div(id="table-container")), - html.Div( - className="four columns", - children=html.Div( - [ - html.Div( - CONTROLS, - style={"maxHeight": "100vh", "overflowY": "scroll"}, - ) - ] - ), - ), - html.Div( - className="four columns", - children=html.Div( - id="output", style={"maxHeight": "100vh", "overflowY": "scroll"} - ), - ), - ], - ) - - -@app.callback( - Output("table-container", "children"), - [ - Input(prop, "value") - for prop in [ - "editable", # 0 - "sorting", # 1 - "sorting_type", # 2 - "sorting_treat_empty_string_as_none", # 3 - "row_selectable", # 4 - "pagination_mode", # 5 - "fixed_rows", # 6 - "fixed_columns", # 7 - "dataset", - ] - ], -) -def update_table(*args): - if args[8] == "1700 Rows": - rows = df.to_dict("rows") - else: - rows = df_small.to_dict("rows") - return dash_table.DataTable( - id=sanitized_name, - columns=[{"name": i, "id": i} for i in df.columns], - data=rows, - editable=args[0], - sorting=args[1], - sorting_type=args[2], - sorting_treat_empty_string_as_none=args[3], - row_selectable=args[4], - pagination_mode=args[5], - fixed_rows=args[6], - fixed_columns=args[7], - ) - - -AVAILABLE_PROPERTIES = dash_table.DataTable(id="req").available_properties - - -@app.callback( - Output("output", "children"), - [Input(sanitized_name, prop) for prop in AVAILABLE_PROPERTIES], -) -def display_propeties(*args): - return html.Div( - [ - html.Details( - open=True, - children=[ - html.Summary(html.Code(prop)), - html.Pre( - str(pprint.pformat(args[i])), - style={"maxHeight": 200, "overflowY": "scroll"}, - ), - ], - ) - for (i, prop) in enumerate(AVAILABLE_PROPERTIES) - ] - ) diff --git a/tests/visual/percy-storybook/DashTable.percy.tsx b/tests/visual/percy-storybook/DashTable.percy.tsx index 8c9ae7189..6c30578ae 100644 --- a/tests/visual/percy-storybook/DashTable.percy.tsx +++ b/tests/visual/percy-storybook/DashTable.percy.tsx @@ -259,7 +259,7 @@ storiesOf('DashTable/Sorting', module) columns={mergedColumns} sorting={true} sort_by={[{ column_id: 'a', direction: 'asc' }]} - sorting_treat_empty_string_as_none={true} + sort_as_none={['']} style_data_conditional={style_data_conditional} />)) .add('"a" descending -- empty string override', () => ()) + .add(`"a" descending -- '' & 426 override`, () => ()) + .add(`"a" ascending -- '' and 426 override`, () => ()); storiesOf('DashTable/Without id', module) From fa7e455cda17dee521d1e5d0d67cf3c6b524780c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Rivet?= Date: Tue, 4 Jun 2019 19:07:36 -0400 Subject: [PATCH 27/46] Page, filter, sort props refactoring --- demo/App.js | 4 +- demo/AppMode.ts | 29 +-- generator/cssPropertiesGenerator.js | 12 +- package.json | 2 +- src/core/sorting/index.ts | 10 +- src/core/sorting/multi.ts | 28 +-- src/core/sorting/single.ts | 14 +- .../components/ControlledTable/index.tsx | 24 +- src/dash-table/components/EdgeFactory.tsx | 16 +- src/dash-table/components/FilterFactory.tsx | 12 +- src/dash-table/components/HeaderFactory.tsx | 12 +- src/dash-table/components/Table/index.tsx | 67 +++--- src/dash-table/components/Table/props.ts | 52 +++-- src/dash-table/conditional/index.ts | 6 +- src/dash-table/dash/DataTable.js | 71 +++--- src/dash-table/dash/sanitize.ts | 10 +- src/dash-table/dash/validate.ts | 21 +- src/dash-table/derived/cell/dropdowns.ts | 4 +- src/dash-table/derived/data/viewport.ts | 31 ++- src/dash-table/derived/data/virtual.ts | 21 +- .../derived/edges/operationOfFilters.ts | 4 +- src/dash-table/derived/header/content.tsx | 47 ++-- src/dash-table/derived/paginator.ts | 64 +++--- src/dash-table/derived/style/index.ts | 6 +- src/dash-table/derived/table/index.tsx | 6 +- tests/cypress/dash/v_be_page.py | 22 +- tests/cypress/dash/v_copy_paste.py | 2 +- tests/cypress/dash/v_fe_page.py | 12 +- .../tests/unit/derivedViewport_test.ts | 21 +- tests/cypress/tests/unit/sorting_test.ts | 32 +-- tests/dash/app_dataframe_backend_paging.py | 205 +++++++++--------- tests/dash/app_dataframe_updating_graph_fe.py | 10 +- .../percy-storybook/Border.style.percy.tsx | 35 +-- .../percy-storybook/DashTable.percy.tsx | 17 +- .../visual/percy-storybook/Dropdown.percy.tsx | 6 +- .../visual/percy-storybook/Filters.percy.tsx | 3 +- tests/visual/percy-storybook/Style.percy.tsx | 4 +- .../percy-storybook/TriadValidation.percy.tsx | 17 +- .../percy-storybook/Virtualization.percy.tsx | 3 +- .../percy-storybook/Width.empty.percy.tsx | 11 +- tslint.json | 6 +- 41 files changed, 486 insertions(+), 493 deletions(-) diff --git a/demo/App.js b/demo/App.js index 07dc6acc2..6c189180d 100644 --- a/demo/App.js +++ b/demo/App.js @@ -40,7 +40,7 @@ class App extends Component { className='clear-filters' onClick={() => { const tableProps = R.clone(this.state.tableProps); - tableProps.filter = ''; + tableProps.filter_query = ''; this.setState({ tableProps }); }} @@ -53,7 +53,7 @@ class App extends Component { } onBlur={e => { const tableProps = R.clone(this.state.tableProps); - tableProps.filter = e.target.value; + tableProps.filter_query = e.target.value; this.setState({ tableProps }); }} /> diff --git a/demo/AppMode.ts b/demo/AppMode.ts index ac9f98bec..a5683732a 100644 --- a/demo/AppMode.ts +++ b/demo/AppMode.ts @@ -8,7 +8,8 @@ import { ChangeAction, ChangeFailure, IVisibleColumn, - ColumnType + ColumnType, + TableAction } from 'dash-table/components/Table/props'; import { TooltipSyntax } from 'dash-table/tooltips/props'; @@ -57,7 +58,7 @@ function getBaseTableProps(mock: IDataMock) { })) } }, - pagination_mode: false, + page_action: TableAction.None, style_table: { max_height: '800px', height: '800px', @@ -76,23 +77,23 @@ function getBaseTableProps(mock: IDataMock) { function getDefaultState( generateData: Function = generateMockData ): { - filter: string, + filter_query: string, tableProps: Partial } { const mock = generateData(5000); return { - filter: '', + filter_query: '', tableProps: R.merge(getBaseTableProps(mock), { data: mock.data, editable: true, - sorting: true, + sort_action: TableAction.Native, fixed_rows: { headers: true }, fixed_columns: { headers: true }, merge_duplicate_headers: false, row_deletable: true, row_selectable: 'single', - pagination_mode: 'fe' + page_action: TableAction.Native }) as Partial }; } @@ -111,7 +112,7 @@ function getReadonlyState() { function getSpaceInColumn() { const state = getDefaultState(generateSpaceMockData); - state.tableProps.filtering = true; + state.tableProps.filter_action = TableAction.Native; return state; } @@ -137,7 +138,7 @@ function getTooltipsState() { { type: TooltipSyntax.Markdown, value: `### Go Proverb\nSente gains nothing` }, { type: TooltipSyntax.Text, value: `Beware of going back to patch up` }, { type: TooltipSyntax.Text, value: `When in doubt, Tenuki` }, - `People in glass houses shouldn't throw stones` + `People in glass houses should not throw stones` ] }; state.tableProps.tooltip = { @@ -148,7 +149,7 @@ function getTooltipsState() { state.tableProps.tooltip_conditional = [{ if: { column_id: 'aaa-readonly', - filter: `{aaa} is prime` + filter_query: `{aaa} is prime` }, type: TooltipSyntax.Markdown, value: `### Go Proverbs\nCapture three to get an eye` @@ -216,7 +217,7 @@ function getDateState() { function getFilteringState() { const state = getDefaultState(); - state.tableProps.filtering = true; + state.tableProps.filter_action = TableAction.Native; return state; } @@ -225,11 +226,11 @@ function getVirtualizedState() { const mock = generateMockData(5000); return { - filter: '', + filter_query: '', tableProps: R.merge(getBaseTableProps(mock), { data: mock.data, editable: true, - sorting: true, + sort_action: TableAction.Native, merge_duplicate_headers: false, row_deletable: true, row_selectable: 'single', @@ -242,11 +243,11 @@ function getFixedVirtualizedState() { const mock = generateMockData(5000); return { - filter: '', + filter_query: '', tableProps: R.merge(getBaseTableProps(mock), { data: mock.data, editable: true, - sorting: true, + sort_action: TableAction.Native, fixed_rows: { headers: true }, fixed_columns: { headers: true }, merge_duplicate_headers: false, diff --git a/generator/cssPropertiesGenerator.js b/generator/cssPropertiesGenerator.js index 0ebffdc45..410c25fb8 100644 --- a/generator/cssPropertiesGenerator.js +++ b/generator/cssPropertiesGenerator.js @@ -302,7 +302,7 @@ camels.forEach(([camel]) => map.set(camel, camel)); const fs = require('fs'); -var stream1 = fs.createWriteStream("src/dash-table/derived/style/py2jsCssProperties.ts"); +var stream1 = fs.createWriteStream('src/dash-table/derived/style/py2jsCssProperties.ts'); stream1.once('open', () => { stream1.write('export type StyleProperty = string | number;\n'); stream1.write('\n'); @@ -317,12 +317,12 @@ stream1.once('open', () => { first = false; stream1.write(` ['${key}', '${value}']`); }); - stream1.write('\n]);') + stream1.write('\n]);'); stream1.end(); }); -var stream2 = fs.createWriteStream("src/dash-table/derived/style/IStyle.ts"); +var stream2 = fs.createWriteStream('src/dash-table/derived/style/IStyle.ts'); stream2.once('open', () => { stream2.write(`import { StyleProperty } from './ py2jsCssProperties';\n`); stream2.write('\n'); @@ -330,12 +330,12 @@ stream2.once('open', () => { camels.forEach(([key]) => { stream2.write(` ${key}: StyleProperty;\n`); }); - stream2.write('}') + stream2.write('}'); stream2.end(); }); -var stream3 = fs.createWriteStream("proptypes.js"); +var stream3 = fs.createWriteStream('proptypes.js'); stream3.once('open', () => { let first = true; map.forEach((value, key) => { @@ -350,7 +350,7 @@ stream3.once('open', () => { stream3.write(` ${key}: PropTypes.oneOfType([PropTypes.string, PropTypes.number])`); } }); - stream3.write('\n') + stream3.write('\n'); stream3.end(); }); \ No newline at end of file diff --git a/package.json b/package.json index eaa738710..9cada4040 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "private::host_dash8082": "python tests/cypress/dash/v_copy_paste.py", "private::host_dash8083": "python tests/cypress/dash/v_fe_page.py", "private::host_js": "http-server ./dash_table -c-1 --silent", - "private::lint:ts": "tslint '{src,demo,tests}/**/*.{js,ts,tsx}' --exclude '**/@Types/*.*'", + "private::lint:ts": "tslint --project tsconfig.json --config tslint.json", "private::lint:py": "flake8 --exclude=DataTable.py,__init__.py,_imports_.py dash_table", "private::wait_dash8081": "wait-on http://localhost:8081", "private::wait_dash8082": "wait-on http://localhost:8082", diff --git a/src/core/sorting/index.ts b/src/core/sorting/index.ts index c90b0a413..4dd2473bd 100644 --- a/src/core/sorting/index.ts +++ b/src/core/sorting/index.ts @@ -2,7 +2,7 @@ import * as R from 'ramda'; import { ColumnId } from 'dash-table/components/Table/props'; -export interface ISortSetting { +export interface ISortBy { column_id: ColumnId; direction: SortDirection; } @@ -13,11 +13,11 @@ export enum SortDirection { None = 'none' } -export type SortSettings = ISortSetting[]; +export type SortBy = ISortBy[]; type IsNullyFn = (value: any, id: string) => boolean; export const defaultIsNully: IsNullyFn = (value: any, _: string) => R.isNil(value); -export default (data: any[], settings: SortSettings, isNully: IsNullyFn = defaultIsNully): any[] => { - if (!settings.length) { +export default (data: any[], sortBy: SortBy, isNully: IsNullyFn = defaultIsNully): any[] => { + if (!sortBy.length) { return data; } @@ -52,7 +52,7 @@ export default (data: any[], settings: SortSettings, isNully: IsNullyFn = defaul return prop1 < prop2; }); - }, settings), + }, sortBy), data ); }; \ No newline at end of file diff --git a/src/core/sorting/multi.ts b/src/core/sorting/multi.ts index c02a9682d..b254ebce6 100644 --- a/src/core/sorting/multi.ts +++ b/src/core/sorting/multi.ts @@ -1,31 +1,31 @@ import * as R from 'ramda'; import Logger from 'core/Logger'; -import { SortSettings, ISortSetting, SortDirection } from 'core/sorting'; +import { SortBy, ISortBy, SortDirection } from 'core/sorting'; export default ( - settings: SortSettings, - setting: ISortSetting -): SortSettings => { - Logger.trace('multi - updateSettings', settings, setting); + sortBy: SortBy, + sort: ISortBy +): SortBy => { + Logger.trace('multi - update sortBy', sortBy, sort); - settings = R.clone(settings); + sortBy = R.clone(sortBy); - if (setting.direction === SortDirection.None) { - const currentIndex = R.findIndex(s => s.column_id === setting.column_id, settings); + if (sort.direction === SortDirection.None) { + const currentIndex = R.findIndex(s => s.column_id === sort.column_id, sortBy); if (currentIndex !== -1) { - settings.splice(currentIndex, 1); + sortBy.splice(currentIndex, 1); } } else { - const currentSetting = R.find(s => s.column_id === setting.column_id, settings); + const current = R.find(s => s.column_id === sort.column_id, sortBy); - if (currentSetting) { - currentSetting.direction = setting.direction; + if (current) { + current.direction = sort.direction; } else { - settings.push(setting); + sortBy.push(sort); } } - return settings; + return sortBy; }; \ No newline at end of file diff --git a/src/core/sorting/single.ts b/src/core/sorting/single.ts index bbb1a2de5..d8a498731 100644 --- a/src/core/sorting/single.ts +++ b/src/core/sorting/single.ts @@ -1,13 +1,13 @@ import Logger from 'core/Logger'; -import { SortSettings, ISortSetting, SortDirection } from 'core/sorting'; +import { SortBy, ISortBy, SortDirection } from 'core/sorting'; export default ( - settings: SortSettings, - setting: ISortSetting -): SortSettings => { - Logger.trace('single - updateSettings', settings, setting); + sortBy: SortBy, + sort: ISortBy +): SortBy => { + Logger.trace('single - update sortBy', sortBy, sort); - return setting.direction === SortDirection.None ? + return sort.direction === SortDirection.None ? [] : - [setting]; + [sort]; }; \ No newline at end of file diff --git a/src/dash-table/components/ControlledTable/index.tsx b/src/dash-table/components/ControlledTable/index.tsx index 59ce6fa68..3f2021e16 100644 --- a/src/dash-table/components/ControlledTable/index.tsx +++ b/src/dash-table/components/ControlledTable/index.tsx @@ -18,7 +18,7 @@ import { memoizeOne } from 'core/memoizer'; import lexer from 'core/syntax-tree/lexer'; import TableClipboardHelper from 'dash-table/utils/TableClipboardHelper'; -import { ControlledTableProps, ICellFactoryProps } from 'dash-table/components/Table/props'; +import { ControlledTableProps, ICellFactoryProps, TableAction } from 'dash-table/components/Table/props'; import dropdownHelper from 'dash-table/components/dropdownHelper'; import derivedTable from 'dash-table/derived/table'; @@ -54,9 +54,9 @@ export default class ControlledTable extends PureComponent getLexerResult = memoizeOne(lexer.bind(undefined, queryLexicon)); get lexerResult() { - const { filter } = this.props; + const { filter_query } = this.props; - return this.getLexerResult(filter); + return this.getLexerResult(filter_query); } private updateStylesheet() { @@ -550,7 +550,7 @@ export default class ControlledTable extends PureComponent columns, data, editable, - filter, + filter_query, setProps, sort_by, viewport @@ -567,7 +567,7 @@ export default class ControlledTable extends PureComponent columns, data, true, - !sort_by.length || !filter.length + !sort_by.length || !filter_query.length ); if (result) { @@ -578,14 +578,14 @@ export default class ControlledTable extends PureComponent get displayPagination() { const { data, - pagination_mode, - pagination_settings + page_action, + page_size } = this.props; return ( - pagination_mode === 'fe' && - pagination_settings.page_size < data.length - ) || pagination_mode === 'be'; + page_action === TableAction.Native && + page_size < data.length + ) || page_action === TableAction.Custom; } loadNext = () => { @@ -685,7 +685,7 @@ export default class ControlledTable extends PureComponent tooltip_conditional, tooltip, currentTooltip, - filtering, + filter_action, fixed_columns, fixed_rows, scrollbarWidth, @@ -731,7 +731,7 @@ export default class ControlledTable extends PureComponent ...(empty[1][1] ? ['dash-empty-11'] : []), ...(columns.length ? [] : ['dash-no-columns']), ...(virtualized.data.length ? [] : ['dash-no-data']), - ...(filtering ? [] : ['dash-no-filter']) + ...(filter_action !== TableAction.None ? [] : ['dash-no-filter']) ]; const containerClasses = ['dash-spreadsheet-container', ...classes]; diff --git a/src/dash-table/components/EdgeFactory.tsx b/src/dash-table/components/EdgeFactory.tsx index 1bef05835..08da253d4 100644 --- a/src/dash-table/components/EdgeFactory.tsx +++ b/src/dash-table/components/EdgeFactory.tsx @@ -15,7 +15,7 @@ import getHeaderRows from 'dash-table/derived/header/headerRows'; import { derivedRelevantCellStyles, derivedRelevantFilterStyles, derivedRelevantHeaderStyles } from 'dash-table/derived/style'; import { Style, Cells, DataCells, BasicFilters, Headers } from 'dash-table/derived/style/props'; -import { ControlledTableProps, VisibleColumns, IViewportOffset, Data, ICellCoordinates } from './Table/props'; +import { ControlledTableProps, VisibleColumns, IViewportOffset, Data, ICellCoordinates, TableAction } from './Table/props'; import { SingleColumnSyntaxTree } from 'dash-table/syntax-tree'; type EdgesMatricesOp = EdgesMatrices | undefined; @@ -140,7 +140,7 @@ export default class EdgeFactory { const { active_cell, columns, - filtering, + filter_action, workFilter, fixed_columns, fixed_rows, @@ -162,7 +162,7 @@ export default class EdgeFactory { active_cell, columns, (row_deletable ? 1 : 0) + (row_selectable ? 1 : 0), - !!filtering, + filter_action !== TableAction.None, workFilter.map, fixed_columns, fixed_rows, @@ -184,7 +184,7 @@ export default class EdgeFactory { active_cell: ICellCoordinates, columns: VisibleColumns, operations: number, - filtering: boolean, + filter_action: boolean, filterMap: Map, fixed_columns: number, fixed_rows: number, @@ -242,7 +242,7 @@ export default class EdgeFactory { let filterEdges = this.getFilterEdges( columns, - filtering, + filter_action, filterMap, filterStyles, style_as_list_view @@ -250,7 +250,7 @@ export default class EdgeFactory { let filterOpEdges = this.getFilterOpEdges( operations, - filtering, + filter_action, filterStyles, style_as_list_view ); @@ -288,14 +288,14 @@ export default class EdgeFactory { this.vReconcile(dataOpEdges, dataEdges, cutoffWeight); if (fixed_rows === headerRows) { - if (filtering) { + if (filter_action) { this.hOverride(headerEdges, filterEdges, cutoffWeight); this.hOverride(headerOpEdges, filterOpEdges, cutoffWeight); } else { this.hOverride(headerEdges, dataEdges, cutoffWeight); this.hOverride(headerOpEdges, dataOpEdges, cutoffWeight); } - } else if (filtering && fixed_rows === headerRows + 1) { + } else if (filter_action && fixed_rows === headerRows + 1) { this.hOverride(filterEdges, dataEdges, cutoffWeight); this.hOverride(filterOpEdges, dataOpEdges, cutoffWeight); } diff --git a/src/dash-table/components/FilterFactory.tsx b/src/dash-table/components/FilterFactory.tsx index 0390f7a39..206bde1d4 100644 --- a/src/dash-table/components/FilterFactory.tsx +++ b/src/dash-table/components/FilterFactory.tsx @@ -7,7 +7,7 @@ import memoizerCache from 'core/cache/memoizer'; import { memoizeOne } from 'core/memoizer'; import ColumnFilter from 'dash-table/components/Filter/Column'; -import { ColumnId, Filtering, IVisibleColumn, VisibleColumns, RowSelection } from 'dash-table/components/Table/props'; +import { ColumnId, IVisibleColumn, VisibleColumns, RowSelection, TableAction } from 'dash-table/components/Table/props'; import derivedFilterStyles, { derivedFilterOpStyles } from 'dash-table/derived/filter/wrapperStyles'; import derivedHeaderOperations from 'dash-table/derived/header/operations'; import { derivedRelevantFilterStyles } from 'dash-table/derived/style'; @@ -18,15 +18,15 @@ import { IEdgesMatrices } from 'dash-table/derived/edges/type'; import { updateMap } from 'dash-table/derived/filter/map'; type SetFilter = ( - filter: string, + filter_query: string, rawFilter: string, map: Map ) => void; export interface IFilterOptions { columns: VisibleColumns; - filter: string; - filtering: Filtering; + filter_query: string; + filter_action: TableAction; id: string; map: Map; rawFilterQuery: string; @@ -106,7 +106,7 @@ export default class FilterFactory { ) { const { columns, - filtering, + filter_action, map, row_deletable, row_selectable, @@ -117,7 +117,7 @@ export default class FilterFactory { style_filter_conditional } = this.props; - if (!filtering) { + if (filter_action === TableAction.None) { return []; } diff --git a/src/dash-table/components/HeaderFactory.tsx b/src/dash-table/components/HeaderFactory.tsx index 59db68ddf..c7d1bfabe 100644 --- a/src/dash-table/components/HeaderFactory.tsx +++ b/src/dash-table/components/HeaderFactory.tsx @@ -38,13 +38,13 @@ export default class HeaderFactory { const { columns, merge_duplicate_headers, - pagination_mode, + page_action, row_deletable, row_selectable, setProps, - sorting, + sort_action, sort_by, - sorting_type, + sort_mode, style_cell, style_cell_conditional, style_header, @@ -92,10 +92,10 @@ export default class HeaderFactory { const content = this.headerContent( columns, labelsAndIndices, - sorting, - sorting_type, + sort_action, + sort_mode, sort_by, - pagination_mode, + page_action, setProps, props ); diff --git a/src/dash-table/components/Table/index.tsx b/src/dash-table/components/Table/index.tsx index a81e79c59..f53b5195f 100644 --- a/src/dash-table/components/Table/index.tsx +++ b/src/dash-table/components/Table/index.tsx @@ -23,7 +23,8 @@ import { SetProps, IState, StandaloneState, - SanitizedAndDerivedProps + SanitizedAndDerivedProps, + TableAction } from './props'; import 'react-select/dist/react-select.css'; @@ -42,10 +43,10 @@ export default class Table extends Component(), - props.filter, + props.filter_query, props.columns ) }, @@ -55,17 +56,17 @@ export default class Table extends Component { const { workFilter: { map: currentMap, value } } = state; - if (value !== nextProps.filter) { + if (value !== nextProps.filter_query) { const map = this.filterMap( currentMap, - nextProps.filter, + nextProps.filter_query, nextProps.columns ); @@ -102,12 +103,13 @@ export default class Table extends Component = {}; @@ -307,8 +312,8 @@ export default class Table extends Component filter); - private readonly paginationCache = memoizeOneWithFlag(pagination => pagination); + private readonly filterCache = memoizeOneWithFlag(filter_query => filter_query); + private readonly paginationCache = memoizeOneWithFlag((page_current, page_size) => [page_current, page_size]); private readonly sortCache = memoizeOneWithFlag(sort => sort); private readonly viewportCache = memoizeOneWithFlag(viewport => viewport); private readonly viewportSelectedRowsCache = memoizeOneWithFlag(viewport => viewport); diff --git a/src/dash-table/components/Table/props.ts b/src/dash-table/components/Table/props.ts index 452a4297d..60bb6419e 100644 --- a/src/dash-table/components/Table/props.ts +++ b/src/dash-table/components/Table/props.ts @@ -1,4 +1,4 @@ -import { SortSettings } from 'core/sorting'; +import { SortBy } from 'core/sorting'; import { IPaginator } from 'dash-table/derived/paginator'; import { Table, @@ -23,6 +23,17 @@ export enum ColumnType { Datetime = 'datetime' } +export enum SortMode { + Single = 'single', + Multi = 'multi' +} + +export enum TableAction { + Custom = 'custom', + Native = 'native', + None = 'none' +} + export interface IDerivedData { data: Data; indices: Indices; @@ -56,17 +67,13 @@ export type ColumnId = string; export type Columns = IColumn[]; export type Data = Datum[]; export type Datum = IDatumObject | any; -export type Filtering = 'fe' | 'be' | boolean; export type Indices = number[]; export type RowId = string | number; -export type PaginationMode = 'fe' | 'be' | boolean; export type RowSelection = 'single' | 'multi' | false; export type SelectedCells = ICellCoordinates[]; export type SetProps = (...args: any[]) => void; export type SetState = (state: Partial) => void; export type SortAsNone = (string | number | boolean)[]; -export type Sorting = 'fe' | 'be' | boolean; -export type SortingType = 'multi' | 'single'; export type VisibleColumns = IVisibleColumn[]; export enum ChangeAction { @@ -206,11 +213,6 @@ interface IStylesheetRule { rule: string; } -export interface IPaginationSettings { - current_page: number; - page_size: number; -} - export interface IUserInterfaceCell { height: number; } @@ -268,8 +270,8 @@ export interface IProps { css?: IStylesheetRule[]; data?: Data; editable?: boolean; - filter?: string; - filtering?: Filtering; + filter_query?: string; + filter_action?: TableAction; locale_format: INumberLocale; merge_duplicate_headers?: boolean; fixed_columns?: Fixed; @@ -280,13 +282,14 @@ export interface IProps { selected_rows?: Indices; selected_row_ids?: RowId[]; setProps?: SetProps; - sorting?: Sorting; - sort_by?: SortSettings; - sorting_type?: SortingType; + sort_action?: TableAction; + sort_by?: SortBy; + sort_mode?: SortMode; sort_as_none?: SortAsNone; style_as_list_view?: boolean; - pagination_mode?: PaginationMode; - pagination_settings?: IPaginationSettings; + page_action?: TableAction; + page_current?: number; + page_size: number; style_data?: Style; style_cell?: Style; @@ -310,8 +313,8 @@ interface IDefaultProps { css: IStylesheetRule[]; data: Data; editable: boolean; - filter: string; - filtering: Filtering; + filter_query: string; + filter_action: TableAction; merge_duplicate_headers: boolean; fixed_columns: Fixed; fixed_rows: Fixed; @@ -322,14 +325,15 @@ interface IDefaultProps { end_cell: ICellCoordinates; selected_rows: Indices; selected_row_ids: RowId[]; - sorting: Sorting; - sort_by: SortSettings; - sorting_type: SortingType; + sort_action: TableAction; + sort_by: SortBy; + sort_mode: SortMode; sort_as_none: SortAsNone; style_as_list_view: boolean; - pagination_mode: PaginationMode; - pagination_settings: IPaginationSettings; + page_action: TableAction; + page_current: number; + page_size: number; style_data: Style; style_cell: Style; diff --git a/src/dash-table/conditional/index.ts b/src/dash-table/conditional/index.ts index f575435e4..a61f412e4 100644 --- a/src/dash-table/conditional/index.ts +++ b/src/dash-table/conditional/index.ts @@ -2,7 +2,7 @@ import { ColumnId, Datum, ColumnType } from 'dash-table/components/Table/props'; import { QuerySyntaxTree } from 'dash-table/syntax-tree'; export interface IConditionalElement { - filter?: string; + filter_query?: string; } export interface IIndexedHeaderElement { @@ -68,6 +68,6 @@ export function ifHeaderIndex(condition: IIndexedHeaderElement | undefined, head export function ifFilter(condition: IConditionalElement | undefined, datum: Datum) { return !condition || - condition.filter === undefined || - ifAstFilter(new QuerySyntaxTree(condition.filter), datum); + condition.filter_query === undefined || + ifAstFilter(new QuerySyntaxTree(condition.filter_query), datum); } \ No newline at end of file diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index e2712d8dd..91cecffa6 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -31,18 +31,16 @@ export default class DataTable extends Component { } export const defaultProps = { - pagination_mode: 'fe', - pagination_settings: { - current_page: 0, - page_size: 250 - }, + page_action: 'native', + page_current: 0, + page_size: 250, css: [], - filter: '', - filtering: false, + filter_query: '', + filter_action: 'none', sort_as_none: [], - sorting: false, - sorting_type: 'single', + sort_action: 'none', + sort_mode: 'single', sort_by: [], style_as_list_view: false, @@ -564,7 +562,7 @@ export const propTypes = { * in the table (e.g. page through `10,000` rows in `data` `100` rows at a time) * or we can update the data on-the-fly with callbacks * when the user clicks on the "Previous" or "Next" buttons. - * These modes can be toggled with this `pagination_mode` parameter: + * These modes can be toggled with this `page_action` parameter: * - `'fe'` refers to "front-end" paging: passing large data up-front * - `'be'` refers to "back-end" paging: updating the data on the fly via callbacks * - `False` will disable paging, attempting to render all of the data at once @@ -573,21 +571,20 @@ export const propTypes = { * NOTE: The `fe` and `be` names may change in the future. * Tune in to [https://github.com/plotly/dash-table/issues/167](https://github.com/plotly/dash-table/issues/167) for more. */ - pagination_mode: PropTypes.oneOf(['fe', 'be', true, false]), + page_action: PropTypes.oneOf(['custom', 'native', 'none']), /** - * `pagination_settings` controls the pagination settings - * _and_ represents the current state of the pagination UI. - * - `page_size` represents the number of rows that will be - * displayed on a particular page. - * - `current_page` represents which page the user is on. + * `page_current` represents which page the user is on. * Use this property to index through data in your callbacks with * backend paging. */ - pagination_settings: PropTypes.exact({ - current_page: PropTypes.number.isRequired, - page_size: PropTypes.number.isRequired - }), + page_current: PropTypes.number, + + /** + * `page_size` represents the number of rows that will be + * displayed on a particular page. + */ + page_size: PropTypes.number, /** * `dropdown` represents the available dropdown @@ -627,7 +624,7 @@ export const propTypes = { clearable: PropTypes.bool, if: PropTypes.exact({ column_id: PropTypes.string, - filter: PropTypes.string + filter_query: PropTypes.string }), dropdown: PropTypes.arrayOf(PropTypes.exact({ label: PropTypes.string.isRequired, @@ -715,7 +712,7 @@ export const propTypes = { * ID that must be matched. * The `if` nested property `row_index` refers to the index * of the row in the source `data`. - * The `if` nested property `filter` refers to the query that + * The `if` nested property `filter_query` refers to the query that * must evaluate to True. * * The `type` refers to the type of tooltip syntax used @@ -737,7 +734,7 @@ export const propTypes = { duration: PropTypes.number, if: PropTypes.exact({ column_id: PropTypes.string, - filter: PropTypes.string, + filter_query: PropTypes.string, row_index: PropTypes.oneOfType([ PropTypes.number, PropTypes.oneOf([ @@ -812,36 +809,36 @@ export const propTypes = { tooltip_duration: PropTypes.number, /** - * If `filtering` is enabled, then the current filtering - * string is represented in this `filter` + * If `filter_action` is enabled, then the current filtering + * string is represented in this `filter_query` * property. * NOTE: The shape and structure of this property will * likely change in the future. * Stay tuned in [https://github.com/plotly/dash-table/issues/169](https://github.com/plotly/dash-table/issues/169) */ - filter: PropTypes.string, + filter_query: PropTypes.string, /** - * The `filtering` property controls the behavior of the `filtering` UI. + * The `filter_action` property controls the behavior of the `filtering` UI. * If `False`, then the filtering UI is not displayed * If `fe` or True, then the filtering UI is displayed and the filtering * happens in the "front-end". That is, it is performed on the data * that exists in the `data` property. * If `be`, then the filtering UI is displayed but it is the * responsibility of the developer to program the filtering - * through a callback (where `filter` would be the input + * through a callback (where `filter_query` would be the input * and `data` would be the output). * * NOTE - Several aspects of filtering may change in the future, * including the naming of this property. * Tune in to [https://github.com/plotly/dash-table/issues/167](https://github.com/plotly/dash-table/issues/167) */ - filtering: PropTypes.oneOf(['fe', 'be', true, false]), + filter_action: PropTypes.oneOf(['custom', 'native', 'none']), /** - * The `sorting` property enables data to be + * The `sort_action` property enables data to be * sorted on a per-column basis. - * Enabling `sorting` will display a UI element + * Enabling `sort_action` will display a UI element * on each of the columns (up and down arrows). * * Sorting can be performed in the "front-end" @@ -850,7 +847,7 @@ export const propTypes = { * Clicking on the sort arrows will update the * `sort_by` property. */ - sorting: PropTypes.oneOf(['fe', 'be', true, false]), + sort_action: PropTypes.oneOf(['custom', 'native', 'none']), /** * Sorting can be performed across multiple columns @@ -862,7 +859,7 @@ export const propTypes = { * the columns were sorted through the UI. * See [https://github.com/plotly/dash-table/issues/170](https://github.com/plotly/dash-table/issues/170) */ - sorting_type: PropTypes.oneOf(['single', 'multi']), + sort_mode: PropTypes.oneOf(['single', 'multi']), /** * `sort_by` describes the current state @@ -952,7 +949,7 @@ export const propTypes = { if: PropTypes.exact({ column_id: PropTypes.string, column_type: PropTypes.oneOf(['any', 'numeric', 'text', 'datetime']), - filter: PropTypes.string, + filter_query: PropTypes.string, row_index: PropTypes.oneOfType([ PropTypes.number, PropTypes.oneOf(['odd', 'even']) @@ -1000,7 +997,7 @@ export const propTypes = { /** * This property represents the current structure of - * `filter` as a tree structure. Each node of the + * `filter_query` as a tree structure. Each node of the * query structure have: * - type (string; required) * - 'open-block' @@ -1022,10 +1019,10 @@ export const propTypes = { * - left (nested query structure; optional) * - right (nested query structure; optional) * - * If the query is invalid or empty, the `derived_filter_structure` will + * If the query is invalid or empty, the `derived_filter_query_structure` will * be null. */ - derived_filter_structure: PropTypes.object, + derived_filter_query_structure: PropTypes.object, /** * This property represents the current state of `data` diff --git a/src/dash-table/dash/sanitize.ts b/src/dash-table/dash/sanitize.ts index fe9f235e5..6d0204ae8 100644 --- a/src/dash-table/dash/sanitize.ts +++ b/src/dash-table/dash/sanitize.ts @@ -5,14 +5,14 @@ import { memoizeOne } from 'core/memoizer'; import { Columns, ColumnType, - Filtering, Fixed, IColumn, INumberLocale, PropsWithDefaults, RowSelection, SanitizedProps, - SortAsNone + SortAsNone, + TableAction } from 'dash-table/components/Table/props'; import headerRows from 'dash-table/derived/header/headerRows'; @@ -58,10 +58,10 @@ const getFixedColumns = ( const getFixedRows = ( fixed: Fixed, columns: IColumn[], - filter: Filtering + filter_action: TableAction ) => !fixed.headers ? 0 : - headerRows(columns) + (filter ? 1 : 0) + data2number(fixed.data); + headerRows(columns) + (filter_action !== TableAction.None ? 1 : 0) + data2number(fixed.data); export default (props: PropsWithDefaults): SanitizedProps => { const locale_format = applyDefaultToLocale(props.locale_format); @@ -69,7 +69,7 @@ export default (props: PropsWithDefaults): SanitizedProps => { return R.merge(props, { columns: applyDefaultsToColumns(locale_format, props.sort_as_none, props.columns), fixed_columns: getFixedColumns(props.fixed_columns, props.row_deletable, props.row_selectable), - fixed_rows: getFixedRows(props.fixed_rows, props.columns, props.filtering), + fixed_rows: getFixedRows(props.fixed_rows, props.columns, props.filter_action), locale_format }); }; diff --git a/src/dash-table/dash/validate.ts b/src/dash-table/dash/validate.ts index 6fc29134e..0bf444ffe 100644 --- a/src/dash-table/dash/validate.ts +++ b/src/dash-table/dash/validate.ts @@ -1,13 +1,14 @@ import * as R from 'ramda'; import Logger from 'core/Logger'; +import { TableAction } from 'dash-table/components/Table/props'; -function isFrontEnd(value: any) { - return ['fe', true, false].indexOf(value) !== -1; +function isFrontEnd(value: TableAction) { + return value !== TableAction.Custom; } -function isBackEnd(value: any) { - return ['be', false].indexOf(value) !== -1; +function isBackEnd(value: TableAction) { + return value !== TableAction.Native; } function validColumns(props: any) { @@ -32,18 +33,18 @@ function validColumns(props: any) { function validFSP(props: any) { const { - filtering, - sorting, - pagination_mode + filter_action, + sort_action, + page_action } = props; - return isFrontEnd(pagination_mode) || - (isBackEnd(filtering) && isBackEnd(sorting)); + return isFrontEnd(page_action) || + (isBackEnd(filter_action) && isBackEnd(sort_action)); } export default (props: any): boolean => { if (!validFSP(props)) { - Logger.error(`Invalid combination of filtering / sorting / pagination`); + Logger.error(`Invalid combination of filter_action / sort_action / page_action`); return false; } diff --git a/src/dash-table/derived/cell/dropdowns.ts b/src/dash-table/derived/cell/dropdowns.ts index 08b79b2fc..2b13101ca 100644 --- a/src/dash-table/derived/cell/dropdowns.ts +++ b/src/dash-table/derived/cell/dropdowns.ts @@ -68,9 +68,9 @@ class Dropdowns { ifColumnId(cd.if, column.id) && ( R.isNil(cd.if) || - R.isNil(cd.if.filter) || + R.isNil(cd.if.filter_query) || this.evaluation.get(column.id, i)( - this.ast.get(column.id, i)(cd.if.filter), + this.ast.get(column.id, i)(cd.if.filter_query), datum ) ), diff --git a/src/dash-table/derived/data/viewport.ts b/src/dash-table/derived/data/viewport.ts index 6257d12d0..c60cf05f4 100644 --- a/src/dash-table/derived/data/viewport.ts +++ b/src/dash-table/derived/data/viewport.ts @@ -4,21 +4,20 @@ import { lastPage } from 'dash-table/derived/paginator'; import { Data, Indices, - IPaginationSettings, - PaginationMode, - IDerivedData + IDerivedData, + TableAction } from 'dash-table/components/Table/props'; function getNoPagination(data: Data, indices: Indices): IDerivedData { return { data, indices }; } -function getFrontEndPagination(settings: IPaginationSettings, data: Data, indices: Indices): IDerivedData { - let currentPage = Math.min(settings.current_page, lastPage(data, settings)); +function getFrontEndPagination(page_current: number, page_size: number, data: Data, indices: Indices): IDerivedData { + let currentPage = Math.min(page_current, lastPage(data, page_size)); - const firstIndex = settings.page_size * currentPage; + const firstIndex = page_size * currentPage; const lastIndex = Math.min( - firstIndex + settings.page_size, + firstIndex + page_size, data.length ); @@ -33,21 +32,21 @@ function getBackEndPagination(data: Data, indices: Indices): IDerivedData { } const getter = ( - pagination_mode: PaginationMode, - pagination_settings: IPaginationSettings, + page_action: TableAction, + page_current: number, + page_size: number, data: Data, indices: Indices ): IDerivedData => { - switch (pagination_mode) { - case false: + switch (page_action) { + case TableAction.None: return getNoPagination(data, indices); - case true: - case 'fe': - return getFrontEndPagination(pagination_settings, data, indices); - case 'be': + case TableAction.Native: + return getFrontEndPagination(page_current, page_size, data, indices); + case TableAction.Custom: return getBackEndPagination(data, indices); default: - throw new Error(`Unknown pagination mode: '${pagination_mode}'`); + throw new Error(`Unknown pagination mode: '${page_action}'`); } }; diff --git a/src/dash-table/derived/data/virtual.ts b/src/dash-table/derived/data/virtual.ts index 93ddd8f99..0c25b2d02 100644 --- a/src/dash-table/derived/data/virtual.ts +++ b/src/dash-table/derived/data/virtual.ts @@ -1,34 +1,33 @@ import * as R from 'ramda'; import { memoizeOneFactory } from 'core/memoizer'; -import sort, { SortSettings } from 'core/sorting'; +import sort, { SortBy } from 'core/sorting'; import { ColumnId, Data, Datum, - Filtering, IDerivedData, SortAsNone, - Sorting, - VisibleColumns + VisibleColumns, + TableAction } from 'dash-table/components/Table/props'; import { QuerySyntaxTree } from 'dash-table/syntax-tree'; const getter = ( columns: VisibleColumns, data: Data, - filtering: Filtering, - filter: string, - sorting: Sorting, - sort_by: SortSettings = [] + filter_action: TableAction, + filter_query: string, + sort_action: TableAction, + sort_by: SortBy = [] ): IDerivedData => { const map = new Map(); R.addIndex(R.forEach)((datum, index) => { map.set(datum, index); }, data); - if (filtering === 'fe' || filtering === true) { - const tree = new QuerySyntaxTree(filter); + if (filter_action === TableAction.Native) { + const tree = new QuerySyntaxTree(filter_query); data = tree.isValid ? tree.filter(data) : @@ -48,7 +47,7 @@ const getter = ( columnId: ColumnId ) => R.isNil(value) || R.contains(value, getNullyCases(columnId)); - if (sorting === 'fe' || sorting === true) { + if (sort_action === TableAction.Native) { data = sort(data, sort_by, isNully); } diff --git a/src/dash-table/derived/edges/operationOfFilters.ts b/src/dash-table/derived/edges/operationOfFilters.ts index 5098262b1..12964d3d0 100644 --- a/src/dash-table/derived/edges/operationOfFilters.ts +++ b/src/dash-table/derived/edges/operationOfFilters.ts @@ -30,11 +30,11 @@ const getWeightedStyle = ( export default memoizeOneFactory(( columns: number, - filtering: boolean, + filter_action: boolean, borderStyles: IConvertedStyle[], listViewStyle: boolean ) => { - if (!filtering || columns === 0) { + if (!filter_action || columns === 0) { return; } diff --git a/src/dash-table/derived/header/content.tsx b/src/dash-table/derived/header/content.tsx index d2ae2b61b..c0a4c8d21 100644 --- a/src/dash-table/derived/header/content.tsx +++ b/src/dash-table/derived/header/content.tsx @@ -2,19 +2,18 @@ import * as R from 'ramda'; import React from 'react'; import { memoizeOneFactory } from 'core/memoizer'; -import { SortDirection, SortSettings } from 'core/sorting'; -import multiUpdateSettings from 'core/sorting/multi'; -import singleUpdateSettings from 'core/sorting/single'; +import { SortDirection, SortBy } from 'core/sorting'; +import multiUpdate from 'core/sorting/multi'; +import singleUpdate from 'core/sorting/single'; import { ColumnId, - PaginationMode, - SortingType, + SortMode, VisibleColumns, IVisibleColumn, SetProps, ControlledTableProps, - Sorting + TableAction } from 'dash-table/components/Table/props'; import * as actions from 'dash-table/utils/actions'; @@ -24,10 +23,10 @@ function deleteColumn(column: IVisibleColumn, columns: VisibleColumns, columnRow }; } -function doSort(columnId: ColumnId, sortSettings: SortSettings, sortType: SortingType, setProps: SetProps) { +function doSort(columnId: ColumnId, sortBy: SortBy, mode: SortMode, setProps: SetProps) { return () => { let direction: SortDirection; - switch (getSorting(columnId, sortSettings)) { + switch (getSorting(columnId, sortBy)) { case SortDirection.Descending: direction = SortDirection.None; break; @@ -42,13 +41,13 @@ function doSort(columnId: ColumnId, sortSettings: SortSettings, sortType: Sortin break; } - const sortingStrategy = sortType === 'single' ? - singleUpdateSettings : - multiUpdateSettings; + const sortingStrategy = mode === SortMode.Single ? + singleUpdate : + multiUpdate; setProps({ sort_by: sortingStrategy( - sortSettings, + sortBy, { column_id: columnId, direction } ), ...actions.clearSelection @@ -62,14 +61,14 @@ function editColumnName(column: IVisibleColumn, columns: VisibleColumns, columnR }; } -function getSorting(columnId: ColumnId, settings: SortSettings): SortDirection { - const setting = R.find(s => s.column_id === columnId, settings); +function getSorting(columnId: ColumnId, sortBy: SortBy): SortDirection { + const setting = R.find(s => s.column_id === columnId, sortBy); return setting ? setting.direction : SortDirection.None; } -function getSortingIcon(columnId: ColumnId, sortSettings: SortSettings) { - switch (getSorting(columnId, sortSettings)) { +function getSortingIcon(columnId: ColumnId, sortBy: SortBy) { + switch (getSorting(columnId, sortBy)) { case SortDirection.Descending: return '↓'; case SortDirection.Ascending: @@ -83,10 +82,10 @@ function getSortingIcon(columnId: ColumnId, sortSettings: SortSettings) { function getter( columns: VisibleColumns, labelsAndIndices: R.KeyValuePair[], - sorting: Sorting, - sortType: SortingType, - sortSettings: SortSettings, - paginationMode: PaginationMode, + sort_action: TableAction, + mode: SortMode, + sortBy: SortBy, + paginationMode: TableAction, setProps: SetProps, options: ControlledTableProps ): JSX.Element[][] { @@ -102,19 +101,19 @@ function getter( column.renamable : !!column.renamable && column.renamable[headerRowIndex]; - const deletable = paginationMode !== 'be' && ( + const deletable = paginationMode !== TableAction.Custom && ( typeof column.deletable === 'boolean' ? column.deletable : !!column.deletable && column.deletable[headerRowIndex] ); return (
- {sorting && isLastRow ? + {sort_action !== TableAction.None && isLastRow ? ( - {getSortingIcon(column.id, sortSettings)} + {getSortingIcon(column.id, sortBy)} ) : '' } diff --git a/src/dash-table/derived/paginator.ts b/src/dash-table/derived/paginator.ts index da0a8a343..8f3671eb6 100644 --- a/src/dash-table/derived/paginator.ts +++ b/src/dash-table/derived/paginator.ts @@ -1,14 +1,11 @@ -import { merge } from 'ramda'; - import { memoizeOneFactory } from 'core/memoizer'; import { clearSelection } from 'dash-table/utils/actions'; import { Data, - PaginationMode, SetProps, - IPaginationSettings + TableAction } from 'dash-table/components/Table/props'; export interface IPaginator { @@ -16,59 +13,54 @@ export interface IPaginator { loadPrevious(): void; } -export function lastPage(data: Data, settings: IPaginationSettings) { - return Math.max(Math.ceil(data.length / settings.page_size) - 1, 0); +export function lastPage(data: Data, page_size: number) { + return Math.max(Math.ceil(data.length / page_size) - 1, 0); } function getBackEndPagination( - pagination_settings: IPaginationSettings, + page_current: number, setProps: SetProps ): IPaginator { return { loadNext: () => { - pagination_settings.current_page++; - setProps({ pagination_settings, ...clearSelection }); + page_current++; + setProps({ page_current, ...clearSelection }); }, loadPrevious: () => { - if (pagination_settings.current_page <= 0) { + if (page_current <= 0) { return; } - pagination_settings.current_page--; - setProps({ pagination_settings, ...clearSelection }); + page_current--; + setProps({ page_current, ...clearSelection }); } }; } function getFrontEndPagination( - pagination_settings: IPaginationSettings, + page_current: number, + page_size: number, setProps: SetProps, data: Data ) { return { loadNext: () => { - const maxPageIndex = lastPage(data, pagination_settings); + const maxPageIndex = lastPage(data, page_size); - if (pagination_settings.current_page >= maxPageIndex) { + if (page_current >= maxPageIndex) { return; } - pagination_settings = merge(pagination_settings, { - current_page: pagination_settings.current_page + 1 - }); - - setProps({ pagination_settings, ...clearSelection }); + page_current++; + setProps({ page_current, ...clearSelection }); }, loadPrevious: () => { - if (pagination_settings.current_page <= 0) { + if (page_current <= 0) { return; } - pagination_settings = merge(pagination_settings, { - current_page: pagination_settings.current_page - 1 - }); - - setProps({ pagination_settings, ...clearSelection }); + page_current--; + setProps({ page_current, ...clearSelection }); } }; } @@ -81,21 +73,21 @@ function getNoPagination() { } const getter = ( - pagination_mode: PaginationMode, - pagination_settings: IPaginationSettings, + page_action: TableAction, + page_current: number, + page_size: number, setProps: SetProps, data: Data ): IPaginator => { - switch (pagination_mode) { - case false: + switch (page_action) { + case TableAction.None: return getNoPagination(); - case true: - case 'fe': - return getFrontEndPagination(pagination_settings, setProps, data); - case 'be': - return getBackEndPagination(pagination_settings, setProps); + case TableAction.Native: + return getFrontEndPagination(page_current, page_size, setProps, data); + case TableAction.Custom: + return getBackEndPagination(page_current, setProps); default: - throw new Error(`Unknown pagination mode: '${pagination_mode}'`); + throw new Error(`Unknown pagination mode: '${page_action}'`); } }; diff --git a/src/dash-table/derived/style/index.ts b/src/dash-table/derived/style/index.ts index 61d6e6930..fc90ea4b9 100644 --- a/src/dash-table/derived/style/index.ts +++ b/src/dash-table/derived/style/index.ts @@ -49,7 +49,7 @@ function convertElement(style: GenericStyle): IConvertedStyle { !R.isNil(style.if.column_type) ), checksRow: () => !R.isNil(indexFilter), - checksFilter: () => !R.isNil(style.if) && !R.isNil(style.if.filter), + checksFilter: () => !R.isNil(style.if) && !R.isNil(style.if.filter_query), matchesColumn: (column: IVisibleColumn | undefined) => !style.if || ( @@ -65,8 +65,8 @@ function convertElement(style: GenericStyle): IConvertedStyle { !R.isNil(index) && (indexFilter === 'odd' ? index % 2 === 1 : index % 2 === 0), matchesFilter: (datum: Datum) => !style.if || - style.if.filter === undefined || - (ast = ast || new QuerySyntaxTree(style.if.filter)).evaluate(datum), + style.if.filter_query === undefined || + (ast = ast || new QuerySyntaxTree(style.if.filter_query)).evaluate(datum), style: convertStyle(style) }; } diff --git a/src/dash-table/derived/table/index.tsx b/src/dash-table/derived/table/index.tsx index 14417c49e..de8500994 100644 --- a/src/dash-table/derived/table/index.tsx +++ b/src/dash-table/derived/table/index.tsx @@ -14,12 +14,12 @@ import { SingleColumnSyntaxTree } from 'dash-table/syntax-tree'; const handleSetFilter = ( setProps: SetProps, setState: SetState, - filter: string, + filter_query: string, rawFilterQuery: string, map: Map ) => { - setProps({ filter, ...clearSelection }); - setState({ workFilter: { map, value: filter }, rawFilterQuery }); + setProps({ filter_query, ...clearSelection }); + setState({ workFilter: { map, value: filter_query }, rawFilterQuery }); }; function filterPropsFn(propsFn: () => ControlledTableProps, setFilter: any) { diff --git a/tests/cypress/dash/v_be_page.py b/tests/cypress/dash/v_be_page.py index fdb59f598..20775f7f6 100644 --- a/tests/cypress/dash/v_be_page.py +++ b/tests/cypress/dash/v_be_page.py @@ -28,11 +28,9 @@ dash_table.DataTable( id="table", data=[], - pagination_mode="be", - pagination_settings={ - "current_page": 0, - "page_size": 250, - }, + page_action="custom", + page_current=0, + page_size=250, columns=[ {"id": 0, "name": "Complaint ID"}, {"id": 1, "name": "Product"}, @@ -53,8 +51,8 @@ fixed_rows={ 'headers': True, 'data': -1 }, row_selectable=True, row_deletable=True, - sorting="be", - filtering=False, + sort_action="custom", + filter_action='none', editable=True, ), ] @@ -62,15 +60,11 @@ @app.callback(Output("table", "data"), [ - Input("table", "pagination_settings"), + Input("table", "page_current"), + Input("table", "page_size"), Input("table", "sort_by") ]) -def updateData(pagination_settings, sort_by): - print(pagination_settings) - - current_page = pagination_settings["current_page"] - page_size = pagination_settings["page_size"] - +def updateData(current_page, page_size, sort_by): start_index = current_page * page_size end_index = start_index + page_size print(str(start_index) + "," + str(end_index)) diff --git a/tests/cypress/dash/v_copy_paste.py b/tests/cypress/dash/v_copy_paste.py index fb2c58c54..052bcfe30 100644 --- a/tests/cypress/dash/v_copy_paste.py +++ b/tests/cypress/dash/v_copy_paste.py @@ -46,7 +46,7 @@ {"id": 13, "name": "Consumer disputed?"}, ], editable=True, - sorting=True, + sort_action='native', ), ] ) diff --git a/tests/cypress/dash/v_fe_page.py b/tests/cypress/dash/v_fe_page.py index 324f7daa5..0f6b97cc0 100644 --- a/tests/cypress/dash/v_fe_page.py +++ b/tests/cypress/dash/v_fe_page.py @@ -36,11 +36,9 @@ dash_table.DataTable( id="table", data=data, - pagination_mode="fe", - pagination_settings={ - "current_page": 0, - "page_size": 250, - }, + page_action="native", + page_current=0, + page_size=250, columns=[ {"id": 0, "name": "Complaint ID"}, {"id": 1, "name": "Product"}, @@ -61,8 +59,8 @@ fixed_rows={ 'headers': True }, row_selectable=True, row_deletable=True, - sorting="fe", - filtering=True, + sort_action="native", + filter_action='native', editable=True, ), html.Div(id="props_container") diff --git a/tests/cypress/tests/unit/derivedViewport_test.ts b/tests/cypress/tests/unit/derivedViewport_test.ts index 2e7e18397..b93818e0a 100644 --- a/tests/cypress/tests/unit/derivedViewport_test.ts +++ b/tests/cypress/tests/unit/derivedViewport_test.ts @@ -1,6 +1,7 @@ import * as R from 'ramda'; import derivedViewportData from 'dash-table/derived/data/viewport'; +import { TableAction } from 'dash-table/components/Table/props'; describe('derived viewport', () => { const viewportData = derivedViewportData(); @@ -9,8 +10,9 @@ describe('derived viewport', () => { describe('with no pagination', () => { it('returns entire data', () => { const result = viewportData( - false, - { current_page: 0, page_size: 250 }, + TableAction.None, + 0, + 250, R.map(() => { }, R.range(0, 5)), R.range(0, 5) ); @@ -23,8 +25,9 @@ describe('derived viewport', () => { describe('with fe pagination', () => { it('returns entire data', () => { const result = viewportData( - 'fe', - { current_page: 0, page_size: 250 }, + TableAction.Native, + 0, + 250, R.map(() => { }, R.range(0, 5)), R.range(0, 5) ); @@ -37,8 +40,9 @@ describe('derived viewport', () => { describe('with be pagination', () => { it('returns entire data', () => { const result = viewportData( - 'be', - { current_page: 0, page_size: 250 }, + TableAction.Custom, + 0, + 250, R.map(() => { }, R.range(0, 5)), R.range(0, 5) ); @@ -53,8 +57,9 @@ describe('derived viewport', () => { describe('with fe pagination', () => { it('returns slice of data', () => { const result = viewportData( - 'fe', - { current_page: 0, page_size: 250 }, + TableAction.Native, + 0, + 250, R.map(idx => ({ idx }), R.range(0, 500)), R.range(0, 500) ); diff --git a/tests/cypress/tests/unit/sorting_test.ts b/tests/cypress/tests/unit/sorting_test.ts index 395e8b044..f2c557c30 100644 --- a/tests/cypress/tests/unit/sorting_test.ts +++ b/tests/cypress/tests/unit/sorting_test.ts @@ -1,8 +1,8 @@ import * as R from 'ramda'; -import sort, { SortDirection, SortSettings } from 'core/sorting'; -import multiUpdateSettings from 'core/sorting/multi'; -import singleUpdateSettings from 'core/sorting/single'; +import sort, { SortDirection, SortBy } from 'core/sorting'; +import multiUpdate from 'core/sorting/multi'; +import singleUpdate from 'core/sorting/single'; describe('sort', () => { it('sorts', () => { @@ -179,7 +179,7 @@ describe('sort', () => { describe('sorting settings', () => { describe('single column sorting', () => { it('new descending', () => { - const settings = singleUpdateSettings([], { column_id: 'a', direction: SortDirection.Descending }); + const settings = singleUpdate([], { column_id: 'a', direction: SortDirection.Descending }); expect(settings.length).to.equal(1); expect(settings[0].column_id).to.equal('a'); @@ -187,7 +187,7 @@ describe('sorting settings', () => { }); it('update to descending', () => { - const settings = singleUpdateSettings( + const settings = singleUpdate( [{ column_id: 'a', direction: SortDirection.Ascending }], { column_id: 'a', direction: SortDirection.Descending } ); @@ -198,7 +198,7 @@ describe('sorting settings', () => { }); it('remove by setting to None', () => { - const settings = singleUpdateSettings( + const settings = singleUpdate( [{ column_id: 'a', direction: SortDirection.Ascending }], { column_id: 'a', direction: SortDirection.None } ); @@ -207,7 +207,7 @@ describe('sorting settings', () => { }); it('replace with other', () => { - const settings = singleUpdateSettings( + const settings = singleUpdate( [{ column_id: 'a', direction: SortDirection.Ascending }], { column_id: 'b', direction: SortDirection.Ascending } ); @@ -218,7 +218,7 @@ describe('sorting settings', () => { }); it('replace with None', () => { - const settings = singleUpdateSettings( + const settings = singleUpdate( [{ column_id: 'a', direction: SortDirection.Ascending }], { column_id: 'b', direction: SortDirection.None } ); @@ -229,7 +229,7 @@ describe('sorting settings', () => { describe('multi columns sorting', () => { it('new descending', () => { - const settings = multiUpdateSettings([], { column_id: 'a', direction: SortDirection.Descending }); + const settings = multiUpdate([], { column_id: 'a', direction: SortDirection.Descending }); expect(settings.length).to.equal(1); expect(settings[0].column_id).to.equal('a'); @@ -237,7 +237,7 @@ describe('sorting settings', () => { }); it('update to descending', () => { - const settings = multiUpdateSettings( + const settings = multiUpdate( [{ column_id: 'a', direction: SortDirection.Ascending }], { column_id: 'a', direction: SortDirection.Descending } ); @@ -248,7 +248,7 @@ describe('sorting settings', () => { }); it('remove by setting to None', () => { - const settings = multiUpdateSettings( + const settings = multiUpdate( [{ column_id: 'a', direction: SortDirection.Ascending }], { column_id: 'a', direction: SortDirection.None } ); @@ -257,7 +257,7 @@ describe('sorting settings', () => { }); it('respects order', () => { - const settings = multiUpdateSettings( + const settings = multiUpdate( [{ column_id: 'a', direction: SortDirection.Ascending }], { column_id: 'b', direction: SortDirection.Ascending } ); @@ -268,19 +268,19 @@ describe('sorting settings', () => { }); it('respects order when removed and added back', () => { - let settings: SortSettings = [{ column_id: 'a', direction: SortDirection.Ascending }]; + let settings: SortBy = [{ column_id: 'a', direction: SortDirection.Ascending }]; - settings = multiUpdateSettings( + settings = multiUpdate( settings, { column_id: 'b', direction: SortDirection.Ascending } ); - settings = multiUpdateSettings( + settings = multiUpdate( settings, { column_id: 'a', direction: SortDirection.None } ); - settings = multiUpdateSettings( + settings = multiUpdate( settings, { column_id: 'a', direction: SortDirection.Ascending } ); diff --git a/tests/dash/app_dataframe_backend_paging.py b/tests/dash/app_dataframe_backend_paging.py index 6fe7dcc28..adcf83f77 100644 --- a/tests/dash/app_dataframe_backend_paging.py +++ b/tests/dash/app_dataframe_backend_paging.py @@ -40,11 +40,9 @@ def layout(): columns=[ {"name": i, "id": i, "deletable": True} for i in sorted(df.columns) ], - pagination_settings={ - 'current_page': 0, - 'page_size': PAGE_SIZE - }, - pagination_mode='be' + page_current=0, + page_size=PAGE_SIZE, + page_action='custom' ), html.Hr(), @@ -58,7 +56,7 @@ def layout(): with large pages, might not be aware that this is only occuring on the current page. - Instead, we recommend implmenting sorting and filtering on the + Instead, we recommend implementing sorting and filtering on the backend as well. That is, on the entire underlying dataset. ''')), @@ -69,14 +67,12 @@ def layout(): columns=[ {"name": i, "id": i, "deletable": True} for i in sorted(df.columns) ], - pagination_settings={ - 'current_page': 0, - 'page_size': PAGE_SIZE - }, - pagination_mode='be', + page_current=0, + page_size=PAGE_SIZE, + page_action='custom', - sorting='be', - sorting_type='single', + sort_action='custom', + sort_mode='single', sort_by=[] ), @@ -96,14 +92,12 @@ def layout(): columns=[ {"name": i, "id": i, "deletable": True} for i in sorted(df.columns) ], - pagination_settings={ - 'current_page': 0, - 'page_size': PAGE_SIZE - }, - pagination_mode='be', + page_current=0, + page_size=PAGE_SIZE, + page_action='custom', - sorting='be', - sorting_type='multi', + sort_action='custom', + sort_mode='multi', sort_by=[] ), @@ -134,14 +128,12 @@ def layout(): columns=[ {"name": i, "id": i, "deletable": True} for i in sorted(df.columns) ], - pagination_settings={ - 'current_page': 0, - 'page_size': PAGE_SIZE - }, - pagination_mode='be', + page_current=0, + page_size=PAGE_SIZE, + page_action='custom', - filtering='be', - filter='' + filter_action='custom', + filter_query='' ), section_title('Backend Paging with Filtering and Multi-Column Sorting'), @@ -151,17 +143,15 @@ def layout(): columns=[ {"name": i, "id": i, "deletable": True} for i in sorted(df.columns) ], - pagination_settings={ - 'current_page': 0, - 'page_size': PAGE_SIZE - }, - pagination_mode='be', + page_current=0, + page_size=PAGE_SIZE, + page_action='custom', - filtering='be', - filter='', + filter_action='custom', + filter_query='', - sorting='be', - sorting_type='multi', + sort_action='custom', + sort_mode='multi', sort_by=[] ), @@ -181,17 +171,15 @@ def layout(): columns=[ {"name": i, "id": i, "deletable": True} for i in sorted(df.columns) ], - pagination_settings={ - 'current_page': 0, - 'page_size': 20 - }, - pagination_mode='be', + page_current=0, + page_size=20, + page_action='custom', - filtering='be', - filter='', + filter_action='custom', + filter_query='', - sorting='be', - sorting_type='multi', + sort_action='custom', + sort_mode='multi', sort_by=[] ), style={'height': 750, 'overflowY': 'scroll'}, @@ -210,19 +198,20 @@ def layout(): @app.callback( Output(IDS["table"], "data"), - [Input(IDS["table"], "pagination_settings")]) -def update_graph(pagination_settings): + [Input(IDS["table"], "page_current"), Input(IDS["table"], "page_size")]) +def update_graph(page_current, page_size): return df.iloc[ - pagination_settings['current_page']*pagination_settings['page_size']: - (pagination_settings['current_page'] + 1)*pagination_settings['page_size'] + page_current * page_size: + (page_current + 1) * page_size ].to_dict('rows') @app.callback( Output(IDS["table-sorting"], "data"), - [Input(IDS["table-sorting"], "pagination_settings"), + [Input(IDS["table-sorting"], "page_current"), + Input(IDS["table-sorting"], "page_size"), Input(IDS["table-sorting"], "sort_by")]) -def update_graph(pagination_settings, sort_by): +def update_graph(page_current, page_size, sort_by): print(sort_by) if len(sort_by): dff = df.sort_values( @@ -235,17 +224,18 @@ def update_graph(pagination_settings, sort_by): dff = df return dff.iloc[ - pagination_settings['current_page']*pagination_settings['page_size']: - (pagination_settings['current_page'] + 1)*pagination_settings['page_size'] + page_current * page_size: + (page_current + 1) * page_size ].to_dict('rows') @app.callback( Output(IDS["table-multi-sorting"], "data"), - [Input(IDS["table-multi-sorting"], "pagination_settings"), + [Input(IDS["table-multi-sorting"], "page_current"), + Input(IDS["table-multi-sorting"], "page_size"), Input(IDS["table-multi-sorting"], "sort_by")]) -def update_graph(pagination_settings, sort_by): +def update_graph(page_current, page_size, sort_by): print(sort_by) if len(sort_by): dff = df.sort_values( @@ -261,59 +251,61 @@ def update_graph(pagination_settings, sort_by): dff = df return dff.iloc[ - pagination_settings['current_page']*pagination_settings['page_size']: - (pagination_settings['current_page'] + 1)*pagination_settings['page_size'] + page_current * page_size: + (page_current + 1) * page_size ].to_dict('rows') @app.callback( Output(IDS["table-filtering"], "data"), - [Input(IDS["table-filtering"], "pagination_settings"), - Input(IDS["table-filtering"], "filter")]) -def update_graph(pagination_settings, filter): - print(filter) - filtering_expressions = filter.split(' && ') + [Input(IDS["table-filtering"], "page_current"), + Input(IDS["table-filtering"], "page_size"), + Input(IDS["table-filtering"], "filter_query")]) +def update_graph(page_current, page_size, filter_query): + print(filter_query) + filtering_expressions = filter_query.split(' && ') dff = df - for filter in filtering_expressions: - if ' eq ' in filter: - col_name = filter.split(' eq ')[0] - filter_value = filter.split(' eq ')[1] + for filter_query in filtering_expressions: + if ' eq ' in filter_query: + col_name = filter_query.split(' eq ')[0] + filter_value = filter_query.split(' eq ')[1] dff = dff.loc[dff[col_name] == filter_value] - if ' > ' in filter: - col_name = filter.split(' > ')[0] - filter_value = float(filter.split(' > ')[1]) + if ' > ' in filter_query: + col_name = filter_query.split(' > ')[0] + filter_value = float(filter_query.split(' > ')[1]) dff = dff.loc[dff[col_name] > filter_value] - if ' < ' in filter: - col_name = filter.split(' < ')[0] - filter_value = float(filter.split(' < ')[1]) + if ' < ' in filter_query: + col_name = filter_query.split(' < ')[0] + filter_value = float(filter_query.split(' < ')[1]) dff = dff.loc[dff[col_name] < filter_value] return dff.iloc[ - pagination_settings['current_page']*pagination_settings['page_size']: - (pagination_settings['current_page'] + 1)*pagination_settings['page_size'] + page_current * page_size: + (page_current + 1) * page_size ].to_dict('rows') @app.callback( Output(IDS["table-sorting-filtering"], "data"), - [Input(IDS["table-sorting-filtering"], "pagination_settings"), + [Input(IDS["table-sorting-filtering"], "page_current"), + Input(IDS["table-sorting-filtering"], "page_size"), Input(IDS["table-sorting-filtering"], "sort_by"), - Input(IDS["table-sorting-filtering"], "filter")]) -def update_graph(pagination_settings, sort_by, filter): - filtering_expressions = filter.split(' && ') + Input(IDS["table-sorting-filtering"], "filter_query")]) +def update_graph(page_current, page_size, sort_by, filter_query): + filtering_expressions = filter_query.split(' && ') dff = df - for filter in filtering_expressions: - if ' eq ' in filter: - col_name = filter.split(' eq ')[0] - filter_value = filter.split(' eq ')[1] + for filter_query in filtering_expressions: + if ' eq ' in filter_query: + col_name = filter_query.split(' eq ')[0] + filter_value = filter_query.split(' eq ')[1] dff = dff.loc[dff[col_name] == filter_value] - if ' > ' in filter: - col_name = filter.split(' > ')[0] - filter_value = float(filter.split(' > ')[1]) + if ' > ' in filter_query: + col_name = filter_query.split(' > ')[0] + filter_value = float(filter_query.split(' > ')[1]) dff = dff.loc[dff[col_name] > filter_value] - if ' < ' in filter: - col_name = filter.split(' < ')[0] - filter_value = float(filter.split(' < ')[1]) + if ' < ' in filter_query: + col_name = filter_query.split(' < ')[0] + filter_value = float(filter_query.split(' < ')[1]) dff = dff.loc[dff[col_name] < filter_value] if len(sort_by): @@ -327,31 +319,32 @@ def update_graph(pagination_settings, sort_by, filter): ) return dff.iloc[ - pagination_settings['current_page']*pagination_settings['page_size']: - (pagination_settings['current_page'] + 1)*pagination_settings['page_size'] + page_current * page_size: + (page_current + 1) * page_size ].to_dict('rows') @app.callback( Output(IDS["table-paging-with-graph"], "data"), - [Input(IDS["table-paging-with-graph"], "pagination_settings"), + [Input(IDS["table-paging-with-graph"], "page_current"), + Input(IDS["table-paging-with-graph"], "page_size"), Input(IDS["table-paging-with-graph"], "sort_by"), - Input(IDS["table-paging-with-graph"], "filter")]) -def update_table(pagination_settings, sort_by, filter): - filtering_expressions = filter.split(' && ') + Input(IDS["table-paging-with-graph"], "filter_query")]) +def update_table(page_current, page_size, sort_by, filter_query): + filtering_expressions = filter_query.split(' && ') dff = df - for filter in filtering_expressions: - if ' eq ' in filter: - col_name = filter.split(' eq ')[0] - filter_value = filter.split(' eq ')[1] + for filter_query in filtering_expressions: + if ' eq ' in filter_query: + col_name = filter_query.split(' eq ')[0] + filter_value = filter_query.split(' eq ')[1] dff = dff.loc[dff[col_name] == filter_value] - if ' > ' in filter: - col_name = filter.split(' > ')[0] - filter_value = float(filter.split(' > ')[1]) + if ' > ' in filter_query: + col_name = filter_query.split(' > ')[0] + filter_value = float(filter_query.split(' > ')[1]) dff = dff.loc[dff[col_name] > filter_value] - if ' < ' in filter: - col_name = filter.split(' < ')[0] - filter_value = float(filter.split(' < ')[1]) + if ' < ' in filter_query: + col_name = filter_query.split(' < ')[0] + filter_value = float(filter_query.split(' < ')[1]) dff = dff.loc[dff[col_name] < filter_value] if len(sort_by): @@ -365,8 +358,8 @@ def update_table(pagination_settings, sort_by, filter): ) return dff.iloc[ - pagination_settings['current_page']*pagination_settings['page_size']: - (pagination_settings['current_page'] + 1)*pagination_settings['page_size'] + page_current * page_size: + (page_current + 1) * page_size ].to_dict('rows') diff --git a/tests/dash/app_dataframe_updating_graph_fe.py b/tests/dash/app_dataframe_updating_graph_fe.py index 5877e8549..26e4e2fad 100644 --- a/tests/dash/app_dataframe_updating_graph_fe.py +++ b/tests/dash/app_dataframe_updating_graph_fe.py @@ -24,9 +24,9 @@ def layout(): ], data=df.to_dict("rows"), editable=True, - filtering=True, - sorting=True, - sorting_type="multi", + filter_action='native', + sort_action='native', + sort_mode="multi", row_selectable="multi", row_deletable=True, selected_rows=[], @@ -43,8 +43,8 @@ def layout(): `Table` includes several features for modifying and transforming the view of the data. These include: - - Sorting by column (`sorting=True`) - - Filtering by column (`filtering=True`) + - Sorting by column (`sort_action='native'`) + - Filtering by column (`filter_action='native'`) - Editing the cells (`editable=True`) - Deleting rows (`row_deletable=True`) - Deleting columns (`columns[i].deletable=True`) diff --git a/tests/visual/percy-storybook/Border.style.percy.tsx b/tests/visual/percy-storybook/Border.style.percy.tsx index 4378bec14..af66c357d 100644 --- a/tests/visual/percy-storybook/Border.style.percy.tsx +++ b/tests/visual/percy-storybook/Border.style.percy.tsx @@ -3,13 +3,14 @@ import React from 'react'; import { storiesOf } from '@storybook/react'; import DataTable from 'dash-table/dash/DataTable'; import { BORDER_PROPS_DEFAULTS } from './Border.defaults.percy'; +import { TableAction, SortMode } from 'dash-table/components/Table/props'; const OPS_VARIANTS: ITest[] = [ - { name: 'with ops', props: { row_deletable: true, row_selectable: 'single' } }, - { name: 'fixed columns', props: { fixed_columns: { headers: true }, row_deletable: true, row_selectable: 'single' } }, - { name: 'fixed rows', props: { fixed_rows: { headers: true }, row_deletable: true, row_selectable: 'single' } }, - { name: 'fixed columns & rows', props: { fixed_columns: { headers: true }, fixed_rows: { headers: true }, row_deletable: true, row_selectable: 'single' } }, - { name: 'fixed columns & rows inside fragments', props: { fixed_columns: { headers: true, data: 1 }, fixed_rows: { headers: true , data: 1}, row_deletable: true, row_selectable: 'single' } } + { name: 'with ops', props: { row_deletable: true, row_selectable: SortMode.Single } }, + { name: 'fixed columns', props: { fixed_columns: { headers: true }, row_deletable: true, row_selectable: SortMode.Single } }, + { name: 'fixed rows', props: { fixed_rows: { headers: true }, row_deletable: true, row_selectable: SortMode.Single } }, + { name: 'fixed columns & rows', props: { fixed_columns: { headers: true }, fixed_rows: { headers: true }, row_deletable: true, row_selectable: SortMode.Single } }, + { name: 'fixed columns & rows inside fragments', props: { fixed_columns: { headers: true, data: 1 }, fixed_rows: { headers: true, data: 1 }, row_deletable: true, row_selectable: SortMode.Single } } ]; interface ITest { @@ -69,7 +70,7 @@ const scenarios: ITest[] = [ }, { name: 'with filter style', props: { - filtering: true, + filter_action: TableAction.Native, style_filter: { border: '1px solid hotpink' } @@ -97,7 +98,7 @@ const scenarios: ITest[] = [ }, { name: 'with header / filter / cell (data) style - filter wins on header, filter wins on cell (data)', props: { - filtering: true, + filter_action: TableAction.Native, style_cell: { border: '1px solid teal' }, @@ -111,7 +112,7 @@ const scenarios: ITest[] = [ }, { name: 'with header / data / cell (filter) style - header wins on cell (filter), data wins on cell (filter)', props: { - filtering: true, + filter_action: TableAction.Native, style_data: { border: '1px solid teal' }, @@ -125,7 +126,7 @@ const scenarios: ITest[] = [ }, { name: 'with cell (header) / filter / data style - filter wins on cell (header), data wins on filter', props: { - filtering: true, + filter_action: TableAction.Native, style_data: { border: '1px solid teal' }, @@ -139,7 +140,7 @@ const scenarios: ITest[] = [ }, { name: 'with data / cell (header, filter) style - data wins on filter', props: { - filtering: true, + filter_action: TableAction.Native, style_data: { border: '1px solid teal' }, @@ -150,7 +151,7 @@ const scenarios: ITest[] = [ }, { name: 'with header / filter / data style - data wins on filter, filter wins on header', props: { - filtering: true, + filter_action: TableAction.Native, style_data: { border: '1px solid teal' }, @@ -164,7 +165,7 @@ const scenarios: ITest[] = [ }, { name: 'style as list view', props: { - filtering: true, + filter_action: TableAction.Native, style_data: { border: '1px solid teal' }, @@ -177,7 +178,7 @@ const scenarios: ITest[] = [ style_as_list_view: true } }, { - name: 'hoizontal border between header and first row should be blue', + name: 'horizontal border between header and first row should be blue', props: { css: [{selector: 'th', rule: 'border: 1px solid pink'}], style_data: {border: '1px solid blue'} @@ -185,7 +186,7 @@ const scenarios: ITest[] = [ }, { name: 'horizontal border between header and filter should be purple', props: { - filtering: true, + filter_action: TableAction.Native, css: [{selector: 'th', rule: 'border: 1px solid pink'}], style_filter: {border: '1px solid purple'} } @@ -247,7 +248,7 @@ const ops_scenarios: ITest[] = [ border: '1px solid black' }, style_data_conditional: [{ - if: { filter: '{a} eq 85' }, + if: { filter_query: '{a} eq 85' }, backgroundColor: 'pink', border: '1px solid red' }] @@ -291,7 +292,7 @@ const ops_scenarios: ITest[] = [ }, { name: 'filter ops do not get styled on conditional column_id', props: { - filtering: true, + filter_action: TableAction.Native, style_filter: { border: '1px solid black' }, @@ -304,7 +305,7 @@ const ops_scenarios: ITest[] = [ }, { name: 'filter ops do not get styled on conditional column_type', props: { - filtering: true, + filter_action: TableAction.Native, style_filter: { border: '1px solid black' }, diff --git a/tests/visual/percy-storybook/DashTable.percy.tsx b/tests/visual/percy-storybook/DashTable.percy.tsx index 7c8255034..f1f54be17 100644 --- a/tests/visual/percy-storybook/DashTable.percy.tsx +++ b/tests/visual/percy-storybook/DashTable.percy.tsx @@ -46,6 +46,7 @@ fixtures.forEach(fixture => { }); import dataset from './../../../datasets/gapminder.csv'; +import { TableAction } from 'dash-table/components/Table/props'; storiesOf('DashTable/Without Data', module) .add('with 1 column', () => ()) @@ -249,7 +250,7 @@ storiesOf('DashTable/Sorting', module) id='table' data={sparseData} columns={mergedColumns} - sorting={true} + sort_action={TableAction.Native} sort_by={[{ column_id: 'a', direction: 'desc' }]} style_data_conditional={style_data_conditional} />)) @@ -258,7 +259,7 @@ storiesOf('DashTable/Sorting', module) id='table' data={sparseData} columns={mergedColumns} - sorting={true} + sort_action={TableAction.Native} sort_by={[{ column_id: 'a', direction: 'asc' }]} sort_as_none={['']} style_data_conditional={style_data_conditional} @@ -268,7 +269,7 @@ storiesOf('DashTable/Sorting', module) id='table' data={sparseData} columns={mergedColumns} - sorting={true} + sort_action={TableAction.Native} sort_by={[{ column_id: 'a', direction: 'desc' }]} sort_as_none={['']} style_data_conditional={style_data_conditional} @@ -278,7 +279,7 @@ storiesOf('DashTable/Sorting', module) id='table' data={sparseData} columns={mergedColumns} - sorting={true} + sort_action={TableAction.Native} sort_by={[{ column_id: 'a', direction: 'desc' }]} sort_as_none={['', 426]} style_data_conditional={style_data_conditional} @@ -288,7 +289,7 @@ storiesOf('DashTable/Sorting', module) id='table' data={sparseData} columns={mergedColumns} - sorting={true} + sort_action={TableAction.Native} sort_by={[{ column_id: 'a', direction: 'asc' }]} sort_as_none={['', 426]} style_data_conditional={style_data_conditional} diff --git a/tests/visual/percy-storybook/Dropdown.percy.tsx b/tests/visual/percy-storybook/Dropdown.percy.tsx index 29d735ad3..eb11262f9 100644 --- a/tests/visual/percy-storybook/Dropdown.percy.tsx +++ b/tests/visual/percy-storybook/Dropdown.percy.tsx @@ -60,7 +60,7 @@ storiesOf('DashTable/Dropdown', module) dropdown_conditional={[{ if: { column_id: 'Neighborhood', - filter: '{City} eq "NYC"' + filter_query: '{City} eq "NYC"' }, dropdown: R.map( i => ({ label: i, value: i }), @@ -69,7 +69,7 @@ storiesOf('DashTable/Dropdown', module) }, { if: { column_id: 'Neighborhood', - filter: '{City} eq "Montreal"' + filter_query: '{City} eq "Montreal"' }, dropdown: R.map( i => ({ label: i, value: i }), @@ -79,7 +79,7 @@ storiesOf('DashTable/Dropdown', module) { if: { column_id: 'Neighborhood', - filter: '{City} eq "Los Angeles"' + filter_query: '{City} eq "Los Angeles"' }, dropdown: R.map( i => ({ label: i, value: i }), diff --git a/tests/visual/percy-storybook/Filters.percy.tsx b/tests/visual/percy-storybook/Filters.percy.tsx index c08197cf9..c551959bc 100644 --- a/tests/visual/percy-storybook/Filters.percy.tsx +++ b/tests/visual/percy-storybook/Filters.percy.tsx @@ -3,6 +3,7 @@ import React from 'react'; import { storiesOf } from '@storybook/react'; import random from 'core/math/random'; import DataTable from 'dash-table/dash/DataTable'; +import { TableAction } from 'dash-table/components/Table/props'; const setProps = () => { }; @@ -21,7 +22,7 @@ let props = { setProps, id: 'table', data: data, - filtering: true, + filter_action: TableAction.Native, style_cell: { width: 100, min_width: 100, diff --git a/tests/visual/percy-storybook/Style.percy.tsx b/tests/visual/percy-storybook/Style.percy.tsx index b5b3bbd09..f099c2f31 100644 --- a/tests/visual/percy-storybook/Style.percy.tsx +++ b/tests/visual/percy-storybook/Style.percy.tsx @@ -125,10 +125,10 @@ storiesOf('DashTable/Style type condition', module) width: '100%' }} style_data_conditional={[{ - if: { column_id: 'Region', filter: '{Region} eq Montreal' }, + if: { column_id: 'Region', filter_query: '{Region} eq Montreal' }, background_color: 'yellow' }, { - if: { column_id: 'Humidity', filter: '{Humidity} eq 20' }, + if: { column_id: 'Humidity', filter_query: '{Humidity} eq 20' }, background_color: 'yellow' }]} />)) diff --git a/tests/visual/percy-storybook/TriadValidation.percy.tsx b/tests/visual/percy-storybook/TriadValidation.percy.tsx index 4fbfb3e07..d51b5e08d 100644 --- a/tests/visual/percy-storybook/TriadValidation.percy.tsx +++ b/tests/visual/percy-storybook/TriadValidation.percy.tsx @@ -2,22 +2,21 @@ import React from 'react'; import { storiesOf } from '@storybook/react'; import DataTable from 'dash-table/dash/DataTable'; +import { TableAction } from 'dash-table/components/Table/props'; -const filteringValues = ['fe', 'be']; -const sortingValues = ['fe', 'be']; -const paginationValues = ['fe', 'be']; +const actions = [TableAction.Native, TableAction.Custom]; const setProps = () => { }; let stories = storiesOf('DashTable/Props Validation', module); -filteringValues.forEach(filter => { - sortingValues.forEach(sort => { - paginationValues.forEach(page => { +actions.forEach(filter => { + actions.forEach(sort => { + actions.forEach(page => { stories = stories.add(`filter=${filter}, sorting=${sort}, pagination=${page}`, () => ()); }); diff --git a/tests/visual/percy-storybook/Virtualization.percy.tsx b/tests/visual/percy-storybook/Virtualization.percy.tsx index 2500199a4..e0d4ea70d 100644 --- a/tests/visual/percy-storybook/Virtualization.percy.tsx +++ b/tests/visual/percy-storybook/Virtualization.percy.tsx @@ -6,6 +6,7 @@ import { storiesOf } from '@storybook/react'; import dataset from './../../../datasets/16zpallagi-25cols-100klines.csv'; import DataTable from 'dash-table/dash/DataTable'; +import { TableAction } from 'dash-table/components/Table/props'; const setProps = () => { }; @@ -18,7 +19,7 @@ storiesOf('DashTable/Virtualization', module) id='table' data={data} columns={columns} - pagination_mode={false} + page_action={TableAction.None} virtualization={true} editable={true} fixed_rows={{ headers: true }} diff --git a/tests/visual/percy-storybook/Width.empty.percy.tsx b/tests/visual/percy-storybook/Width.empty.percy.tsx index f2d0429e0..f9ef27bd3 100644 --- a/tests/visual/percy-storybook/Width.empty.percy.tsx +++ b/tests/visual/percy-storybook/Width.empty.percy.tsx @@ -4,6 +4,7 @@ import { storiesOf } from '@storybook/react'; import random from 'core/math/random'; import DataTable from 'dash-table/dash/DataTable'; +import { TableAction } from 'dash-table/components/Table/props'; const setProps = () => { }; @@ -24,7 +25,7 @@ const baseProps = { setProps, id: 'table', data, - filtering: 'fe', + filter_action: TableAction.Native, style_cell: { width: 100, max_width: 100, min_width: 100 } }; @@ -38,21 +39,21 @@ storiesOf('DashTable/Empty', module) />)) .add('with column filters -- invalid query', () => ()) .add('with column filters -- single query', () => ()) .add('with column filters -- multi query', () => ()) .add('with column filters -- multi query, no data', () => ()); \ No newline at end of file diff --git a/tslint.json b/tslint.json index 84bd4fd2e..9fbffe5c6 100644 --- a/tslint.json +++ b/tslint.json @@ -5,9 +5,11 @@ ], "linterOptions": { "exclude": [ - "node_modules/**", + ".config/**", "cypress/**", - "@Types/**" + "node_modules/**", + "@Types/**", + "venv/**" ] }, "jsRules": { From c9957e0e3327b3e74bdb2a6a81da375e4cd142c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Wed, 5 Jun 2019 09:36:23 -0400 Subject: [PATCH 28/46] dropdown_data & tooltip_data - from objectOf(arrayOf) -> arrayOf(objectOf) - better match data structure --- demo/AppMode.ts | 18 ++++---- src/core/sorting/index.ts | 16 +++---- .../components/CellDropdown/index.tsx | 4 +- src/dash-table/components/Table/props.ts | 32 +++++++------- src/dash-table/dash/DataTable.js | 7 +-- src/dash-table/derived/cell/contents.tsx | 8 ++-- src/dash-table/derived/cell/dropdowns.ts | 29 ++++++------ src/dash-table/derived/header/content.tsx | 4 +- src/dash-table/derived/table/tooltip.ts | 23 +++++----- .../visual/percy-storybook/Dropdown.percy.tsx | 44 +++++++++++-------- 10 files changed, 95 insertions(+), 90 deletions(-) diff --git a/demo/AppMode.ts b/demo/AppMode.ts index a5683732a..66156f66c 100644 --- a/demo/AppMode.ts +++ b/demo/AppMode.ts @@ -131,16 +131,14 @@ function getTooltipsState() { state.tableProps.tooltip_delay = 250; state.tableProps.tooltip_duration = 1000; - state.tableProps.tooltip_data = { - ccc: [ - { type: TooltipSyntax.Markdown, value: `### Go Proverb\nThe enemy's key point is yours` }, - { type: TooltipSyntax.Markdown, value: `### Go Proverb\nPlay on the point of symmetry` }, - { type: TooltipSyntax.Markdown, value: `### Go Proverb\nSente gains nothing` }, - { type: TooltipSyntax.Text, value: `Beware of going back to patch up` }, - { type: TooltipSyntax.Text, value: `When in doubt, Tenuki` }, - `People in glass houses should not throw stones` - ] - }; + state.tableProps.tooltip_data = [ + { ccc: { type: TooltipSyntax.Markdown, value: `### Go Proverb\nThe enemy's key point is yours` } }, + { ccc: { type: TooltipSyntax.Markdown, value: `### Go Proverb\nPlay on the point of symmetry` } }, + { ccc: { type: TooltipSyntax.Markdown, value: `### Go Proverb\nSente gains nothing` } }, + { ccc: { type: TooltipSyntax.Text, value: `Beware of going back to patch up` } }, + { ccc: { type: TooltipSyntax.Text, value: `When in doubt, Tenuki` } }, + { ccc: `People in glass houses should not throw stones` } + ]; state.tableProps.tooltip = { ccc: { type: TooltipSyntax.Text, value: `There is death in the hane` }, ddd: { type: TooltipSyntax.Markdown, value: `Hane, Cut, Placement` }, diff --git a/src/core/sorting/index.ts b/src/core/sorting/index.ts index 4dd2473bd..0037127d5 100644 --- a/src/core/sorting/index.ts +++ b/src/core/sorting/index.ts @@ -22,31 +22,31 @@ export default (data: any[], sortBy: SortBy, isNully: IsNullyFn = defaultIsNully } return R.sortWith( - R.map(setting => { - return setting.direction === SortDirection.Descending ? + R.map(sort => { + return sort.direction === SortDirection.Descending ? R.comparator((d1: any, d2: any) => { - const id = setting.column_id; + const id = sort.column_id; const prop1 = d1[id]; const prop2 = d2[id]; - if (isNully(prop1, setting.column_id)) { + if (isNully(prop1, sort.column_id)) { return false; - } else if (isNully(prop2, setting.column_id)) { + } else if (isNully(prop2, sort.column_id)) { return true; } return prop1 > prop2; }) : R.comparator((d1: any, d2: any) => { - const id = setting.column_id; + const id = sort.column_id; const prop1 = d1[id]; const prop2 = d2[id]; - if (isNully(prop1, setting.column_id)) { + if (isNully(prop1, sort.column_id)) { return false; - } else if (isNully(prop2, setting.column_id)) { + } else if (isNully(prop2, sort.column_id)) { return true; } diff --git a/src/dash-table/components/CellDropdown/index.tsx b/src/dash-table/components/CellDropdown/index.tsx index e98fb08fc..1ba7dbe7f 100644 --- a/src/dash-table/components/CellDropdown/index.tsx +++ b/src/dash-table/components/CellDropdown/index.tsx @@ -8,12 +8,12 @@ import DOM from 'core/browser/DOM'; import dropdownHelper from 'dash-table/components/dropdownHelper'; -import { DropdownValues } from '../Table/props'; +import { IDropdownValue } from '../Table/props'; interface IProps { active: boolean; clearable?: boolean; - dropdown?: DropdownValues; + dropdown?: IDropdownValue[]; onChange: (e: ChangeEvent) => void; value: any; } diff --git a/src/dash-table/components/Table/props.ts b/src/dash-table/components/Table/props.ts index 60bb6419e..0ae16712c 100644 --- a/src/dash-table/components/Table/props.ts +++ b/src/dash-table/components/Table/props.ts @@ -162,6 +162,11 @@ export interface IBaseVisibleColumn { name: string | string[]; } +export type ConditionalDropdowns = IConditionalDropdown[]; +export type DataDropdowns = Partial[]; +export type DataTooltips = Partial[]; +export type StaticDropdowns = Partial; + export type Fixed = { headers: false, data?: 0 } | { headers: true, data?: number }; export type IColumnType = INumberColumn | ITextColumn | IDatetimeColumn | IAnyColumn; export type IVisibleColumn = IBaseVisibleColumn & IColumnType; @@ -179,21 +184,17 @@ export interface IDropdownValue { value: string | number; } -export type DropdownValues = IDropdownValue[]; - export interface IDropdown { clearable?: boolean; - dropdown: DropdownValues; + dropdown: IDropdownValue[]; } export interface IConditionalDropdown extends IDropdown { if: Partial; } -export type ConditionalDropdowns = IConditionalDropdown[]; - export interface IDataDropdowns { - [key: string]: (IDropdown | undefined)[]; + [key: string]: IDropdown; } export interface IStaticDropdowns { @@ -201,7 +202,7 @@ export interface IStaticDropdowns { } export interface ITableTooltips { - [key: string]: Tooltip[]; + [key: string]: Tooltip; } export interface ITableStaticTooltips { @@ -256,7 +257,7 @@ export interface IProps { id: string; - tooltip_data?: ITableTooltips; + tooltip_data?: DataTooltips; tooltip_delay: number | null; tooltip_duration: number | null; tooltip: ITableStaticTooltips; @@ -264,9 +265,9 @@ export interface IProps { active_cell?: ICellCoordinates; columns?: Columns; - dropdown?: IStaticDropdowns; + dropdown?: StaticDropdowns; dropdown_conditional?: ConditionalDropdowns; - dropdown_data: IDataDropdowns; + dropdown_data: DataDropdowns; css?: IStylesheetRule[]; data?: Data; editable?: boolean; @@ -307,9 +308,9 @@ export interface IProps { interface IDefaultProps { active_cell: ICellCoordinates; columns: Columns; - dropdown: IStaticDropdowns; + dropdown: StaticDropdowns; dropdown_conditional: ConditionalDropdowns; - dropdown_data: IDataDropdowns; + dropdown_data: DataDropdowns; css: IStylesheetRule[]; data: Data; editable: boolean; @@ -330,6 +331,7 @@ interface IDefaultProps { sort_mode: SortMode; sort_as_none: SortAsNone; style_as_list_view: boolean; + tooltip_data: DataTooltips; page_action: TableAction; page_current: number; @@ -392,9 +394,9 @@ export type ControlledTableProps = SanitizedProps & IState & { export interface ICellFactoryProps { active_cell: ICellCoordinates; columns: VisibleColumns; - dropdown: IStaticDropdowns; + dropdown: StaticDropdowns; dropdown_conditional: ConditionalDropdowns; - dropdown_data: IDataDropdowns; + dropdown_data: DataDropdowns; tooltip: ITableStaticTooltips; currentTooltip: IUSerInterfaceTooltip; data: Data; @@ -421,7 +423,7 @@ export interface ICellFactoryProps { style_filter_conditional: BasicFilters; style_header_conditional: Headers; style_table: Table; - tooltip_data?: ITableTooltips; + tooltip_data: DataTooltips; uiCell?: IUserInterfaceCell; uiViewport?: IUserInterfaceViewport; viewport: IDerivedData; diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index 91cecffa6..eafeb4b20 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -70,6 +70,7 @@ export const defaultProps = { tooltip: {}, tooltip_conditional: [], + tooltip_data: [], tooltip_delay: 350, tooltip_duration: 2000, @@ -638,8 +639,8 @@ export const propTypes = { /** * */ - dropdown_data: PropTypes.objectOf( - PropTypes.arrayOf( + dropdown_data: PropTypes.arrayOf( + PropTypes.objectOf( PropTypes.exact({ clearable: PropTypes.bool, dropdown: PropTypes.arrayOf(PropTypes.exact({ @@ -775,7 +776,7 @@ export const propTypes = { * a plain string. The `text` syntax will be used in * that case. */ - tooltip_data: PropTypes.objectOf(PropTypes.arrayOf( + tooltip_data: PropTypes.arrayOf(PropTypes.objectOf( PropTypes.oneOfType([ PropTypes.string, PropTypes.exact({ diff --git a/src/dash-table/derived/cell/contents.tsx b/src/dash-table/derived/cell/contents.tsx index 1ca45f887..81b6452f8 100644 --- a/src/dash-table/derived/cell/contents.tsx +++ b/src/dash-table/derived/cell/contents.tsx @@ -5,13 +5,13 @@ import { ICellCoordinates, Data, Datum, - DropdownValues, ICellFactoryProps, + IDropdown, + IDropdownValue, IViewportOffset, IVisibleColumn, Presentation, - VisibleColumns, - IDropdown + VisibleColumns } from 'dash-table/components/Table/props'; import CellInput from 'dash-table/components/CellInput'; import derivedCellEventHandlerProps, { Handler } from 'dash-table/derived/cell/eventHandlerProps'; @@ -34,7 +34,7 @@ enum CellType { function getCellType( active: boolean, editable: boolean, - dropdown: DropdownValues | undefined, + dropdown: IDropdownValue[] | undefined, presentation: Presentation | undefined ): CellType { switch (presentation) { diff --git a/src/dash-table/derived/cell/dropdowns.ts b/src/dash-table/derived/cell/dropdowns.ts index 2b13101ca..fa6cfb364 100644 --- a/src/dash-table/derived/cell/dropdowns.ts +++ b/src/dash-table/derived/cell/dropdowns.ts @@ -4,17 +4,17 @@ import { memoizeOne } from 'core/memoizer'; import memoizerCache from 'core/cache/memoizer'; import { + ColumnId, + ConditionalDropdowns, Data, + DataDropdowns, Datum, - VisibleColumns, - ColumnId, + IConditionalDropdown, + IDropdown, Indices, IVisibleColumn, - IDataDropdowns, - IStaticDropdowns, - ConditionalDropdowns, - IDropdown, - IConditionalDropdown + StaticDropdowns, + VisibleColumns } from 'dash-table/components/Table/props'; import { QuerySyntaxTree } from 'dash-table/syntax-tree'; import { ifColumnId } from 'dash-table/conditional'; @@ -32,18 +32,17 @@ class Dropdowns { data: Data, indices: Indices, conditionalDropdowns: ConditionalDropdowns, - staticDropdowns: IStaticDropdowns, - dataDropdowns: IDataDropdowns + staticDropdowns: StaticDropdowns, + dataDropdowns: DataDropdowns ) => mapData((datum, rowIndex) => R.map(column => { const realIndex = indices[rowIndex]; const appliedStaticDropdown = ( dataDropdowns && - dataDropdowns[column.id] && - dataDropdowns[column.id].length > realIndex && - dataDropdowns[column.id][realIndex] && - dataDropdowns[column.id][realIndex] - ) || staticDropdowns[column.id] || null; + dataDropdowns.length > realIndex && + dataDropdowns[realIndex] && + dataDropdowns[realIndex][column.id] + ) || staticDropdowns[column.id]; return this.dropdown.get(column.id, rowIndex)( appliedStaticDropdown, @@ -58,7 +57,7 @@ class Dropdowns { * applicable dropdowns. */ private readonly dropdown = memoizerCache<[ColumnId, number]>()(( - base: IDropdown | null, + base: IDropdown | undefined, conditionals: ConditionalDropdowns, column: IVisibleColumn, datum: Datum diff --git a/src/dash-table/derived/header/content.tsx b/src/dash-table/derived/header/content.tsx index c0a4c8d21..bae228d49 100644 --- a/src/dash-table/derived/header/content.tsx +++ b/src/dash-table/derived/header/content.tsx @@ -62,9 +62,9 @@ function editColumnName(column: IVisibleColumn, columns: VisibleColumns, columnR } function getSorting(columnId: ColumnId, sortBy: SortBy): SortDirection { - const setting = R.find(s => s.column_id === columnId, sortBy); + const sort = R.find(s => s.column_id === columnId, sortBy); - return setting ? setting.direction : SortDirection.None; + return sort ? sort.direction : SortDirection.None; } function getSortingIcon(columnId: ColumnId, sortBy: SortBy) { diff --git a/src/dash-table/derived/table/tooltip.ts b/src/dash-table/derived/table/tooltip.ts index 9e42f7288..7fc44d17a 100644 --- a/src/dash-table/derived/table/tooltip.ts +++ b/src/dash-table/derived/table/tooltip.ts @@ -1,6 +1,6 @@ import * as R from 'ramda'; -import { IUSerInterfaceTooltip, ITableTooltips, ITableStaticTooltips, IVirtualizedDerivedData } from 'dash-table/components/Table/props'; +import { IUSerInterfaceTooltip, ITableStaticTooltips, IVirtualizedDerivedData, DataTooltips } from 'dash-table/components/Table/props'; import { ifColumnId, ifRowIndex, ifFilter } from 'dash-table/conditional'; import { ConditionalTooltip, TooltipSyntax } from 'dash-table/tooltips/props'; import { memoizeOne } from 'core/memoizer'; @@ -10,7 +10,7 @@ export const MAX_32BITS = 2147483647; function getSelectedTooltip( currentTooltip: IUSerInterfaceTooltip, - tooltip_data: ITableTooltips | undefined, + tooltip_data: DataTooltips, tooltip_conditional: ConditionalTooltip[], tooltip_static: ITableStaticTooltips, virtualized: IVirtualizedDerivedData @@ -25,15 +25,12 @@ function getSelectedTooltip( return undefined; } - const legacyTooltip = tooltip_data && - tooltip_data[id] && - ( - tooltip_data[id].length > row ? - tooltip_data[id][row] : - null - ); - - const staticTooltip = tooltip_static[id]; + const appliedStaticTooltip = ( + tooltip_data && + tooltip_data.length > row && + tooltip_data[row] && + tooltip_data[row][id] + ) || tooltip_static[id]; const conditionalTooltips = R.filter(tt => { return !tt.if || @@ -46,7 +43,7 @@ function getSelectedTooltip( return conditionalTooltips.length ? conditionalTooltips.slice(-1)[0] : - legacyTooltip || staticTooltip; + appliedStaticTooltip; } function convertDelay(delay: number | null) { @@ -75,7 +72,7 @@ function getDuration(duration: number | null | undefined, defaultTo: number) { export default memoizeOne(( currentTooltip: IUSerInterfaceTooltip, - tooltip_data: ITableTooltips | undefined, + tooltip_data: DataTooltips, tooltip_conditional: ConditionalTooltip[], tooltip_static: ITableStaticTooltips, virtualized: IVirtualizedDerivedData, diff --git a/tests/visual/percy-storybook/Dropdown.percy.tsx b/tests/visual/percy-storybook/Dropdown.percy.tsx index eb11262f9..4ed2fc0bf 100644 --- a/tests/visual/percy-storybook/Dropdown.percy.tsx +++ b/tests/visual/percy-storybook/Dropdown.percy.tsx @@ -92,22 +92,30 @@ storiesOf('DashTable/Dropdown', module) data={data2} columns={columns2} editable={true} - dropdown_data={{ - Neighborhood: [{ - dropdown: R.map( - i => ({ label: i, value: i }), - ['Brooklyn', 'Queens', 'Staten Island'] - ) - }, { - dropdown: R.map( - i => ({ label: i, value: i }), - ['Mile End', 'Plateau', 'Hochelaga'] - ) - }, { - dropdown: R.map( - i => ({ label: i, value: i }), - ['Venice', 'Hollywood', 'Los Feliz'] - ) - }] - }} + dropdown_data={[ + { + Neighborhood: { + dropdown: R.map( + i => ({ label: i, value: i }), + ['Brooklyn', 'Queens', 'Staten Island'] + ) + } + }, + { + Neighborhood: { + dropdown: R.map( + i => ({ label: i, value: i }), + ['Mile End', 'Plateau', 'Hochelaga'] + ) + } + }, + { + Neighborhood: { + dropdown: R.map( + i => ({ label: i, value: i }), + ['Venice', 'Hollywood', 'Los Feliz'] + ) + } + } + ]} />)); \ No newline at end of file From 36d9ec059543baaf1870e862bf9acf1e5dcc6635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Wed, 5 Jun 2019 09:39:28 -0400 Subject: [PATCH 29/46] rename `dropdown` nest prop `options` --- src/dash-table/components/Table/props.ts | 2 +- src/dash-table/dash/DataTable.js | 6 +++--- src/dash-table/derived/cell/contents.tsx | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/dash-table/components/Table/props.ts b/src/dash-table/components/Table/props.ts index 0ae16712c..24308329a 100644 --- a/src/dash-table/components/Table/props.ts +++ b/src/dash-table/components/Table/props.ts @@ -186,7 +186,7 @@ export interface IDropdownValue { export interface IDropdown { clearable?: boolean; - dropdown: IDropdownValue[]; + options: IDropdownValue[]; } export interface IConditionalDropdown extends IDropdown { diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index eafeb4b20..c01905d46 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -600,7 +600,7 @@ export const propTypes = { */ dropdown: PropTypes.objectOf(PropTypes.exact({ clearable: PropTypes.bool, - dropdown: PropTypes.arrayOf(PropTypes.exact({ + options: PropTypes.arrayOf(PropTypes.exact({ label: PropTypes.string.isRequired, value: PropTypes.oneOfType([ PropTypes.number, @@ -627,7 +627,7 @@ export const propTypes = { column_id: PropTypes.string, filter_query: PropTypes.string }), - dropdown: PropTypes.arrayOf(PropTypes.exact({ + options: PropTypes.arrayOf(PropTypes.exact({ label: PropTypes.string.isRequired, value: PropTypes.oneOfType([ PropTypes.number, @@ -643,7 +643,7 @@ export const propTypes = { PropTypes.objectOf( PropTypes.exact({ clearable: PropTypes.bool, - dropdown: PropTypes.arrayOf(PropTypes.exact({ + options: PropTypes.arrayOf(PropTypes.exact({ label: PropTypes.string.isRequired, value: PropTypes.oneOfType([ PropTypes.number, diff --git a/src/dash-table/derived/cell/contents.tsx b/src/dash-table/derived/cell/contents.tsx index 81b6452f8..ffe4a8067 100644 --- a/src/dash-table/derived/cell/contents.tsx +++ b/src/dash-table/derived/cell/contents.tsx @@ -83,13 +83,13 @@ class Contents { 'dash-cell-value' ].join(' '); - switch (getCellType(active, isEditable, dropdown && dropdown.dropdown, column.presentation)) { + switch (getCellType(active, isEditable, dropdown && dropdown.options, column.presentation)) { case CellType.Dropdown: return (); From f0298e6f27dd01042a1ab91e39cc26149e6f5fbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Wed, 5 Jun 2019 09:43:33 -0400 Subject: [PATCH 30/46] ignore `inst` during lint --- tslint.json | 1 + 1 file changed, 1 insertion(+) diff --git a/tslint.json b/tslint.json index 9fbffe5c6..74acc28ba 100644 --- a/tslint.json +++ b/tslint.json @@ -7,6 +7,7 @@ "exclude": [ ".config/**", "cypress/**", + "inst/**", "node_modules/**", "@Types/**", "venv/**" From 2cc9acc68fa31b9f4cb2ed01a9cb019b1c48f3cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Wed, 5 Jun 2019 11:46:03 -0400 Subject: [PATCH 31/46] dropdown -> options --- demo/AppMode.ts | 2 +- tests/visual/percy-storybook/Dropdown.percy.tsx | 16 ++++++++-------- tests/visual/percy-storybook/fixtures.ts | 12 ++++++------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/demo/AppMode.ts b/demo/AppMode.ts index 66156f66c..97aec6b10 100644 --- a/demo/AppMode.ts +++ b/demo/AppMode.ts @@ -52,7 +52,7 @@ function getBaseTableProps(mock: IDataMock) { dropdown: { bbb: { clearable: true, - dropdown: ['Humid', 'Wet', 'Snowy', 'Tropical Beaches'].map(i => ({ + options: ['Humid', 'Wet', 'Snowy', 'Tropical Beaches'].map(i => ({ label: i, value: i })) diff --git a/tests/visual/percy-storybook/Dropdown.percy.tsx b/tests/visual/percy-storybook/Dropdown.percy.tsx index 4ed2fc0bf..a5d7864d7 100644 --- a/tests/visual/percy-storybook/Dropdown.percy.tsx +++ b/tests/visual/percy-storybook/Dropdown.percy.tsx @@ -38,13 +38,13 @@ storiesOf('DashTable/Dropdown', module) editable={true} dropdown={{ climate: { - dropdown: R.map( + options: R.map( i => ({ label: i, value: i }), ['Sunny', 'Snowy', 'Rainy'] ) }, city: { - dropdown: R.map( + options: R.map( i => ({ label: i, value: i }), ['NYC', 'Montreal', 'Miami'] ) @@ -62,7 +62,7 @@ storiesOf('DashTable/Dropdown', module) column_id: 'Neighborhood', filter_query: '{City} eq "NYC"' }, - dropdown: R.map( + options: R.map( i => ({ label: i, value: i }), ['Brooklyn', 'Queens', 'Staten Island'] ) @@ -71,7 +71,7 @@ storiesOf('DashTable/Dropdown', module) column_id: 'Neighborhood', filter_query: '{City} eq "Montreal"' }, - dropdown: R.map( + options: R.map( i => ({ label: i, value: i }), ['Mile End', 'Plateau', 'Hochelaga'] ) @@ -81,7 +81,7 @@ storiesOf('DashTable/Dropdown', module) column_id: 'Neighborhood', filter_query: '{City} eq "Los Angeles"' }, - dropdown: R.map( + options: R.map( i => ({ label: i, value: i }), ['Venice', 'Hollywood', 'Los Feliz'] ) @@ -95,7 +95,7 @@ storiesOf('DashTable/Dropdown', module) dropdown_data={[ { Neighborhood: { - dropdown: R.map( + options: R.map( i => ({ label: i, value: i }), ['Brooklyn', 'Queens', 'Staten Island'] ) @@ -103,7 +103,7 @@ storiesOf('DashTable/Dropdown', module) }, { Neighborhood: { - dropdown: R.map( + options: R.map( i => ({ label: i, value: i }), ['Mile End', 'Plateau', 'Hochelaga'] ) @@ -111,7 +111,7 @@ storiesOf('DashTable/Dropdown', module) }, { Neighborhood: { - dropdown: R.map( + options: R.map( i => ({ label: i, value: i }), ['Venice', 'Hollywood', 'Los Feliz'] ) diff --git a/tests/visual/percy-storybook/fixtures.ts b/tests/visual/percy-storybook/fixtures.ts index 9c5d933eb..18b80be77 100644 --- a/tests/visual/percy-storybook/fixtures.ts +++ b/tests/visual/percy-storybook/fixtures.ts @@ -15,7 +15,7 @@ export default [ ], dropdown: { 'column-1': { - dropdown: [ + options: [ { label: 'Montréal', value: 'mtl' }, { label: 'San Francisco', value: 'sf' } ] @@ -139,7 +139,7 @@ export default [ ], dropdown: { 'column-1': { - dropdown: [ + options: [ { label: 'Montréal', value: 'mtl' }, { label: 'San Francisco', value: 'sf' } ] @@ -185,19 +185,19 @@ export default [ ], dropdown: { 'column-1': { - dropdown: [ + options: [ { label: 'Montréal', value: 'mtl' }, { label: 'San Francisco', value: 'sf' } ] }, 'column-2': { - dropdown: [ + options: [ { label: 'Montréal', value: 'mtl' }, { label: 'San Francisco', value: 'sf' } ] }, 'column-3': { - dropdown: [ + options: [ { label: 'Montréal', value: 'mtl' }, { label: 'San Francisco', value: 'sf' } ] @@ -246,7 +246,7 @@ export default [ ], dropdown: { region: { - dropdown: [ + options: [ { label: 'Hawaii', value: 'hawaii' }, { label: 'Costa Rica', value: 'costa-rica' } ] From c06a637b4e4e63a17f436b30b4f1da1c2c1873f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Wed, 5 Jun 2019 13:02:13 -0400 Subject: [PATCH 32/46] update docstrings --- src/dash-table/dash/DataTable.js | 111 +++++++++++++++++-------------- 1 file changed, 62 insertions(+), 49 deletions(-) diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index c01905d46..6e377c2cc 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -112,9 +112,9 @@ export const propTypes = { * button on the column. * If there are merged, multi-header columns then you can choose * which column header row to display the "x" in by - * supplying a column row index. - * For example, `0` will display the "x" on the first row, - * `1` on the second row. + * supplying an array of booleans. + * For example, `[true, false]` will display the "x" on the first row, + * but not the second row. * If the "x" appears on a merged column, then clicking on that button * will delete *all* of the merged columns associated with it. */ @@ -140,7 +140,9 @@ export const propTypes = { * If True, then the name of this column is editable. * If there are multiple column headers (if `name` is a list of strings), * then `renamable` can refer to _which_ column header should be - * editable by setting it to the column header index. + * editable by defining an array of booleans. + * For example, `[true, false]` will make the first row's name editable, + * but not the second row. * Also, updating the name in a merged column header cell will * update the name of each column. */ @@ -252,6 +254,16 @@ export const propTypes = { }), /** + * There are two `sort_as_none` flags in the table. + * This is the column-level editable flag and there is + * also the table-level `sort_as_none` flag. + * + * These flags determine how the content of the table is + * sorted. + * + * If the column-level `sort_as_none` flag is set it overrides + * the table-level `sort_as_none` flag for that column. + * * An array of string, number and boolean values that are treated as `None` * when sorting is applied to the column. */ @@ -433,8 +445,14 @@ export const propTypes = { * `fixed_columns` will "fix" the set of columns so that * they remain visible when scrolling horizontally across * the unfixed columns. `fixed_columns` fixes columns - * from left-to-right, so `fixed_columns=3` will fix - * the first 3 columns. + * from left-to-right. + * + * If `headers` is False, no columns are fixed. + * If `headers` is True, all operation columns (see `row_deletable` and `row_selectable`) + * are fixed. Additional data columns can be fixed by + * assigning a number to `data`. + * + * Defaults to `{ headers: False }`. * * Note that fixing columns introduces some changes to the * underlying markup of the table and may impact the @@ -456,10 +474,14 @@ export const propTypes = { * `fixed_rows` will "fix" the set of rows so that * they remain visible when scrolling vertically down * the table. `fixed_rows` fixes rows - * from top-to-bottom, starting from the headers, - * so `fixed_rows=1` will fix the header row, - * `fixed_rows=2` will fix the header row and the first row, - * or the first two header rows (if there are multiple headers). + * from top-to-bottom, starting from the headers. + * + * If `headers` is False, no rows are fixed. + * If `headers` is True, all header and filter rows (see `filter_action`) are + * fixed. Additional data rows can be fixed by assigning + * a number to `data`. + * + * Defaults to `{ headers: False }`. * * Note that fixing rows introduces some changes to the * underlying markup of the table and may impact the @@ -549,7 +571,7 @@ export const propTypes = { style_as_list_view: PropTypes.bool, /** - * "pagination" refers to a mode of the table where + * `page_action` refers to a mode of the table where * not all of the rows are displayed at once: only a subset * are displayed (a "page") and the next subset of rows * can viewed by clicking "Next" or "Previous" buttons @@ -564,13 +586,11 @@ export const propTypes = { * or we can update the data on-the-fly with callbacks * when the user clicks on the "Previous" or "Next" buttons. * These modes can be toggled with this `page_action` parameter: - * - `'fe'` refers to "front-end" paging: passing large data up-front - * - `'be'` refers to "back-end" paging: updating the data on the fly via callbacks - * - `False` will disable paging, attempting to render all of the data at once - * - `True` is the same as `fe` - * - * NOTE: The `fe` and `be` names may change in the future. - * Tune in to [https://github.com/plotly/dash-table/issues/167](https://github.com/plotly/dash-table/issues/167) for more. + * - `'native'`: all data is passed to the table up-front, paging logic is + * handled by the table + * - `'custom'`: data is passed to the table one page at a time, paging logic + * is handled via callbacks + * - `none`: disables paging, render all of the data at once */ page_action: PropTypes.oneOf(['custom', 'native', 'none']), @@ -588,15 +608,11 @@ export const propTypes = { page_size: PropTypes.number, /** - * `dropdown` represents the available dropdown - * options for different columns. - * The `id` property refers to the column ID. - * The `dropdown` property refers to the `options` of the - * dropdown. + * `dropdown` specifies dropdown options for different columns. * - * NOTE: The naming and the behavior of this option may change - * in the future. - * Tune in to [https://github.com/plotly/dash-table/issues/168](https://github.com/plotly/dash-table/issues/168) + * Each entry refers to the column ID. + * The `clearable` property defines whether the value can be deleted. + * The `options` property refers to the `options` of the dropdown. */ dropdown: PropTypes.objectOf(PropTypes.exact({ clearable: PropTypes.bool, @@ -610,16 +626,12 @@ export const propTypes = { })), /** - * `dropdown_conditional` specifies the available options - * for dropdowns in various columns and cells. + * `dropdown_conditional` specifies dropdown options in various columns and cells. + * * This property allows you to specify different dropdowns * depending on certain conditions. For example, you may * render different "city" dropdowns in a row depending on the * current value in the "state" column. - * - * NOTE: The naming and the behavior of this option may change - * in the future. - * Tune in to [https://github.com/plotly/dash-table/issues/168](https://github.com/plotly/dash-table/issues/168) */ dropdown_conditional: PropTypes.arrayOf(PropTypes.exact({ clearable: PropTypes.bool, @@ -637,7 +649,10 @@ export const propTypes = { })), /** + * `dropdown_data` specifies dropdown options on a row-by-row, column-by-column basis. * + * Each item in the array corresponds to the corresponding dropdowns for the `data` item + * at the same index. Each entry in the item refers to the Column ID. */ dropdown_data: PropTypes.arrayOf( PropTypes.objectOf( @@ -813,38 +828,36 @@ export const propTypes = { * If `filter_action` is enabled, then the current filtering * string is represented in this `filter_query` * property. - * NOTE: The shape and structure of this property will - * likely change in the future. - * Stay tuned in [https://github.com/plotly/dash-table/issues/169](https://github.com/plotly/dash-table/issues/169) */ filter_query: PropTypes.string, /** * The `filter_action` property controls the behavior of the `filtering` UI. - * If `False`, then the filtering UI is not displayed - * If `fe` or True, then the filtering UI is displayed and the filtering - * happens in the "front-end". That is, it is performed on the data + * + * If `'none'`, then the filtering UI is not displayed + * If `'native'`, then the filtering UI is displayed and the filtering + * logic is handled by the table. That is, it is performed on the data * that exists in the `data` property. - * If `be`, then the filtering UI is displayed but it is the + * If `'custom'`, then the filtering UI is displayed but it is the * responsibility of the developer to program the filtering - * through a callback (where `filter_query` would be the input + * through a callback (where `filter_query` or `derived_filter_query_structure` would be the input * and `data` would be the output). - * - * NOTE - Several aspects of filtering may change in the future, - * including the naming of this property. - * Tune in to [https://github.com/plotly/dash-table/issues/167](https://github.com/plotly/dash-table/issues/167) */ filter_action: PropTypes.oneOf(['custom', 'native', 'none']), /** * The `sort_action` property enables data to be * sorted on a per-column basis. - * Enabling `sort_action` will display a UI element - * on each of the columns (up and down arrows). * - * Sorting can be performed in the "front-end" - * with the `fe` (or True) setting or via a callback in your - * python "back-end" with the `be` setting. + * If `'none'`, then the sorting UI is not displayed. + * If `'native'`, then the sorting UI is displayed and the sorting + * logic is hanled by the table. That is, it is performed on the data + * that exists in the `data` property. + * If `'custom'`, the the sorting UI is displayed but it is the + * responsibility of the developer to program the sorting + * through a callback (where `sort_by` would be the input and `data` + * would be the output). + * * Clicking on the sort arrows will update the * `sort_by` property. */ From 9f58e8a86fde7cc1d369e5774e96917f2f1adc54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Wed, 5 Jun 2019 13:35:31 -0400 Subject: [PATCH 33/46] update changelog --- CHANGELOG.md | 80 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b7d78ad1..57e4909f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,16 +3,84 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Changed +[#446](https://github.com/plotly/dash-table/pull/446) +- Table API rework +#### NEW + - `column.sort_as_none`: Allows sorting behavior customization. + Accepts an array of string, number or booleans. + - `sort_as_none`: Allows sorting behavior customization. + Accepts an array of string, number or booleans. + +#### REMOVED + - `column.clearable`: Allows clearing the value of a dropdown cell. + Removed in favor of `dropdown_**` `clearable` nested property + - `column.options` + Removed. Redundant with `dropdown`. + - `pagination_settings` + Replaced by two props `page_current` and `page_size`. + +#### RENAMED + - `column_static_tooltip` + Renamed `tooltip`. + - `column_conditional_tooltips` + Renamed `tooltip_conditional`. + - `filter` + Renamed `filter_query`. + - `sort_type` + Renamed `sort_mode`. + - `derived_filter_structure` + Renamed to `derived_filter_query_structure`. + +#### MODIFIED + - `column.deletable`: Allows column deletion. + Now accepts a boolean or an array of booleans (for multi-line headers) + - `column.editable_name`: Allows column renaming. + Renamed to `column.renamable` + Now accepts a boolean or an array of booleans (for multi-line headers) + - `column.id` + Now accepts `string` only -- `number` column ids can be casted to string + - `n_fixed_columns`: Will fix columns to the left. + Renamed to `fixed_columns` + Now accepts an object { headers: boolean, data: number } instead of a number. + { headers: true } determines the number of columns to fix automatically. + - `n_fixed_rows`: Will fix rows to the top. + Renamed to `fixed_rows` + Now accepts an object { headers: boolean, data: number } instead of a number. + { headers: true } determines the number of rows to fix automatically. + - `pagination_mode` + Renamed to `page_action`. + - `column_static_dropdown` + Renamed `dropdown`. + Now an object with each entry refering to a Column ID. Each nested prop expects + `clearable` and `options`. + - `column_conditional_dropdowns` + Renamed to `dropdown_conditional`. + `condition` changed to the same `if` nested prop used by styles. + `dropdown` renamed to `options`. + - `dropdown_properties` + Renamed to `dropdown_data`. + Matches the `data` structure. + - `tooltips` + Renamed `tooltip_data`. + Matches the `data` structure. + - `filtering` + Renamed `filter_action`. + - `sorting` + Renamed `sort_action`. + - `style_**_conditional` + Renamed `filter` to `filter_query`. + ### Added [#456](https://github.com/plotly/dash-table/issues/456) - Support for dash-table is now available for R users of Dash. ### Fixed [#434](https://github.com/plotly/dash-table/issues/434) -- Fix CSS borders propeties overwrite style_* borders properties. +- Fix CSS borders properties overwrite style_* borders properties. [#435](https://github.com/plotly/dash-table/issues/435) -- selected_cells background color is set through styling pipeline / derivations. +- selected_cells background color is set through styling pipeline / derivations. ## [3.7.0] - 2019-05-15 ### Added @@ -315,8 +383,8 @@ The remote URL path for the bundle was incorrect. } A+B = { - background_color: 'floralwhite', // from A, not overriden - color: 'black', // from B, A overriden + background_color: 'floralwhite', // from A, not overridden + color: 'black', // from B, A overridden font_size: 22, // from B font_type: 'monospace', // from A width: 100 // from A @@ -352,7 +420,7 @@ The remote URL path for the bundle was incorrect. if: { column_id: string | number, header_index: number | 'odd' | 'even' }, ...CSSProperties }] - - All CSSProperties are supported in kebab-cass, camelCase and snake_case + - All CSSProperties are supported in kebab-case, camelCase and snake_case ### Changed - Renaming 'dataframe' props to 'data' @@ -637,7 +705,7 @@ Freeze Top Rows (Limitations) Freeze Left Columns (Limitations) - performance is highly impacted if the table is in a scrollable container as the frozen columns position has to be recalculated on each scroll event; impact is minimal up to 50-100 items and makes the table difficult to use with 250-500 items - can't freeze rows and columns at the same time -- when using merged headers, make sure that the number of fixed columns respects the merged headers, otherwise there will be some unresolved visual bugs/artefacts +- when using merged headers, make sure that the number of fixed columns respects the merged headers, otherwise there will be some unresolved visual bugs/artifacts - rows are assumed to all have the same height Deletable Columns (Limitations) From 98834e4b4734e29fe54bff65bab3799d3935b894 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Thu, 6 Jun 2019 13:21:10 -0400 Subject: [PATCH 34/46] clean up console errors in standalone mode --- demo/data.ts | 7 ------- src/dash-table/dash/DataTable.js | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/demo/data.ts b/demo/data.ts index c38b8a2d2..0b3df6de5 100644 --- a/demo/data.ts +++ b/demo/data.ts @@ -58,7 +58,6 @@ export const generateMockData = (rows: number) => unpackIntoColumnsAndData([ name: ['', 'Weather', 'Climate'], type: ColumnType.Text, presentation: 'dropdown', - clearable: true, data: gendata( i => ['Humid', 'Wet', 'Snowy', 'Tropical Beaches'][i % 4], rows @@ -141,7 +140,6 @@ export const generateSpaceMockData = (rows: number) => unpackIntoColumnsAndData( name: ['', 'Weather', 'Climate'], type: ColumnType.Text, presentation: 'dropdown', - clearable: true, data: gendata( i => ['Humid', 'Wet', 'Snowy', 'Tropical Beaches'][i % 4], rows @@ -162,11 +160,6 @@ export const mockDataSimple = (rows: number) => unpackIntoColumnsAndData([ name: 'Climate', type: ColumnType.Text, presentation: 'dropdown', - options: ['Humid', 'Wet', 'Snowy', 'Tropical Beaches'].map(i => ({ - label: i, - value: i - })), - clearable: true, data: gendata( i => ['Humid', 'Wet', 'Snowy', 'Tropical Beaches'][i % 4], rows diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index 6e377c2cc..e872b7a69 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -57,7 +57,7 @@ export const defaultProps = { dropdown: {}, dropdown_conditional: [], - dropdown_data: {}, + dropdown_data: [], fixed_columns: { headers: false, From 3586758ee4d77d86b568f1b6b9b3e1af25694814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Fri, 7 Jun 2019 08:45:57 -0400 Subject: [PATCH 35/46] sort_as_none changelog --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57e4909f1..00f025e2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,6 @@ This project adheres to [Semantic Versioning](http://semver.org/). #### NEW - `column.sort_as_none`: Allows sorting behavior customization. Accepts an array of string, number or booleans. - - `sort_as_none`: Allows sorting behavior customization. - Accepts an array of string, number or booleans. #### REMOVED - `column.clearable`: Allows clearing the value of a dropdown cell. @@ -68,6 +66,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). Renamed `filter_action`. - `sorting` Renamed `sort_action`. + - `sorting_treat_empty_string_as_none` + Renamed `sort_as_none`. + Now accepts an array of string, number or booleans. + Table-level prop for the `column.sort_as_none` column nested prop. - `style_**_conditional` Renamed `filter` to `filter_query`. From ce68fc6b7f6987c3038dc9de8fd2a5d7e2514e9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Fri, 7 Jun 2019 09:49:10 -0400 Subject: [PATCH 36/46] sort_as_null --- CHANGELOG.md | 6 +++--- src/dash-table/components/Table/props.ts | 10 +++++----- src/dash-table/dash/DataTable.js | 18 +++++++++--------- src/dash-table/dash/sanitize.ts | 8 ++++---- src/dash-table/derived/data/virtual.ts | 6 +++--- src/dash-table/utils/applyClipboardToData.ts | 2 +- .../visual/percy-storybook/DashTable.percy.tsx | 8 ++++---- 7 files changed, 29 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00f025e2b..6f5384f68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). [#446](https://github.com/plotly/dash-table/pull/446) - Table API rework #### NEW - - `column.sort_as_none`: Allows sorting behavior customization. + - `column.sort_as_null`: Allows sorting behavior customization. Accepts an array of string, number or booleans. #### REMOVED @@ -67,9 +67,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). - `sorting` Renamed `sort_action`. - `sorting_treat_empty_string_as_none` - Renamed `sort_as_none`. + Renamed `sort_as_null`. Now accepts an array of string, number or booleans. - Table-level prop for the `column.sort_as_none` column nested prop. + Table-level prop for the `column.sort_as_null` column nested prop. - `style_**_conditional` Renamed `filter` to `filter_query`. diff --git a/src/dash-table/components/Table/props.ts b/src/dash-table/components/Table/props.ts index 24308329a..8f62fc665 100644 --- a/src/dash-table/components/Table/props.ts +++ b/src/dash-table/components/Table/props.ts @@ -73,7 +73,7 @@ export type RowSelection = 'single' | 'multi' | false; export type SelectedCells = ICellCoordinates[]; export type SetProps = (...args: any[]) => void; export type SetState = (state: Partial) => void; -export type SortAsNone = (string | number | boolean)[]; +export type SortAsNull = (string | number | boolean)[]; export type VisibleColumns = IVisibleColumn[]; export enum ChangeAction { @@ -157,7 +157,7 @@ export interface IBaseVisibleColumn { deletable?: boolean | boolean[]; editable?: boolean; renamable?: boolean | boolean[]; - sort_as_none: SortAsNone; + sort_as_null: SortAsNull; id: ColumnId; name: string | string[]; } @@ -286,7 +286,7 @@ export interface IProps { sort_action?: TableAction; sort_by?: SortBy; sort_mode?: SortMode; - sort_as_none?: SortAsNone; + sort_as_null?: SortAsNull; style_as_list_view?: boolean; page_action?: TableAction; page_current?: number; @@ -329,7 +329,7 @@ interface IDefaultProps { sort_action: TableAction; sort_by: SortBy; sort_mode: SortMode; - sort_as_none: SortAsNone; + sort_as_null: SortAsNull; style_as_list_view: boolean; tooltip_data: DataTooltips; @@ -372,7 +372,7 @@ export type SanitizedProps = Omit< Merge, 'locale_format' >, - 'sort_as_none' + 'sort_as_null' >; export type SanitizedAndDerivedProps = SanitizedProps & IDerivedProps; diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index e872b7a69..a813eb69f 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -38,7 +38,7 @@ export const defaultProps = { css: [], filter_query: '', filter_action: 'none', - sort_as_none: [], + sort_as_null: [], sort_action: 'none', sort_mode: 'single', sort_by: [], @@ -254,20 +254,20 @@ export const propTypes = { }), /** - * There are two `sort_as_none` flags in the table. + * There are two `sort_as_null` flags in the table. * This is the column-level editable flag and there is - * also the table-level `sort_as_none` flag. + * also the table-level `sort_as_null` flag. * * These flags determine how the content of the table is * sorted. * - * If the column-level `sort_as_none` flag is set it overrides - * the table-level `sort_as_none` flag for that column. + * If the column-level `sort_as_null` flag is set it overrides + * the table-level `sort_as_null` flag for that column. * * An array of string, number and boolean values that are treated as `None` * when sorting is applied to the column. */ - sort_as_none: PropTypes.arrayOf(PropTypes.oneOfType([ + sort_as_null: PropTypes.arrayOf(PropTypes.oneOfType([ PropTypes.string, PropTypes.number, PropTypes.bool @@ -465,7 +465,7 @@ export const propTypes = { data: PropTypes.oneOf([0]) }), PropTypes.exact({ - headers: PropTypes.oneOf([true]), + headers: PropTypes.oneOf([true]).isRequired, data: PropTypes.number }) ]), @@ -894,11 +894,11 @@ export const propTypes = { /** * An array of string, number and boolean values that are treated as `None` - * when sorting. This value will be used by columns without `sort_as_none`. + * when sorting. This value will be used by columns without `sort_as_null`. * * Defaults to `[]`. */ - sort_as_none: PropTypes.arrayOf(PropTypes.oneOfType([ + sort_as_null: PropTypes.arrayOf(PropTypes.oneOfType([ PropTypes.string, PropTypes.number, PropTypes.bool diff --git a/src/dash-table/dash/sanitize.ts b/src/dash-table/dash/sanitize.ts index 6d0204ae8..e90579bf1 100644 --- a/src/dash-table/dash/sanitize.ts +++ b/src/dash-table/dash/sanitize.ts @@ -11,7 +11,7 @@ import { PropsWithDefaults, RowSelection, SanitizedProps, - SortAsNone, + SortAsNull, TableAction } from 'dash-table/components/Table/props'; import headerRows from 'dash-table/derived/header/headerRows'; @@ -31,10 +31,10 @@ const DEFAULT_SPECIFIER = ''; const applyDefaultToLocale = memoizeOne((locale: INumberLocale) => getLocale(locale)); const applyDefaultsToColumns = memoizeOne( - (defaultLocale: INumberLocale, defaultSort: SortAsNone, columns: Columns) => R.map(column => { + (defaultLocale: INumberLocale, defaultSort: SortAsNull, columns: Columns) => R.map(column => { const c = R.clone(column); - c.sort_as_none = c.sort_as_none || defaultSort; + c.sort_as_null = c.sort_as_null || defaultSort; if (c.type === ColumnType.Numeric && c.format) { c.format.locale = getLocale(defaultLocale, c.format.locale); @@ -67,7 +67,7 @@ export default (props: PropsWithDefaults): SanitizedProps => { const locale_format = applyDefaultToLocale(props.locale_format); return R.merge(props, { - columns: applyDefaultsToColumns(locale_format, props.sort_as_none, props.columns), + columns: applyDefaultsToColumns(locale_format, props.sort_as_null, props.columns), fixed_columns: getFixedColumns(props.fixed_columns, props.row_deletable, props.row_selectable), fixed_rows: getFixedRows(props.fixed_rows, props.columns, props.filter_action), locale_format diff --git a/src/dash-table/derived/data/virtual.ts b/src/dash-table/derived/data/virtual.ts index 0c25b2d02..186057541 100644 --- a/src/dash-table/derived/data/virtual.ts +++ b/src/dash-table/derived/data/virtual.ts @@ -7,7 +7,7 @@ import { Data, Datum, IDerivedData, - SortAsNone, + SortAsNull, VisibleColumns, TableAction } from 'dash-table/components/Table/props'; @@ -36,10 +36,10 @@ const getter = ( const getNullyCases = ( columnId: ColumnId - ): SortAsNone => { + ): SortAsNull => { const column = R.find(c => c.id === columnId, columns); - return (column && column.sort_as_none) || []; + return (column && column.sort_as_null) || []; }; const isNully = ( diff --git a/src/dash-table/utils/applyClipboardToData.ts b/src/dash-table/utils/applyClipboardToData.ts index 6a46f3001..e0e731a83 100644 --- a/src/dash-table/utils/applyClipboardToData.ts +++ b/src/dash-table/utils/applyClipboardToData.ts @@ -37,7 +37,7 @@ export default ( id: `Column ${i + 1}`, name: `Column ${i + 1}`, type: ColumnType.Any, - sort_as_none: [] + sort_as_null: [] }); newData.forEach(row => (row[`Column ${i}`] = '')); } diff --git a/tests/visual/percy-storybook/DashTable.percy.tsx b/tests/visual/percy-storybook/DashTable.percy.tsx index f1f54be17..64916f4cc 100644 --- a/tests/visual/percy-storybook/DashTable.percy.tsx +++ b/tests/visual/percy-storybook/DashTable.percy.tsx @@ -261,7 +261,7 @@ storiesOf('DashTable/Sorting', module) columns={mergedColumns} sort_action={TableAction.Native} sort_by={[{ column_id: 'a', direction: 'asc' }]} - sort_as_none={['']} + sort_as_null={['']} style_data_conditional={style_data_conditional} />)) .add('"a" descending -- empty string override', () => ()) .add(`"a" descending -- '' & 426 override`, () => ()) .add(`"a" ascending -- '' and 426 override`, () => ()); storiesOf('DashTable/Without id', module) From 42075a27c75730a76015f0c72531844928bf4752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Fri, 7 Jun 2019 11:16:14 -0400 Subject: [PATCH 37/46] flags and props --- src/dash-table/dash/DataTable.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index a813eb69f..6109d64b6 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -254,15 +254,15 @@ export const propTypes = { }), /** - * There are two `sort_as_null` flags in the table. - * This is the column-level editable flag and there is - * also the table-level `sort_as_null` flag. + * There are two `sort_as_null` props in the table. + * This is the column-level prop and there is + * also the table-level `sort_as_null` prop. * - * These flags determine how the content of the table is + * These props determine how the content of the table is * sorted. * - * If the column-level `sort_as_null` flag is set it overrides - * the table-level `sort_as_null` flag for that column. + * If the column-level `sort_as_null` is set it overrides + * the table-level `sort_as_null` for that column. * * An array of string, number and boolean values that are treated as `None` * when sorting is applied to the column. From 799369ac4a6ebed08cd9c570184c6c4232fffdd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Fri, 7 Jun 2019 12:16:03 -0400 Subject: [PATCH 38/46] typing, changelog --- CHANGELOG.md | 28 ++++++++++++++-------------- src/dash-table/dash/DataTable.js | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f5384f68..be084427a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). #### REMOVED - `column.clearable`: Allows clearing the value of a dropdown cell. - Removed in favor of `dropdown_**` `clearable` nested property + Removed in favor of `dropdown_**` `clearable` nested property. - `column.options` Removed. Redundant with `dropdown`. - `pagination_settings` @@ -20,24 +20,24 @@ This project adheres to [Semantic Versioning](http://semver.org/). #### RENAMED - `column_static_tooltip` - Renamed `tooltip`. + Renamed to `tooltip`. - `column_conditional_tooltips` - Renamed `tooltip_conditional`. + Renamed to `tooltip_conditional`. - `filter` - Renamed `filter_query`. + Renamed to `filter_query`. - `sort_type` - Renamed `sort_mode`. + Renamed to `sort_mode`. - `derived_filter_structure` Renamed to `derived_filter_query_structure`. #### MODIFIED - `column.deletable`: Allows column deletion. - Now accepts a boolean or an array of booleans (for multi-line headers) + Now accepts a boolean or an array of booleans (for multi-line headers). - `column.editable_name`: Allows column renaming. Renamed to `column.renamable` - Now accepts a boolean or an array of booleans (for multi-line headers) + Now accepts a boolean or an array of booleans (for multi-line headers). - `column.id` - Now accepts `string` only -- `number` column ids can be casted to string + Now accepts `string` only -- `number` column ids can be casted to string. - `n_fixed_columns`: Will fix columns to the left. Renamed to `fixed_columns` Now accepts an object { headers: boolean, data: number } instead of a number. @@ -49,8 +49,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). - `pagination_mode` Renamed to `page_action`. - `column_static_dropdown` - Renamed `dropdown`. - Now an object with each entry refering to a Column ID. Each nested prop expects + Renamed to `dropdown`. + Now an object with each entry refering to a Column ID. Each nested prop expects. `clearable` and `options`. - `column_conditional_dropdowns` Renamed to `dropdown_conditional`. @@ -60,14 +60,14 @@ This project adheres to [Semantic Versioning](http://semver.org/). Renamed to `dropdown_data`. Matches the `data` structure. - `tooltips` - Renamed `tooltip_data`. + Renamed to `tooltip_data`. Matches the `data` structure. - `filtering` - Renamed `filter_action`. + Renamed to `filter_action`. - `sorting` - Renamed `sort_action`. + Renamed to `sort_action`. - `sorting_treat_empty_string_as_none` - Renamed `sort_as_null`. + Renamed to `sort_as_null`. Now accepts an array of string, number or booleans. Table-level prop for the `column.sort_as_null` column nested prop. - `style_**_conditional` diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index 6109d64b6..f39587bf3 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -494,7 +494,7 @@ export const propTypes = { data: PropTypes.oneOf([0]) }), PropTypes.exact({ - headers: PropTypes.oneOf([true]), + headers: PropTypes.oneOf([true]).isRequired, data: PropTypes.number }) ]), From 55703a4a896987f877cffed5b509514131cdaf6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Rivet?= Date: Fri, 7 Jun 2019 12:17:48 -0400 Subject: [PATCH 39/46] Update CHANGELOG.md Co-Authored-By: Chris Parmer --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be084427a..ee90abbb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,7 +45,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). - `n_fixed_rows`: Will fix rows to the top. Renamed to `fixed_rows` Now accepts an object { headers: boolean, data: number } instead of a number. - { headers: true } determines the number of rows to fix automatically. + { headers: true } determines the number of rows to fix automatically (i.e. if there are multiple headers, it will fix all of them as well as the filter row). + { headers: true, data: 2} would fix all of the header rows as well as the first 2 data rows. - `pagination_mode` Renamed to `page_action`. - `column_static_dropdown` From 4f3c4c100a575ea191731a588938414e7e062296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Rivet?= Date: Fri, 7 Jun 2019 12:18:28 -0400 Subject: [PATCH 40/46] Update CHANGELOG.md Co-Authored-By: Chris Parmer --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee90abbb4..46dd8d0b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,7 +41,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - `n_fixed_columns`: Will fix columns to the left. Renamed to `fixed_columns` Now accepts an object { headers: boolean, data: number } instead of a number. - { headers: true } determines the number of columns to fix automatically. + { headers: true } determines the number of columns to fix automatically. For example, if the rows are selectable or deletable, { headers: true } would fix those columns automatically. If { headers: true, data: 2 }, it would fix the first two data columns in addition to the selectable and deletable if visible. - `n_fixed_rows`: Will fix rows to the top. Renamed to `fixed_rows` Now accepts an object { headers: boolean, data: number } instead of a number. From 1ede48f37f62a43d7b1dcfa8d32e5bcc32b93f93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Rivet?= Date: Fri, 7 Jun 2019 12:18:53 -0400 Subject: [PATCH 41/46] Update CHANGELOG.md Co-Authored-By: Chris Parmer --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46dd8d0b2..9879be1b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,7 +69,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). Renamed to `sort_action`. - `sorting_treat_empty_string_as_none` Renamed to `sort_as_null`. - Now accepts an array of string, number or booleans. + Now accepts an array of string, number or booleans that can be ignored during sort. Table-level prop for the `column.sort_as_null` column nested prop. - `style_**_conditional` Renamed `filter` to `filter_query`. From 7445e3de41e5edb8f3e86ccc20be7ed8d0dcd48b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Rivet?= Date: Fri, 7 Jun 2019 12:20:49 -0400 Subject: [PATCH 42/46] Update CHANGELOG.md Co-Authored-By: Chris Parmer --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9879be1b1..b85653606 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,7 +71,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). Renamed to `sort_as_null`. Now accepts an array of string, number or booleans that can be ignored during sort. Table-level prop for the `column.sort_as_null` column nested prop. - - `style_**_conditional` + - `style_data_conditional` Renamed `filter` to `filter_query`. ### Added From 4609c44ec553d0c53e61cc4081e3a666a10e2b6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Fri, 7 Jun 2019 12:21:58 -0400 Subject: [PATCH 43/46] column deletable, renamable changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b85653606..478badf1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,9 +33,11 @@ This project adheres to [Semantic Versioning](http://semver.org/). #### MODIFIED - `column.deletable`: Allows column deletion. Now accepts a boolean or an array of booleans (for multi-line headers). + For example, if there are multiple headers and you want the second header row to be deletable, this would be `[False, True]`. - `column.editable_name`: Allows column renaming. Renamed to `column.renamable` Now accepts a boolean or an array of booleans (for multi-line headers). + For example, if there are multiple headers and you want the second row's header's name to be editable, this would be `[False, True]`. - `column.id` Now accepts `string` only -- `number` column ids can be casted to string. - `n_fixed_columns`: Will fix columns to the left. From b0a2c218c78a9edb97d883ebe89e8138af3f2ce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Rivet?= Date: Fri, 7 Jun 2019 12:42:04 -0400 Subject: [PATCH 44/46] Apply suggestions from code review Co-Authored-By: Chris Parmer --- src/dash-table/dash/DataTable.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index f39587bf3..2b13dc7f7 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -603,7 +603,7 @@ export const propTypes = { /** * `page_size` represents the number of rows that will be - * displayed on a particular page. + * displayed on a particular page when `page_action` is `'custom'` or `'native'` */ page_size: PropTypes.number, @@ -894,7 +894,8 @@ export const propTypes = { /** * An array of string, number and boolean values that are treated as `None` - * when sorting. This value will be used by columns without `sort_as_null`. + * (i.e. ignored and always displayed last) when sorting. + * This value will be used by columns without `sort_as_null`. * * Defaults to `[]`. */ From 5a1d7d41dbc42dc868267377197aafb5238f3caf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andre=CC=81=20Rivet?= Date: Fri, 7 Jun 2019 12:46:09 -0400 Subject: [PATCH 45/46] sort_as_null and `None` usage --- src/dash-table/dash/DataTable.js | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/dash-table/dash/DataTable.js b/src/dash-table/dash/DataTable.js index 2b13dc7f7..351adde46 100644 --- a/src/dash-table/dash/DataTable.js +++ b/src/dash-table/dash/DataTable.js @@ -254,18 +254,9 @@ export const propTypes = { }), /** - * There are two `sort_as_null` props in the table. - * This is the column-level prop and there is - * also the table-level `sort_as_null` prop. - * - * These props determine how the content of the table is - * sorted. - * - * If the column-level `sort_as_null` is set it overrides - * the table-level `sort_as_null` for that column. - * - * An array of string, number and boolean values that are treated as `None` - * when sorting is applied to the column. + * An array of string, number and boolean values that are treated as `null` + * (i.e. ignored and always displayed last) when sorting. + * This value overrides the table-level `sort_as_null`. */ sort_as_null: PropTypes.arrayOf(PropTypes.oneOfType([ PropTypes.string, @@ -893,8 +884,8 @@ export const propTypes = { })), /** - * An array of string, number and boolean values that are treated as `None` - * (i.e. ignored and always displayed last) when sorting. + * An array of string, number and boolean values that are treated as `null` + * (i.e. ignored and always displayed last) when sorting. * This value will be used by columns without `sort_as_null`. * * Defaults to `[]`. From da09942f5d5887d3ec70e0e86e9ea71f24dc84a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Rivet?= Date: Fri, 7 Jun 2019 12:49:26 -0400 Subject: [PATCH 46/46] Update CHANGELOG.md Co-Authored-By: Chris Parmer --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 478badf1b..dfb7517ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). { headers: true, data: 2} would fix all of the header rows as well as the first 2 data rows. - `pagination_mode` Renamed to `page_action`. + `'fe'` is now `'native'`, `'be'` is now `'custom'`, and `false` is now '`none'` - `column_static_dropdown` Renamed to `dropdown`. Now an object with each entry refering to a Column ID. Each nested prop expects.