Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions src/data-table/__tests__/data-table-stateful-callback.e2e.ts
Original file line number Diff line number Diff line change
@@ -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"}',
]);
});
});
120 changes: 120 additions & 0 deletions src/data-table/__tests__/data-table-stateful-callback.scenario.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Tag
closeable={false}
overrides={{
Root: {
style: {
marginTop: 0,
marginRight: 0,
marginBottom: 0,
marginLeft: 0,
},
},
}}
kind={TAG_KIND.accent}
>
{props.value}
</Tag>
);
},
}),

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 (
<div>
<div style={{ height: '600px', width: '700px' }}>
<StatefulDataTable
onFilterAdd={(title, data) => addChange({ title, data })}
onFilterRemove={(title) => addChange({ title })}
onSort={(sortIndex, sortDirection) => addChange({ sortIndex, sortDirection })}
onTextQueryChange={(textQuery) => addChange({ textQuery })}
columns={columns}
rows={rows}
/>
</div>

<p>change list</p>
<ul id="change-list">
{changes.map((c, i) => (
<li key={i}>{JSON.stringify(c)}</li>
))}
</ul>
</div>
);
}
2 changes: 2 additions & 0 deletions src/data-table/__tests__/data-table.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -66,6 +67,7 @@ export const RowActions = () => <DataTableRowActions />;
export const RowActionsButton = () => <DataTableRowActionsButton />;
export const RowActionsDynamic = () => <DataTableRowActionsDynamic />;
export const RowHeight = () => <DataTableRowHeight />;
export const StatefulCallback = () => <DataTableStatefulCallback />;
export const TextSearch = () => <DataTableTextSearch />;
export const DataTable = () => <DataTableDefault />;
export const TestRtl = () => <DataTableRtl />;
Expand Down
57 changes: 34 additions & 23 deletions src/data-table/stateful-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<StatefulContainerProps> = (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<Set<string | number>>(
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<StatefulContainerProps> = (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);
Expand All @@ -67,9 +81,6 @@ export const StatefulContainer: React.FC<StatefulContainerProps> = (props) => {
setFilters(new Map(filters));
}

const [selectedRowIds, setSelectedRowIds] = React.useState<Set<string | number>>(
props.initialSelectedRowIds || new Set()
);
function handleSelectChange(next) {
setSelectedRowIds(next);

Expand Down Expand Up @@ -122,7 +133,7 @@ export const StatefulContainer: React.FC<StatefulContainerProps> = (props) => {
onSelectNone: handleSelectNone,
onSelectOne: handleSelectOne,
onSort: handleSort,
onTextQueryChange: setTextQuery,
onTextQueryChange: handleTextQueryChange,
resizableColumnWidths: Boolean(props.resizableColumnWidths),
rowHighlightIndex: props.rowHighlightIndex,
selectedRowIds,
Expand Down
2 changes: 2 additions & 0 deletions src/data-table/stateful-data-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
1 change: 1 addition & 0 deletions src/data-table/types.js.flow
Original file line number Diff line number Diff line change
Expand Up @@ -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[]),
Expand Down
2 changes: 2 additions & 0 deletions src/data-table/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[]);
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.