From 72c24b83b202e10a0f21dddd7fc173d5dd1f5403 Mon Sep 17 00:00:00 2001 From: metonym Date: Sun, 5 Jun 2022 13:37:50 -0700 Subject: [PATCH] feat(data-table): support programmatic sorting (#1337) * refactor(data-table): pass down sortable props instead of using context * feat(data-table): support programmatic sorting * test(data-table): assert new props * docs(data-table): add "Programmatic sorting" example * refactor(data-table): remove unused tableSortable store * refactor(data-table): remove unused indices --- COMPONENT_INDEX.md | 16 +-- docs/src/COMPONENT_API.json | 52 +++++++++- docs/src/pages/components/DataTable.svx | 12 +++ .../DataTableProgrammaticSorting.svelte | 97 +++++++++++++++++++ src/DataTable/DataTable.svelte | 52 +++++----- src/DataTable/TableHeader.svelte | 23 +++-- tests/DataTable.test.svelte | 9 +- types/DataTable/DataTable.svelte.d.ts | 12 +++ types/DataTable/TableHeader.svelte.d.ts | 16 ++- 9 files changed, 241 insertions(+), 48 deletions(-) create mode 100644 docs/src/pages/framed/DataTable/DataTableProgrammaticSorting.svelte diff --git a/COMPONENT_INDEX.md b/COMPONENT_INDEX.md index 3600d371..2ee61315 100644 --- a/COMPONENT_INDEX.md +++ b/COMPONENT_INDEX.md @@ -967,6 +967,8 @@ export interface DataTableCell { | selectable | No | let | Yes | boolean | false | Set to `true` for the selectable variant
Automatically set to `true` if `radio` or `batchSelection` are `true` | | expandedRowIds | No | let | Yes | ReadonlyArray | [] | Specify the row ids to be expanded | | expandable | No | let | Yes | boolean | false | Set to `true` for the expandable variant
Automatically set to `true` if `batchExpansion` is `true` | +| sortDirection | No | let | Yes | "none" | "ascending" | "descending" | "none" | Specify the sort direction | +| sortKey | No | let | Yes | DataTableKey | null | Specify the header key to sort by | | headers | No | let | No | ReadonlyArray | [] | Specify the data table headers | | rows | No | let | No | ReadonlyArray | [] | Specify the rows the data table should render
keys defined in `headers` are used for the row ids | | size | No | let | No | "compact" | "short" | "medium" | "tall" | undefined | Set the size of the data table | @@ -3864,12 +3866,14 @@ None. ### Props -| Prop name | Required | Kind | Reactive | Type | Default value | Description | -| :-------------- | :------- | :--------------- | :------- | ------------------------- | ------------------------------------------------ | ------------------------------------------------------ | -| disableSorting | No | let | No | boolean | false | Set to `true` to disable sorting on this specific cell | -| scope | No | let | No | string | "col" | Specify the `scope` attribute | -| translateWithId | No | let | No | () => string | () => "" | Override the default id translations | -| id | No | let | No | string | "ccs-" + Math.random().toString(36) | Set an id for the top-level element | +| Prop name | Required | Kind | Reactive | Type | Default value | Description | +| :-------------- | :------- | :--------------- | :------- | ---------------------------------------------------------- | ------------------------------------------------ | -------------------------------------- | +| sortable | No | let | No | boolean | false | Set to `true` for the sortable variant | +| sortDirection | No | let | No | "none" | "ascending" | "descending" | "none" | Specify the sort direction | +| active | No | let | No | boolean | false | Set to `true` if the column sorting | +| scope | No | let | No | string | "col" | Specify the `scope` attribute | +| translateWithId | No | let | No | () => string | () => "" | Override the default id translations | +| id | No | let | No | string | "ccs-" + Math.random().toString(36) | Set an id for the top-level element | ### Slots diff --git a/docs/src/COMPONENT_API.json b/docs/src/COMPONENT_API.json index 562853ce..56d29bac 100644 --- a/docs/src/COMPONENT_API.json +++ b/docs/src/COMPONENT_API.json @@ -2421,6 +2421,30 @@ "constant": false, "reactive": false }, + { + "name": "sortKey", + "kind": "let", + "description": "Specify the header key to sort by", + "type": "DataTableKey", + "value": "null", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": true + }, + { + "name": "sortDirection", + "kind": "let", + "description": "Specify the sort direction", + "type": "\"none\" | \"ascending\" | \"descending\"", + "value": "\"none\"", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": true + }, { "name": "expandable", "kind": "let", @@ -11843,9 +11867,33 @@ "filePath": "src/DataTable/TableHeader.svelte", "props": [ { - "name": "disableSorting", + "name": "sortable", "kind": "let", - "description": "Set to `true` to disable sorting on this specific cell", + "description": "Set to `true` for the sortable variant", + "type": "boolean", + "value": "false", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + }, + { + "name": "sortDirection", + "kind": "let", + "description": "Specify the sort direction", + "type": "\"none\" | \"ascending\" | \"descending\"", + "value": "\"none\"", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + }, + { + "name": "active", + "kind": "let", + "description": "Set to `true` if the column sorting", "type": "boolean", "value": "false", "isFunction": false, diff --git a/docs/src/pages/components/DataTable.svx b/docs/src/pages/components/DataTable.svx index 52714f80..2c130b56 100644 --- a/docs/src/pages/components/DataTable.svx +++ b/docs/src/pages/components/DataTable.svx @@ -1075,6 +1075,18 @@ and then limit displayed content by using `pageSize` and `page` props, which are ]}" /> +### Programmatic sorting + +Use the reactive `sortKey` and `sortDirection` props for programmatic sorting. + +By default, the table is not sorted by a specific key. The `sortKey` value must be a valid `key` specified in the `headers` object. + +Possible values for `sortDirection` include `"none"` or `"ascending"` or `"descending"`. + +Setting `sortKey` to `null` and `sortDirection` to `"none"` should reset the table rows to their initial order. + + + ### Empty column with overflow menu Some use cases require an empty column in the table body without a corresponding table header. diff --git a/docs/src/pages/framed/DataTable/DataTableProgrammaticSorting.svelte b/docs/src/pages/framed/DataTable/DataTableProgrammaticSorting.svelte new file mode 100644 index 00000000..c86e548f --- /dev/null +++ b/docs/src/pages/framed/DataTable/DataTableProgrammaticSorting.svelte @@ -0,0 +1,97 @@ + + + + + + + + + diff --git a/src/DataTable/DataTable.svelte b/src/DataTable/DataTable.svelte index bd6b79c7..f1472a06 100644 --- a/src/DataTable/DataTable.svelte +++ b/src/DataTable/DataTable.svelte @@ -53,6 +53,18 @@ /** Set to `true` for the sortable variant */ export let sortable = false; + /** + * Specify the header key to sort by + * @type {DataTableKey} + */ + export let sortKey = null; + + /** + * Specify the sort direction + * @type {"none" | "ascending" | "descending"} + */ + export let sortDirection = "none"; + /** * Set to `true` for the expandable variant * Automatically set to `true` if `batchExpansion` is `true` @@ -132,18 +144,11 @@ }; const dispatch = createEventDispatcher(); const batchSelectedIds = writable(false); - const tableSortable = writable(sortable); - const sortHeader = writable({ - id: null, - key: null, - sort: undefined, - sortDirection: "none", - }); const headerItems = writable([]); const tableRows = writable(rows); const thKeys = derived(headerItems, () => headers - .map(({ key }, i) => ({ key, id: key })) + .map(({ key }) => ({ key, id: key })) .reduce((a, c) => ({ ...a, [c.key]: c.id }), {}) ); const resolvePath = (object, path) => { @@ -155,8 +160,6 @@ }; setContext("DataTable", { - sortHeader, - tableSortable, batchSelectedIds, tableRows, resetSelectedRowIds: () => { @@ -195,7 +198,6 @@ expanded = expandedRowIds.length === expandableRowIds.length; } $: if (radio || batchSelection) selectable = true; - $: tableSortable.set(sortable); $: headerKeys = headers.map(({ key }) => key); $: tableCellsByRowId = rows.reduce( (rows, row) => ({ @@ -210,11 +212,11 @@ ); $: $tableRows = rows; $: sortedRows = [...$tableRows]; - $: ascending = $sortHeader.sortDirection === "ascending"; - $: sortKey = $sortHeader.key; + $: ascending = sortDirection === "ascending"; $: sorting = sortable && sortKey != null; + $: sortingHeader = headers.find((header) => header.key === sortKey); $: if (sorting) { - if ($sortHeader.sortDirection === "none") { + if (sortDirection === "none") { sortedRows = $tableRows; } else { sortedRows = [...$tableRows].sort((a, b) => { @@ -225,7 +227,7 @@ ? resolvePath(b, sortKey) : resolvePath(a, sortKey); - if ($sortHeader.sort) return $sortHeader.sort(itemA, itemB); + if (sortingHeader?.sort) return sortingHeader.sort(itemA, itemB); if (typeof itemA === "number" && typeof itemB === "number") return itemA - itemB; @@ -337,32 +339,26 @@ /> {/if} - {#each headers as header, i (header.key)} + {#each headers as header (header.key)} {#if header.empty} {:else} diff --git a/src/DataTable/TableHeader.svelte b/src/DataTable/TableHeader.svelte index 836a3171..a9c4d9c1 100644 --- a/src/DataTable/TableHeader.svelte +++ b/src/DataTable/TableHeader.svelte @@ -1,6 +1,15 @@ -{#if $tableSortable && !disableSorting} +{#if sortable}
diff --git a/tests/DataTable.test.svelte b/tests/DataTable.test.svelte index 301efcf3..0ac2a22b 100644 --- a/tests/DataTable.test.svelte +++ b/tests/DataTable.test.svelte @@ -70,7 +70,14 @@ } - + diff --git a/types/DataTable/DataTable.svelte.d.ts b/types/DataTable/DataTable.svelte.d.ts index 9a0a588b..e3be8ebf 100644 --- a/types/DataTable/DataTable.svelte.d.ts +++ b/types/DataTable/DataTable.svelte.d.ts @@ -85,6 +85,18 @@ export interface DataTableProps */ sortable?: boolean; + /** + * Specify the header key to sort by + * @default null + */ + sortKey?: DataTableKey; + + /** + * Specify the sort direction + * @default "none" + */ + sortDirection?: "none" | "ascending" | "descending"; + /** * Set to `true` for the expandable variant * Automatically set to `true` if `batchExpansion` is `true` diff --git a/types/DataTable/TableHeader.svelte.d.ts b/types/DataTable/TableHeader.svelte.d.ts index 170a1f03..6252c1b6 100644 --- a/types/DataTable/TableHeader.svelte.d.ts +++ b/types/DataTable/TableHeader.svelte.d.ts @@ -4,10 +4,22 @@ import type { SvelteComponentTyped } from "svelte"; export interface TableHeaderProps extends svelte.JSX.HTMLAttributes { /** - * Set to `true` to disable sorting on this specific cell + * Set to `true` for the sortable variant * @default false */ - disableSorting?: boolean; + sortable?: boolean; + + /** + * Specify the sort direction + * @default "none" + */ + sortDirection?: "none" | "ascending" | "descending"; + + /** + * Set to `true` if the column sorting + * @default false + */ + active?: boolean; /** * Specify the `scope` attribute