diff --git a/src/components/SearchInput/SearchInput.mdx b/src/components/SearchInput/SearchInput.mdx
index cd743181..6c347cd5 100644
--- a/src/components/SearchInput/SearchInput.mdx
+++ b/src/components/SearchInput/SearchInput.mdx
@@ -12,16 +12,18 @@ The `SearchInput` component creates a search bar.
-
+
```
-
\ No newline at end of file
+
diff --git a/src/components/SearchInput/SearchInput.stories.svelte b/src/components/SearchInput/SearchInput.stories.svelte
index bca718e7..ae596d55 100644
--- a/src/components/SearchInput/SearchInput.stories.svelte
+++ b/src/components/SearchInput/SearchInput.stories.svelte
@@ -8,4 +8,17 @@
});
-
\ No newline at end of file
+
+
+
+
+
diff --git a/src/components/SearchInput/SearchInput.svelte b/src/components/SearchInput/SearchInput.svelte
index 929a0e85..01e78d05 100644
--- a/src/components/SearchInput/SearchInput.svelte
+++ b/src/components/SearchInput/SearchInput.svelte
@@ -6,7 +6,7 @@
interface Props {
/** The placeholder text that appears in the search box.*/
searchPlaceholder?: string;
-
+ /** Optional function that runs when the input value changes. */
onsearch?: (newValue: string) => void;
}
diff --git a/src/components/Table/Table.mdx b/src/components/Table/Table.mdx
index 66c4ad8c..1af6c21b 100644
--- a/src/components/Table/Table.mdx
+++ b/src/components/Table/Table.mdx
@@ -6,14 +6,172 @@ import * as TableStories from './Table.stories.svelte';
# Table
-The `Table` component TKTK
+The `Table` component presents data as a table that you can make searchable, filtereable, sortable, or paginated.
```svelte
-
+
```
+
+## Text elements
+
+Set the `title`, `dek`, `notes` and `source` options to add supporting metadata above and below the table.
+
+```svelte
+
+```
+
+
+
+## Truncated
+
+When your table has 10 or more rows, consider clipping it by setting the `truncated` option. When it is enabled, the table is clipped and readers must click a button below the table to see all rows.
+
+By default, this configuration will limit the table to 5 records. Change the cutoff point by adjusting the `truncateLength` option.
+
+This is a good option for simple tables with between 10 and 30 rows. It works best when the table doesn't require interactivity.
+
+```svelte
+
+```
+
+
+
+## Paginated
+
+When your table has many rows, you should consider breaking it up into pages by setting `paginated` to `true`. When it is enabled, readers can leaf through the data using a set of buttons below the table.
+
+By default, there are 25 records per page. Change the number by adjusting the `pageSize` option.
+
+This is a good option when publishing large tables for readers to explore. It works well with interactive features like searching and filters.
+
+```svelte
+
+```
+
+
+
+## Search bar
+
+Allow users to search the table by setting the optional `searchable` option to `true`. Modify the default text that appears in the box by setting `searchPlaceholder` to a different placeholder text.
+
+```svelte
+
+```
+
+
+
+## Filter
+
+Allow users to filter the table by providing one of the attributes as the `filterField`. This works best with categorical columns.
+
+Set `filterLabel` to make the category name more readable. For example, if the column is `Region`, set `filterLabel` to `regions` or `regions of the world`.
+
+```svelte
+
+```
+
+
+
+## Search and filter
+
+Feel free to both search and filter.
+
+```svelte
+
+```
+
+
+```
+
+## Sort
+
+Allow users to sort the table by setting `sortable` to `true`. Specify the starting order by setting `sortField` to a column name and `sortDirection` to `ascending` or `descending`.
+
+By default, all fields are sortable. If you'd like to limit the columns where sorting is allowed, provide a list to the `sortableFields` option.
+
+```svelte
+
+```
+
+
+
+## Format
+
+Format column values by supplying functions keyed to field names with the `fieldFormatters` option. Columns are still sorted using the raw, underlying values.
+
+Among other things, this feature can be used to provide a unit of measurement, such as `$` or `%`, with numeric fields.
+
+```svelte
+
+
+
+```
+
+
diff --git a/src/components/Table/Table.stories.svelte b/src/components/Table/Table.stories.svelte
index 6f766425..7b230b9a 100644
--- a/src/components/Table/Table.stories.svelte
+++ b/src/components/Table/Table.stories.svelte
@@ -18,8 +18,9 @@
import pressFreedom from './demo/pressFreedom.json';
import homeRuns from './demo/homeRuns.json';
import richestWomen from './demo/richestWomen.json';
+ import type { Formatter } from './utils';
- const currencyFormat = (v: number) => '$' + v.toFixed(1);
+ const currencyFormat: Formatter = (v: number) => '$' + v.toFixed(1);
,
+ },
sortable: true,
sortField: 'Net worth (in billions)',
sortDirection: 'descending',
- fieldFormatters: { 'Net worth (in billions)': currencyFormat },
- }}
-/>
-
-> {
/** Data for the table as an array. */
@@ -47,7 +57,7 @@
/** The direction of the sort. By default it's ascending. */
sortDirection?: 'ascending' | 'descending';
/** Custom field formatting functions. Should be keyed to the name of the field. */
- fieldFormatters?: object;
+ fieldFormatters?: FieldFormatters;
/** Width of the component within the text well. */
width?: 'normal' | 'wide' | 'wider' | 'widest' | 'fluid';
/** Add an ID to target with SCSS. */
@@ -75,33 +85,12 @@
sortField = Object.keys(data[0])[0],
sortableFields = Object.keys(data[0]).filter((f) => f !== 'searchStr'),
sortDirection = $bindable('ascending'),
- fieldFormatters = {},
+ fieldFormatters,
width = 'normal',
id = '',
class: cls = '',
}: Props> = $props();
- /** Derived variables */
- // let includedFieldsDerived = $derived.by(() => {
- // if (includedFields) return includedFields;
- // if (data.length > 0)
- // return Object.keys(data[0]).filter((f) => f !== 'searchStr');
- // return [];
- // });
-
- // let sortableFieldsDerived = $derived.by(() => {
- // if (sortableFields) return sortableFields;
- // if (data.length > 0)
- // return Object.keys(data[0]).filter((f) => f !== 'searchStr');
- // return [];
- // });
-
- // let sortFieldDerived = $derived.by(() => {
- // if (sortField) return sortField;
- // if (data.length > 0) return Object.keys(data[0])[0];
- // return '';
- // });
-
/** Set truncate, filtering and pagination configuration */
let showAll = $state(false);
let pageNumber = $state(1);
@@ -109,61 +98,31 @@
let filterList = $derived(
filterField ? getOptions(data, filterField) : undefined
);
- let filterValue = '';
+ let filterValue = $state('');
+ /** Helper functions that modify variables within this component */
//* * Handle show all, search, filter, sort and pagination events */
- function toggleTruncate(_event) {
+ function toggleTruncate() {
showAll = !showAll;
}
- // function handleSearchInput(event: CustomEvent) {
- // searchText = event.detail;
-
- // console.log('searchText', searchText);
- // pageNumber = 1;
- // }
-
/** Filters table data based on the input value in the search bar */
function handleSearchInput(newSearchText: string) {
searchText = newSearchText;
pageNumber = 1;
}
- function handleFilterInput(event) {
- const value = event.detail.value;
- filterValue = value === 'All' ? '' : value;
+ function handleFilterInput(newSearchText: string) {
+ filterValue = newSearchText === 'All' ? '' : newSearchText;
pageNumber = 1;
}
- function handleSort(event) {
+ function handleSort(event: MouseEvent) {
if (!sortable) return;
- sortField = event.target.getAttribute('data-field');
+ sortField = (event.target as HTMLElement).getAttribute('data-field') || '';
sortDirection = sortDirection === 'ascending' ? 'descending' : 'ascending';
}
- function sortArray(array, column, direction) {
- if (!sortable) return array;
- return array.sort((a, b) => {
- if (a[column] < b[column]) {
- return direction === 'ascending' ? -1 : 1;
- } else if (a[column] > b[column]) {
- return direction === 'ascending' ? 1 : -1;
- } else {
- return 0;
- }
- });
- }
-
- function formatValue(item, field) {
- const value = item[field];
- if (field in fieldFormatters) {
- const func = fieldFormatters[field];
- return func(value);
- } else {
- return value;
- }
- }
-
/** Add the `searchStr` field to data */
let searchableData = $derived.by(() => {
return data.map((d) => {
@@ -182,7 +141,7 @@
);
let sortedData = $derived.by(() =>
- sortArray(filteredData, sortField, sortDirection)
+ sortArray(filteredData, sortField, sortDirection, sortable)
);
let currentPageData = $derived.by(() => {
@@ -212,8 +171,8 @@
{/if}
@@ -270,7 +229,7 @@
data-field={field}
data-value={item[field]}
>
- {@html formatValue(item, field)}
+ {@html formatValue(item, field, fieldFormatters)}
{/each}
diff --git a/src/components/Table/components/Pagination.svelte b/src/components/Table/components/Pagination.svelte
index e5f53075..abe6cedc 100644
--- a/src/components/Table/components/Pagination.svelte
+++ b/src/components/Table/components/Pagination.svelte
@@ -6,22 +6,18 @@
interface Props {
/**
* The current page number.
- * @type {number}
*/
pageNumber?: number;
/**
* The default page size.
- * @type {number}
*/
pageSize?: number;
/**
* The number of records in the current page.
- * @type {number}
*/
pageLength?: number;
/**
* The total number of records in the data set.
- * @type {number}
*/
n?: number;
}
@@ -29,8 +25,8 @@
let {
pageNumber = $bindable(1),
pageSize = $bindable(25),
- pageLength = $bindable(null),
- n = $bindable(null),
+ pageLength = $bindable(1),
+ n = $bindable(1),
}: Props = $props();
let minRow = $derived(pageNumber * pageSize - pageSize + 1);
diff --git a/src/components/Table/components/Select.svelte b/src/components/Table/components/Select.svelte
index c0c7d5e1..7b516572 100644
--- a/src/components/Table/components/Select.svelte
+++ b/src/components/Table/components/Select.svelte
@@ -1,42 +1,37 @@
- {#if label}
-
- {/if}
diff --git a/src/components/Table/components/SortArrow.svelte b/src/components/Table/components/SortArrow.svelte
index 4fb751bf..7eb22c7d 100644
--- a/src/components/Table/components/SortArrow.svelte
+++ b/src/components/Table/components/SortArrow.svelte
@@ -26,11 +26,11 @@
class="avoid-clicks"
>
diff --git a/src/components/Table/utils.ts b/src/components/Table/utils.ts
index be9357e5..51b36a7a 100644
--- a/src/components/Table/utils.ts
+++ b/src/components/Table/utils.ts
@@ -63,3 +63,60 @@ export function getOptions(data: T[], attr: keyof T) {
// Convert the list into Option typed objects ready for our Select component
return attrList.map((a) => ({ text: a, value: a }));
}
+interface SortableItem {
+ [key: string]: unknown; // Or more specific types if known
+}
+
+/**
+ * Sorts an array of objects based on a specified column and direction.
+ */
+export function sortArray(
+ /** The array to sort. */
+ array: T[],
+ /** The column to sort by. */
+ column: keyof T,
+ /** The sorting direction ('ascending' or 'descending'). */
+ direction: 'ascending' | 'descending',
+ /** Whether or not sorting is turned on */
+ sortable: boolean
+) {
+ if (!sortable) return array;
+
+ const sorted = [...array].sort((a, b) => {
+ if (a[column] < b[column]) {
+ return direction === 'ascending' ? -1 : 1;
+ } else if (a[column] > b[column]) {
+ return direction === 'ascending' ? 1 : -1;
+ } else {
+ return 0;
+ }
+ });
+
+ return sorted;
+}
+
+export type Formatter = (value: T) => string;
+
+export type FieldFormatters = {
+ [K in keyof T]?: Formatter;
+};
+/**
+ * Formats a value based on a field and a dictionary of formatters.
+ */
+export function formatValue>(
+ /** The object containing the field. */
+ item: FilterableDatum,
+ /** The field to format. */
+ field: keyof T,
+ /** An optional dictionary of formatters. */
+ fieldFormatters?: FieldFormatters
+) {
+ const value = item[field];
+
+ if (fieldFormatters && field in fieldFormatters && fieldFormatters[field]) {
+ const formatter = fieldFormatters[field];
+ return formatter(value);
+ } else {
+ return value;
+ }
+}