diff --git a/src/data-table/__tests__/data-table-stateful-callback.e2e.ts b/src/data-table/__tests__/data-table-stateful-callback.e2e.ts
new file mode 100644
index 0000000000..7321e46caf
--- /dev/null
+++ b/src/data-table/__tests__/data-table-stateful-callback.e2e.ts
@@ -0,0 +1,51 @@
+/*
+Copyright (c) Uber Technologies, Inc.
+
+This source code is licensed under the MIT license found in the
+LICENSE file in the root directory of this source tree.
+*/
+
+import { expect, test } from '@playwright/test';
+import { mount } from '../../test/integration';
+
+test.describe('data table initial filters', () => {
+ test('mounts with initial sort applied', async ({ page }) => {
+ await mount(page, 'data-table--stateful-callback');
+
+ const textQueryInput = page.locator('input').first();
+ const filterMenuButton = page.locator('text=Add Filter');
+ const filterMenuColumn = page.locator('li').locator('text=Order');
+ const categoricalFilterOption = page.locator('label').locator('text=Crocodylia');
+ const filterMenuApply = page.locator('text=Apply');
+ const changeListItems = page.locator('ul#change-list').locator('li');
+ const filterClearButton = page.locator('[title="Delete"]');
+ const nameColumnHeader = page.locator('text=Name');
+
+ await expect(changeListItems).toHaveCount(1);
+ await textQueryInput.type('Alligator');
+ await expect(changeListItems).toHaveCount(2);
+ await filterMenuButton.click();
+ await filterMenuColumn.click();
+ await categoricalFilterOption.click();
+ await filterMenuApply.click();
+ await expect(changeListItems).toHaveCount(3);
+ await filterClearButton.click();
+ await expect(changeListItems).toHaveCount(4);
+ await nameColumnHeader.click();
+ await expect(changeListItems).toHaveCount(5);
+ await nameColumnHeader.click();
+ await expect(changeListItems).toHaveCount(6);
+ await nameColumnHeader.click();
+ await expect(changeListItems).toHaveCount(7);
+
+ expect(await changeListItems.allTextContents()).toEqual([
+ '{"textQuery":""}',
+ '{"textQuery":"Alligator"}',
+ '{"title":"Order","data":{"description":"Crocodylia","exclude":false,"selection":{}}}',
+ '{"title":"Order"}',
+ '{"sortIndex":0,"sortDirection":"ASC"}',
+ '{"sortIndex":0,"sortDirection":"DESC"}',
+ '{"sortIndex":-1,"sortDirection":"ASC"}',
+ ]);
+ });
+});
diff --git a/src/data-table/__tests__/data-table-stateful-callback.scenario.tsx b/src/data-table/__tests__/data-table-stateful-callback.scenario.tsx
new file mode 100644
index 0000000000..de9de779bc
--- /dev/null
+++ b/src/data-table/__tests__/data-table-stateful-callback.scenario.tsx
@@ -0,0 +1,120 @@
+/*
+Copyright (c) Uber Technologies, Inc.
+
+This source code is licensed under the MIT license found in the
+LICENSE file in the root directory of this source tree.
+*/
+import * as React from 'react';
+
+import { Tag, KIND as TAG_KIND } from '../../tag';
+
+import CategoricalColumn from '../column-categorical';
+import CustomColumn from '../column-custom';
+import StringColumn from '../column-string';
+import { StatefulDataTable } from '../stateful-data-table';
+
+import AnimalData from './animal-data';
+
+type RowData = {
+ Name: string;
+ Kingdom: string;
+ Phylum: string;
+ Class: string;
+ Order: string;
+ Family: string;
+};
+
+const columns = [
+ StringColumn({
+ title: 'Name',
+ minWidth: 300,
+ mapDataToValue: (data: RowData) => data.Name,
+ }),
+
+ CategoricalColumn({
+ title: 'Kingdom',
+ mapDataToValue: (data: RowData) => data.Kingdom,
+ }),
+
+ CustomColumn({
+ title: 'Phylum',
+ minWidth: 90,
+ mapDataToValue: (data: RowData) => data.Phylum,
+ textQueryFilter: function (textQuery, data) {
+ return data.toLowerCase().includes(textQuery.toLowerCase());
+ },
+ renderCell: function PhylumnCell(props) {
+ return (
+
+ {props.value}
+
+ );
+ },
+ }),
+
+ CategoricalColumn({
+ title: 'Class',
+ minWidth: 120,
+ mapDataToValue: (data: RowData) => data.Class,
+ }),
+
+ CategoricalColumn({
+ title: 'Order',
+ mapDataToValue: (data: RowData) => data.Order,
+ }),
+
+ CategoricalColumn({
+ title: 'Family',
+ mapDataToValue: (data: RowData) => data.Family,
+ }),
+];
+
+const rows = AnimalData.map((row) => {
+ return {
+ id: row.Name,
+ data: row,
+ };
+});
+
+export function Scenario() {
+ const [changes, setChanges] = React.useState([]);
+
+ function addChange(change) {
+ setChanges((p) => [...p, change]);
+ }
+
+ return (
+
+
+ addChange({ title, data })}
+ onFilterRemove={(title) => addChange({ title })}
+ onSort={(sortIndex, sortDirection) => addChange({ sortIndex, sortDirection })}
+ onTextQueryChange={(textQuery) => addChange({ textQuery })}
+ columns={columns}
+ rows={rows}
+ />
+
+
+
change list
+
+ {changes.map((c, i) => (
+ - {JSON.stringify(c)}
+ ))}
+
+
+ );
+}
diff --git a/src/data-table/__tests__/data-table.stories.tsx b/src/data-table/__tests__/data-table.stories.tsx
index 7057776333..99caa1afcf 100644
--- a/src/data-table/__tests__/data-table.stories.tsx
+++ b/src/data-table/__tests__/data-table.stories.tsx
@@ -33,6 +33,7 @@ import { Scenario as DataTableRowActions } from './data-table-row-actions.scenar
import { Scenario as DataTableRowActionsButton } from './data-table-row-actions-button.scenario';
import { Scenario as DataTableRowActionsDynamic } from './data-table-row-actions-dynamic.scenario';
import { Scenario as DataTableRowHeight } from './data-table-row-height.scenario';
+import { Scenario as DataTableStatefulCallback } from './data-table-stateful-callback.scenario';
import { Scenario as DataTableTextSearch } from './data-table-text-search.scenario';
import { Scenario as DataTableDefault } from './data-table.scenario';
import { Scenario as DataTableRtl } from './data-table-rtl.scenario';
@@ -66,6 +67,7 @@ export const RowActions = () => ;
export const RowActionsButton = () => ;
export const RowActionsDynamic = () => ;
export const RowHeight = () => ;
+export const StatefulCallback = () => ;
export const TextSearch = () => ;
export const DataTable = () => ;
export const TestRtl = () => ;
diff --git a/src/data-table/stateful-container.ts b/src/data-table/stateful-container.ts
index 14d4023e5a..13b28822a3 100644
--- a/src/data-table/stateful-container.ts
+++ b/src/data-table/stateful-container.ts
@@ -22,35 +22,49 @@ function useDuplicateColumnTitleWarning(columns: ColumnOptions[]) {
}, [columns]);
}
-function useSortParameters(initialSortIndex = -1, initialSortDirection = null) {
- const [sortIndex, setSortIndex] = React.useState(initialSortIndex);
- const [sortDirection, setSortDirection] = React.useState(initialSortDirection);
+export const StatefulContainer: React.FC = (props) => {
+ useDuplicateColumnTitleWarning(props.columns);
+ const [sortIndex, setSortIndex] = React.useState(
+ typeof props.initialSortIndex === 'number' ? props.initialSortIndex : -1
+ );
+ const [sortDirection, setSortDirection] = React.useState(props.initialSortDirection);
+ const [filters, setFilters] = React.useState(props.initialFilters || new Map());
+ const [textQuery, setTextQuery] = React.useState('');
+ const [selectedRowIds, setSelectedRowIds] = React.useState>(
+ props.initialSelectedRowIds || new Set()
+ );
function handleSort(columnIndex) {
+ let nextSortIndex;
+ let nextSortDirection;
+
if (columnIndex === sortIndex) {
if (sortDirection === SORT_DIRECTIONS.DESC) {
- setSortIndex(-1);
- setSortDirection(SORT_DIRECTIONS.ASC);
+ nextSortIndex = -1;
+ nextSortDirection = SORT_DIRECTIONS.ASC;
} else {
- setSortDirection(SORT_DIRECTIONS.DESC);
+ nextSortIndex = columnIndex;
+ nextSortDirection = SORT_DIRECTIONS.DESC;
}
} else {
- setSortIndex(columnIndex);
- setSortDirection(SORT_DIRECTIONS.ASC);
+ nextSortIndex = columnIndex;
+ nextSortDirection = SORT_DIRECTIONS.ASC;
}
- }
- return [sortIndex, sortDirection, handleSort];
-}
+ setSortIndex(nextSortIndex);
+ setSortDirection(nextSortDirection);
-export const StatefulContainer: React.FC = (props) => {
- useDuplicateColumnTitleWarning(props.columns);
- const [sortIndex, sortDirection, handleSort] = useSortParameters(
- props.initialSortIndex,
- props.initialSortDirection
- );
- const [filters, setFilters] = React.useState(props.initialFilters || new Map());
- const [textQuery, setTextQuery] = React.useState('');
+ if (props.onSort) {
+ props.onSort(nextSortIndex, nextSortDirection);
+ }
+ }
+
+ function handleTextQueryChange(nextTextQuery) {
+ setTextQuery(nextTextQuery);
+ if (props.onTextQueryChange) {
+ props.onTextQueryChange(nextTextQuery);
+ }
+ }
function handleFilterAdd(title, filterParams) {
filters.set(title, filterParams);
@@ -67,9 +81,6 @@ export const StatefulContainer: React.FC = (props) => {
setFilters(new Map(filters));
}
- const [selectedRowIds, setSelectedRowIds] = React.useState>(
- props.initialSelectedRowIds || new Set()
- );
function handleSelectChange(next) {
setSelectedRowIds(next);
@@ -122,7 +133,7 @@ export const StatefulContainer: React.FC = (props) => {
onSelectNone: handleSelectNone,
onSelectOne: handleSelectOne,
onSort: handleSort,
- onTextQueryChange: setTextQuery,
+ onTextQueryChange: handleTextQueryChange,
resizableColumnWidths: Boolean(props.resizableColumnWidths),
rowHighlightIndex: props.rowHighlightIndex,
selectedRowIds,
diff --git a/src/data-table/stateful-data-table.tsx b/src/data-table/stateful-data-table.tsx
index 32924ae709..6b29255e08 100644
--- a/src/data-table/stateful-data-table.tsx
+++ b/src/data-table/stateful-data-table.tsx
@@ -168,6 +168,8 @@ export function StatefulDataTable(props: StatefulDataTableProps) {
onIncludedRowsChange={props.onIncludedRowsChange}
onRowHighlightChange={props.onRowHighlightChange}
onSelectionChange={props.onSelectionChange}
+ onSort={props.onSort}
+ onTextQueryChange={props.onTextQueryChange}
resizableColumnWidths={props.resizableColumnWidths}
rows={props.rows}
rowActions={props.rowActions}
diff --git a/src/data-table/types.js.flow b/src/data-table/types.js.flow
index 80a0656976..985e0bfb97 100644
--- a/src/data-table/types.js.flow
+++ b/src/data-table/types.js.flow
@@ -111,6 +111,7 @@ export type StatefulDataTablePropsT = {|
onIncludedRowsChange?: (rows: RowT[]) => void,
onRowHighlightChange?: (rowIndex: number, row: RowT) => void,
onSelectionChange?: (RowT[]) => mixed,
+ onSort?: (columnIndex: number, sortDirection: SortDirectionsT) => void,
resizableColumnWidths?: boolean,
rows: RowT[],
rowActions?: RowActionT[] | ((RowT) => RowActionT[]),
diff --git a/src/data-table/types.ts b/src/data-table/types.ts
index ec617b1946..96516544a9 100644
--- a/src/data-table/types.ts
+++ b/src/data-table/types.ts
@@ -122,6 +122,8 @@ export type StatefulDataTableProps = {
onIncludedRowsChange?: (rows: Row[]) => void;
onRowHighlightChange?: (rowIndex: number, row: Row) => void;
onSelectionChange?: (a: Row[]) => unknown;
+ onSort?: (columnIndex: number, sortDirection: SortDirections) => void;
+ onTextQueryChange?: (textQuery: string) => void;
resizableColumnWidths?: boolean;
rows: Row[];
rowActions?: RowAction[] | ((a: Row) => RowAction[]);
diff --git a/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-dark-chromium-linux.png b/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-dark-chromium-linux.png
new file mode 100644
index 0000000000..bf9bfef890
Binary files /dev/null and b/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-dark-chromium-linux.png differ
diff --git a/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-dark-firefox-linux.png b/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-dark-firefox-linux.png
new file mode 100644
index 0000000000..caf54508fd
Binary files /dev/null and b/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-dark-firefox-linux.png differ
diff --git a/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-dark-webkit-linux.png b/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-dark-webkit-linux.png
new file mode 100644
index 0000000000..ff16fad3d0
Binary files /dev/null and b/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-dark-webkit-linux.png differ
diff --git a/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-desktop-chromium-linux.png b/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-desktop-chromium-linux.png
new file mode 100644
index 0000000000..0b09fda972
Binary files /dev/null and b/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-desktop-chromium-linux.png differ
diff --git a/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-desktop-firefox-linux.png b/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-desktop-firefox-linux.png
new file mode 100644
index 0000000000..deb5326329
Binary files /dev/null and b/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-desktop-firefox-linux.png differ
diff --git a/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-desktop-webkit-linux.png b/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-desktop-webkit-linux.png
new file mode 100644
index 0000000000..3de24ee01f
Binary files /dev/null and b/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-desktop-webkit-linux.png differ
diff --git a/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-mobile-chromium-linux.png b/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-mobile-chromium-linux.png
new file mode 100644
index 0000000000..47b796d228
Binary files /dev/null and b/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-mobile-chromium-linux.png differ
diff --git a/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-mobile-firefox-linux.png b/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-mobile-firefox-linux.png
new file mode 100644
index 0000000000..c87dd99a9e
Binary files /dev/null and b/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-mobile-firefox-linux.png differ
diff --git a/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-mobile-webkit-linux.png b/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-mobile-webkit-linux.png
new file mode 100644
index 0000000000..3df4f417ca
Binary files /dev/null and b/vrt/tests.vrt.js-snapshots/data-table--stateful-callback-mobile-webkit-linux.png differ