Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#149 - DataTable sorting - hook proposal #174

Merged
merged 6 commits into from
Jan 30, 2024
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
2 changes: 2 additions & 0 deletions libs/data-display/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { SortInfo as InternalSortInfo } from "./DataTable";

export type SortInfo = InternalSortInfo;

export { default as useSortableDataTable } from "./useSortableDataTable";

export { default as Amount } from "./Amount";
export { default as DataTable, useDataTable, useLocalSummary } from "./DataTable";
export { default as DescriptionList } from "./DescriptionList";
Expand Down
34 changes: 34 additions & 0 deletions libs/data-display/src/useSortableDataTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useMemo } from "react";

import { useDataTable } from "./index";

export default function useSortableDataTable<T, U extends keyof T>(initialData: T[], columnMapping: Record<U, string>) {
const [dataTableState, dataTableHook] = useDataTable();

const generateSortedData = useMemo(() => {
const sortInstructions = dataTableState.sortBy;

if (!sortInstructions || sortInstructions.length === 0) {
return initialData;
}

return [...initialData].sort((a, b) => {
for (const sortInfo of sortInstructions) {
const columnKey = columnMapping[sortInfo.column];
const compareResult = sortInfo.sortDirection === "ASCENDING" ? -1 : 1;
const aValue = a[columnKey];
const bValue = b[columnKey];

if (aValue !== bValue) {
return (aValue < bValue ? -1 : 1) * compareResult;
}
}

return 0;
});
}, [dataTableState.sortBy]);

return useMemo(() => {
return { sortedData: generateSortedData, dataTableState, dataTableHook };
}, [generateSortedData]);
}
49 changes: 49 additions & 0 deletions storybook/src/data-display/DataTable.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,55 @@ Here is an example utilizing the `useMemo` hook. This specific logic is applied

Now you have a DataTable component with sorting enabled for the specified column, and you can implement your own sorting logic based on your application requirements.

### useSortableDataTable hook
The `@tiller-ds/data-display` package provides a datatable sorting hook that allows you to implement custom sorting logic for your table columns. Here's an example of using the sorting hook in a DataTable:
```tsx
import { DataTable, useSortableDataTable } from '@tiller-ds/data-display';

// Define column mapping for custom sorting
const columnMapping = {
name: 'name',
surname: "surname",
};

const { dataTableHook, sortedData } = useSortableDataTable(allData || [], columnMapping);

<DataTable
data={sortedData}
hook={dataTableHook}
defaultSortBy={[
{
column: 'name',
sortDirection: 'ASCENDING',
},
{
column: "surname",
sortDirection: "ASCENDING",
},
]}
>
<DataTable.Column header="ID" accessor="id" canSort={false} />
<DataTable.Column header="Name" accessor="name" canSort={true} />
<DataTable.Column header="Surname" accessor="surname" canSort={true} />
</DataTable>;
```

The sorting hook, `useSortableDataTable`, enhances the DataTable component by providing a mechanism for sorting logic.
It takes the table data and a column mapping as input and returns the necessary hook and sorted data.
The column mapping is an object where keys represent column names and values represent their corresponding data keys.

The hook returns an object with two properties:
- `dataTableHook`: the hook that should be passed to the `hook` prop of the DataTable component
- `sortedData`: the data array that has been sorted

Ensure that the `canSort` prop is set to `true` for columns that should be sortable. <br/>
Default sorting behavior can be configured using the `defaultSortBy` prop on the DataTable component. <br/>
You can use this hook to implement sorting for specific columns according to your requirements.
ivam5 marked this conversation as resolved.
Show resolved Hide resolved

**Note:**
When using this hook, be aware that it is designed for **client-side sorting**. While it provides flexibility, it may **not be optimal** for handling **larger datasets**. <br/>
Client-side sorting can lead to performance issues with larger amounts of data. It is recommended to consider server-side sorting for improved performance in such cases.

## Best Practices

Data Tables should:
Expand Down
36 changes: 34 additions & 2 deletions storybook/src/data-display/DataTable.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { range, slice } from "lodash";
import { withDesign } from "storybook-addon-designs";

import { Button, Card, IconButton, Link, Pagination, Typography, useLocalPagination } from "@tiller-ds/core";
import { DataTable, useDataTable, useLocalSummary } from "@tiller-ds/data-display";
import { DataTable, useDataTable, useLocalSummary, useSortableDataTable } from "@tiller-ds/data-display";
import { Icon } from "@tiller-ds/icons";
import { DropdownMenu } from "@tiller-ds/menu";
import { defaultThemeConfig } from "@tiller-ds/theme";
Expand Down Expand Up @@ -95,7 +95,7 @@ type Item = {
};

const names = ["Emily", "Michael", "Sarah", "Matthew"];
const surname = ["Moore", "Williams", "Brown", "Davis"];
const surname = ["Moore", "Williams", "Brown", "Davis", "Aron"];
const jobs = ["Nurse", "Teacher", "Software developer", "Lawyer"];
const jobDescription = [
"You will be tasked with caring for pediatric patients with a variety of health conditions and challenges as well as collaborating with physicians to provide the highest-quality care possible to each individual. As a registered nurse on staff, you will communicate orders to medical assistants and other team members and coordinate with staff and families to ensure the adherence to the attending physician’s instructions as well as proper care and disease control practices.",
Expand Down Expand Up @@ -1235,6 +1235,37 @@ export const WithDefaultAscendingSortByName = (args) => {
);
};

export const WithDefaultAscendingSortUsingHook = () => {
// incl-code
const columnMapping = {
name: "name",
surname: "surname",
};

const { dataTableHook, sortedData } = useSortableDataTable(allData || [], columnMapping);

return (
<DataTable
data={sortedData}
hook={dataTableHook}
defaultSortBy={[
{
column: "name",
sortDirection: "ASCENDING",
},
{
column: "surname",
sortDirection: "ASCENDING",
},
]}
>
<DataTable.Column header="ID" accessor="id" canSort={false} />
<DataTable.Column header="Name" accessor="name" canSort={true} />
<DataTable.Column header="Surname" accessor="surname" canSort={true} />
</DataTable>
)
};

export const WithIconButtons = (args) => (
<DataTable data={smallData}>
<DataTable.Column header="ID" accessor="id" />
Expand Down Expand Up @@ -1440,6 +1471,7 @@ WithHorizontalScroll.argTypes = HideControls;
WithHorizontalScrollAndFirstColumnFixed.argTypes = HideControls;
WithHorizontalScrollAndLastColumnFixed.argTypes = HideControls;
WithDefaultAscendingSortByName.argTypes = HideControls;
WithDefaultAscendingSortUsingHook.argTypes = HideControls;
WithIconButtons.argTypes = HideControls;
WithPrimaryAndSecondaryRows.argTypes = HideControls;
WithPrimaryAndSecondaryRowsAndComplexValues.argTypes = HideControls;
Expand Down