diff --git a/COMPONENT_INDEX.md b/COMPONENT_INDEX.md
index e17b3bff..a2b50c50 100644
--- a/COMPONENT_INDEX.md
+++ b/COMPONENT_INDEX.md
@@ -991,8 +991,8 @@ export interface DataTableCell {
| selectable | let | Yes | boolean | false | Set to `true` for the selectable variant Automatically set to `true` if `radio` or `batchSelection` are `true` |
| expandedRowIds | let | Yes | DataTableRowId[] | [] | Specify the row ids to be expanded |
| expandable | let | Yes | boolean | false | Set to `true` for the expandable variant Automatically set to `true` if `batchExpansion` is `true` |
-| rows | let | Yes | DataTableRow[] | [] | Specify the rows the data table should render keys defined in `headers` are used for the row ids |
| headers | let | No | DataTableHeader[] | [] | Specify the data table headers |
+| rows | let | No | DataTableRow[] | [] | Specify the rows the data table should render keys defined in `headers` are used for the row ids |
| size | let | No | "compact" | "short" | "medium" | "tall" | undefined | Set the size of the data table |
| title | let | No | string | "" | Specify the title of the data table |
| description | let | No | string | "" | Specify the description of the data table |
@@ -4640,14 +4640,15 @@ None.
### Props
-| Prop name | Kind | Reactive | Type | Default value | Description |
-| :--------- | :--------------- | :------- | :---------------------------------------- | ------------------ | --------------------------------------------- |
-| ref | let | Yes | null | HTMLInputElement | null | Obtain a reference to the input HTML element |
-| expanded | let | Yes | boolean | false | Set to `true` to expand the search bar |
-| value | let | Yes | number | string | "" | Specify the value of the search input |
-| persistent | let | No | boolean | false | Set to `true` to keep the search bar expanded |
-| disabled | let | No | boolean | false | Set to `true` to disable the search bar |
-| tabindex | let | No | string | "0" | Specify the tabindex |
+| Prop name | Kind | Reactive | Type | Default value | Description |
+| :--------------- | :--------------- | :------- | :---------------------------------------------------------------------------------------------------------------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| ref | let | Yes | null | HTMLInputElement | null | Obtain a reference to the input HTML element |
+| expanded | let | Yes | boolean | false | Set to `true` to expand the search bar |
+| value | let | Yes | number | string | "" | Specify the value of the search input |
+| persistent | let | No | boolean | false | Set to `true` to keep the search bar expanded |
+| disabled | let | No | boolean | false | Set to `true` to disable the search bar |
+| shouldFilterRows | let | No | boolean | ((rows: import("./DataTable.svelte").DataTableRow, value: number | string) => boolean) | false | Set to `true` to filter table rows using the search value.
If `true`, the default search excludes `id`, `cells` fields and only does a basic comparison on string and number type cell values.
To implement your own client-side filtering, pass a function that accepts a row and value and returns a boolean. |
+| tabindex | let | No | string | "0" | Specify the tabindex |
### Slots
diff --git a/docs/src/COMPONENT_API.json b/docs/src/COMPONENT_API.json
index 4384ac97..9db9e5b6 100644
--- a/docs/src/COMPONENT_API.json
+++ b/docs/src/COMPONENT_API.json
@@ -2282,7 +2282,7 @@
"isFunction": false,
"isFunctionDeclaration": false,
"constant": false,
- "reactive": true
+ "reactive": false
},
{
"name": "size",
@@ -13218,6 +13218,17 @@
"constant": false,
"reactive": false
},
+ {
+ "name": "shouldFilterRows",
+ "kind": "let",
+ "description": "Set to `true` to filter table rows using the search value.\n\nIf `true`, the default search excludes `id`, `cells` fields and\nonly does a basic comparison on string and number type cell values.\n\nTo implement your own client-side filtering, pass a function\nthat accepts a row and value and returns a boolean.",
+ "type": "boolean | ((rows: import(\"./DataTable.svelte\").DataTableRow, value: number | string) => boolean)",
+ "value": "false",
+ "isFunction": false,
+ "isFunctionDeclaration": false,
+ "constant": false,
+ "reactive": false
+ },
{
"name": "tabindex",
"kind": "let",
diff --git a/docs/src/pages/components/DataTable.svx b/docs/src/pages/components/DataTable.svx
index 9da963e0..fe68f893 100644
--- a/docs/src/pages/components/DataTable.svx
+++ b/docs/src/pages/components/DataTable.svx
@@ -464,6 +464,22 @@ title="Load balancers" description="Your organization's active load balancers."
+### Filterable
+
+By default, `ToolbarSearch` will not filter `DataTable` rows.
+
+Set `shouldFilterRows` to `true` to enable client-side filtering. The default filtering performs a basic string comparison on cell values that are of a string or a number type.
+
+Note that in-memory filtering is not optimal for large data sets, where you might consider using server-side search.
+
+
+
+### Filterable (custom)
+
+`shouldFilterRows` also accepts a function and passes it the current row and value. It expects the function to return a boolean.
+
+
+
### Zebra stripes
+ import {
+ DataTable,
+ Toolbar,
+ ToolbarContent,
+ ToolbarSearch,
+ ToolbarMenu,
+ ToolbarMenuItem,
+ Button,
+ } from "carbon-components-svelte";
+
+ let rows = Array.from({ length: 10 }).map((_, i) => ({
+ id: i,
+ name: "Load Balancer " + (i + 1),
+ protocol: "HTTP",
+ port: 3000 + i * 10,
+ rule: i % 2 ? "Round robin" : "DNS delegation",
+ }));
+
+
+
+
+
+
+
+ Restart all
+
+ API documentation
+
+ Stop all
+
+
+
+
+
diff --git a/docs/src/pages/framed/DataTable/DataTableFilterable.svelte b/docs/src/pages/framed/DataTable/DataTableFilterable.svelte
new file mode 100644
index 00000000..3d80b057
--- /dev/null
+++ b/docs/src/pages/framed/DataTable/DataTableFilterable.svelte
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+ Restart all
+
+ API documentation
+
+ Stop all
+
+
+
+
+
diff --git a/src/DataTable/DataTable.svelte b/src/DataTable/DataTable.svelte
index 7d0ae252..6f9ff031 100644
--- a/src/DataTable/DataTable.svelte
+++ b/src/DataTable/DataTable.svelte
@@ -140,6 +140,7 @@
sortDirection: "none",
});
const headerItems = writable([]);
+ const tableRows = writable(rows);
const thKeys = derived(headerItems, () =>
headers
.map(({ key }, i) => ({ key, id: key }))
@@ -155,6 +156,7 @@
sortHeader,
tableSortable,
batchSelectedIds,
+ tableRows,
resetSelectedRowIds: () => {
selectAll = false;
selectedRowIds = [];
@@ -173,7 +175,7 @@
let refSelectAll = null;
$: batchSelectedIds.set(selectedRowIds);
- $: rowIds = rows.map((row) => row.id);
+ $: rowIds = $tableRows.map((row) => row.id);
$: expandableRowIds = rowIds.filter(
(id) => !nonExpandableRowIds.includes(id)
);
@@ -193,23 +195,25 @@
$: if (radio || batchSelection) selectable = true;
$: tableSortable.set(sortable);
$: headerKeys = headers.map(({ key }) => key);
- $: rows = rows.map((row) => ({
- ...row,
- cells: headerKeys.map((key, index) => ({
- key,
- value: resolvePath(row, key),
- display: headers[index].display,
- })),
- }));
- $: sortedRows = rows;
+ $: tableRows.set(
+ rows.map((row) => ({
+ ...row,
+ cells: headerKeys.map((key, index) => ({
+ key,
+ value: resolvePath(row, key),
+ display: headers[index].display,
+ })),
+ }))
+ );
+ $: sortedRows = [...$tableRows];
$: ascending = $sortHeader.sortDirection === "ascending";
$: sortKey = $sortHeader.key;
$: sorting = sortable && sortKey != null;
$: if (sorting) {
if ($sortHeader.sortDirection === "none") {
- sortedRows = rows;
+ sortedRows = $tableRows;
} else {
- sortedRows = [...rows].sort((a, b) => {
+ sortedRows = [...$tableRows].sort((a, b) => {
const itemA = ascending
? resolvePath(a, sortKey, "")
: resolvePath(b, sortKey, "");
@@ -236,7 +240,7 @@
page && pageSize
? rows.slice((page - 1) * pageSize, page * pageSize)
: rows;
- $: displayedRows = getDisplayedRows(rows, page, pageSize);
+ $: displayedRows = getDisplayedRows($tableRows, page, pageSize);
$: displayedSortedRows = getDisplayedRows(sortedRows, page, pageSize);
diff --git a/src/DataTable/ToolbarSearch.svelte b/src/DataTable/ToolbarSearch.svelte
index 21c0c256..50ac8c6d 100644
--- a/src/DataTable/ToolbarSearch.svelte
+++ b/src/DataTable/ToolbarSearch.svelte
@@ -16,6 +16,18 @@
/** Set to `true` to disable the search bar */
export let disabled = false;
+ /**
+ * Set to `true` to filter table rows using the search value.
+ *
+ * If `true`, the default search excludes `id`, `cells` fields and
+ * only does a basic comparison on string and number type cell values.
+ *
+ * To implement your own client-side filtering, pass a function
+ * that accepts a row and value and returns a boolean.
+ * @type {boolean | ((rows: import("./DataTable.svelte").DataTableRow, value: number | string) => boolean)}
+ */
+ export let shouldFilterRows = false;
+
/** Specify the tabindex */
export let tabindex = "0";
@@ -25,9 +37,36 @@
*/
export let ref = null;
- import { tick } from "svelte";
+ import { tick, getContext } from "svelte";
import Search from "../Search/Search.svelte";
+ const { tableRows } = getContext("DataTable");
+
+ $: originalRows = tableRows ? [...$tableRows] : [];
+ $: if (shouldFilterRows) {
+ let rows = originalRows;
+
+ if (value.trim().length > 0) {
+ if (shouldFilterRows === true) {
+ rows = rows.filter((row) => {
+ return Object.entries(row)
+ .filter(([key]) => !["cells", "id"].includes(key))
+ .some(([key, _value]) => {
+ if (typeof _value === "string" || typeof _value === "number") {
+ return (_value + "")
+ ?.toLowerCase()
+ .includes(value.trim().toLowerCase());
+ }
+ });
+ });
+ } else if (typeof shouldFilterRows === "function") {
+ rows = rows.filter((row) => shouldFilterRows(row, value) ?? false);
+ }
+ }
+
+ tableRows.set(rows);
+ }
+
async function expandSearch() {
if (disabled || persistent || expanded) return;
expanded = true;
diff --git a/tests/DataTable.test.svelte b/tests/DataTable.test.svelte
index 1545f3d7..67af1f16 100644
--- a/tests/DataTable.test.svelte
+++ b/tests/DataTable.test.svelte
@@ -109,7 +109,11 @@
>
-
+ Restart all
diff --git a/types/DataTable/ToolbarSearch.svelte.d.ts b/types/DataTable/ToolbarSearch.svelte.d.ts
index b3ced787..83653277 100644
--- a/types/DataTable/ToolbarSearch.svelte.d.ts
+++ b/types/DataTable/ToolbarSearch.svelte.d.ts
@@ -27,6 +27,23 @@ export interface ToolbarSearchProps
*/
disabled?: boolean;
+ /**
+ * Set to `true` to filter table rows using the search value.
+ *
+ * If `true`, the default search excludes `id`, `cells` fields and
+ * only does a basic comparison on string and number type cell values.
+ *
+ * To implement your own client-side filtering, pass a function
+ * that accepts a row and value and returns a boolean.
+ * @default false
+ */
+ shouldFilterRows?:
+ | boolean
+ | ((
+ rows: import("./DataTable.svelte").DataTableRow,
+ value: number | string
+ ) => boolean);
+
/**
* Specify the tabindex
* @default "0"