diff --git a/apps/angular-demo/src/app/table/table.html b/apps/angular-demo/src/app/table/table.html index 752cc76e9..646485c64 100644 --- a/apps/angular-demo/src/app/table/table.html +++ b/apps/angular-demo/src/app/table/table.html @@ -1,8 +1,14 @@ - + - Col 1 - Col 2 + + First name and really long header + + + Last name + Col 3 diff --git a/apps/angular-demo/src/app/table/table.ts b/apps/angular-demo/src/app/table/table.ts index a1cfefd37..e46074b14 100644 --- a/apps/angular-demo/src/app/table/table.ts +++ b/apps/angular-demo/src/app/table/table.ts @@ -1,7 +1,8 @@ import { Component } from "@angular/core"; +import { faker } from "@faker-js/faker"; interface User { - id: number; + id: string; firstName: string; lastName: string; age: number; @@ -17,90 +18,18 @@ export class TableComponent { constructor() { for (let i = 0; i < 10; i++) { this.users.push({ - id: Math.random(), - firstName: getFirstName(), - lastName: getLastName(), - age: getAge(), + id: faker.datatype.uuid(), + firstName: faker.name.firstName(), + lastName: faker.name.lastName(), + age: faker.datatype.number({ min: 18, max: 60 }), }); } } -} - -function getFirstName(): string { - const index = Math.floor(Math.random() * (firstNames.length - 1)); - return firstNames[index]; -} - -function getLastName(): string { - const index = Math.floor(Math.random() * (lastNames.length - 1)); - return lastNames[index]; -} -function getAge(): number { - return 18 + Math.floor(Math.random() * 60); + handleSort(event: any) { + const { sortBy, sortDir } = event.detail; + this.users.sort( + (a: any, b: any) => (a[sortBy] > b[sortBy] ? -1 : 1) * sortDir + ); + } } - -const firstNames = [ - "James", - "John", - "Robert", - "Michael", - "William", - "David", - "Richard", - "Charles", - "Josep", - "Thomas", - "Christopher", - "Daniel", - "Paul", - "Mark", - "Donald", - "Georg", - "Kenneth", - "Steve", - "Edward", - "Brian", - "Ronald", - "Anthon", - "Kevin", - "Jason", - "Matthew", - "Gary", - "Timothy", - "Jose", - "Larry", - "Jeffrey", - "Frank", - "Scot", - "Eric", - "Stephen", - "Andrew", -]; - -const lastNames = [ - "Smith", - "Johnson", - "Williams", - "Brown", - "Jones", - "Garcia", - "Miller", - "Davis", - "Rodriguez", - "Martinez", - "Hernandez", - "Lopez", - "Gonzalez", - "Wilson", - "Anderson", - "Thomas", - "Taylor", - "Moore", - "Jackson", - "Martin", - "Lee", - "Thompson", - "White", - "Harris", -]; diff --git a/apps/react-demo/src/routes/table.tsx b/apps/react-demo/src/routes/table.tsx index b7fb295a7..e43cdb902 100644 --- a/apps/react-demo/src/routes/table.tsx +++ b/apps/react-demo/src/routes/table.tsx @@ -1,6 +1,14 @@ import * as React from "react"; import { GoATable } from "@abgov/react-components"; import { useEffect } from "react"; +import { faker } from "@faker-js/faker"; + +interface User { + id: string; + firstName: string; + lastName: string; + age: number; +} export default function Table() { const [data, setData] = React.useState([]); @@ -9,10 +17,10 @@ export default function Table() { const users: User[] = []; for (let i = 0; i < 10; i++) { users.push({ - id: Math.random(), - firstName: getFirstName(), - lastName: getLastName(), - age: getAge(), + id: faker.datatype.uuid(), + firstName: faker.name.firstName(), + lastName: faker.name.lastName(), + age: faker.datatype.number({ min: 18, max: 60 }), }); } setData(users); @@ -66,13 +74,6 @@ export default function Table() { ); } -interface User { - id: number; - firstName: string; - lastName: string; - age: number; -} - function TableStaticHead() { return ( @@ -109,82 +110,3 @@ function TableStaticBody() { ); } - -function getFirstName(): string { - const index = Math.floor(Math.random() * (firstNames.length - 1)); - return firstNames[index]; -} - -function getLastName(): string { - const index = Math.floor(Math.random() * (lastNames.length - 1)); - return lastNames[index]; -} - -function getAge(): number { - return 18 + Math.floor(Math.random() * 60); -} - -const firstNames = [ - "James", - "John", - "Robert", - "Michael", - "William", - "David", - "Richard", - "Charles", - "Josep", - "Thomas", - "Christopher", - "Daniel", - "Paul", - "Mark", - "Donald", - "Georg", - "Kenneth", - "Steve", - "Edward", - "Brian", - "Ronald", - "Anthon", - "Kevin", - "Jason", - "Matthew", - "Gary", - "Timothy", - "Jose", - "Larry", - "Jeffrey", - "Frank", - "Scot", - "Eric", - "Stephen", - "Andrew", -]; - -const lastNames = [ - "Smith", - "Johnson", - "Williams", - "Brown", - "Jones", - "Garcia", - "Miller", - "Davis", - "Rodriguez", - "Martinez", - "Hernandez", - "Lopez", - "Gonzalez", - "Wilson", - "Anderson", - "Thomas", - "Taylor", - "Moore", - "Jackson", - "Martin", - "Lee", - "Thompson", - "White", - "Harris", -]; diff --git a/libs/docs/src/BrowserSupport.stories.mdx b/libs/docs/src/BrowserSupport.stories.mdx index ec3811156..da5372569 100644 --- a/libs/docs/src/BrowserSupport.stories.mdx +++ b/libs/docs/src/BrowserSupport.stories.mdx @@ -1,5 +1,5 @@ import { Meta } from "@storybook/addon-docs"; -import { GoATable } from '@abgov/react-components'; +import { GoATable } from "@abgov/react-components"; @@ -29,16 +29,24 @@ The design system components are tested on the following browsers and devices: Supported Supported Supported - Android OS: 10+, iOS: 15+ + + Android OS: 10+, iOS: 15+ + Microsoft Edge Latest and Latest -1 Supported Supported - Supported (Not tested) - Supported (Not tested) - Android OS: 10+, iOS: 15+ + + Supported (Not tested) + + + Supported (Not tested) + + + Android OS: 10+, iOS: 15+ + Mozilla Firefox @@ -46,8 +54,12 @@ The design system components are tested on the following browsers and devices: Supported Supported Supported - Supported (Not tested) - Android OS: 10+, iOS: 15+ + + Supported (Not tested) + + + Android OS: 10+, iOS: 15+ + Apple Safari @@ -56,10 +68,12 @@ The design system components are tested on the following browsers and devices: Supported N/A Supported - Android OS: 10+, iOS: 15+ + + Android OS: 10+, iOS: 15+ + -*Latest: The latest stable version +\*Latest: The latest stable version -**Latest -1: The version prior to the latest version. \ No newline at end of file +\*\*Latest -1: The version prior to the latest version. diff --git a/libs/docs/src/components/common/Dropdown.stories.mdx b/libs/docs/src/components/common/Dropdown.stories.mdx index 04030e444..dc8a41286 100644 --- a/libs/docs/src/components/common/Dropdown.stories.mdx +++ b/libs/docs/src/components/common/Dropdown.stories.mdx @@ -564,7 +564,10 @@ export const Template = (args) => { #### Placeholder - + {Template.bind()} diff --git a/libs/docs/src/components/common/Table.stories.mdx b/libs/docs/src/components/common/Table.stories.mdx index a9fedb370..0b03442f9 100644 --- a/libs/docs/src/components/common/Table.stories.mdx +++ b/libs/docs/src/components/common/Table.stories.mdx @@ -9,6 +9,8 @@ import { Markdown, } from "@abgov/shared/storybook-common"; import { GoATable } from "@abgov/react-components"; +import { useState, useEffect } from "react"; +import { faker } from "@faker-js/faker"; @@ -34,6 +36,18 @@ import { GoATable } from "@abgov/react-components"; defaultValue="normal" description="A relaxed variant of the table with more vertical padding for the cells" /> + +
@@ -92,6 +106,61 @@ export const TableTemplate = (args) => { ); }; +export const SortableTableTemplate = (args) => { + const [users, setUsers] = useState([]); + const [sortCol, setSortCol] = useState(""); + const [sortDir, setSortDir] = useState(1); + useEffect(() => { + const _users = []; + for (let i = 0; i < 10; i++) { + _users.push({ + id: faker.datatype.uuid(), + firstName: faker.name.firstName(), + lastName: faker.name.lastName(), + age: faker.datatype.number({ min: 18, max: 60 }), + }); + } + setUsers(_users); + }, []); + function sortData(sortBy, sortDir) { + const _users = [...users]; + _users.sort((a, b) => { + return (a[sortBy] > b[sortBy] ? -1 : 1) * sortDir; + }); + setUsers(_users); + } + return ( + + + + + + First name + + + + + Last name + + + + Age + + + + + {users.map((user) => ( + + {user.firstName} + {user.lastName} + {user.age} + + ))} + + + ); +}; + #### Basic {TableTemplate.bind({})} @@ -285,6 +354,116 @@ export const TableTemplate = (args) => { +#### Sortable columns + +{SortableTableTemplate.bind()} + + + + { + return (a[sortBy] > b[sortBy] ? -1 : 1) * sortDir; + }) + } + } + `} + /> + + + + First name and really long header + Last name + Age + + + + + {{ user.firstName }} + {{ user.lastName }} + {{ user.age }} + + + + `} + /> + + + { + const _users = [] + for (let i = 0; i < 10; i++) { + _users.push({ + id: faker.datatype.uuid(), + firstName: faker.name.firstName(), + lastName: faker.name.lastName(), + age: faker.datatype.number({ min: 18, max: 60 }), + }); + } + setUsers(_users); + }, []) + function sortData(sortBy, sortDir) { + const _users = [...users]; + _users.sort((a, b) => { + return (a[sortBy] > b[sortBy] ? -1 : 1) * sortDir; + }); + setUsers(_users); + } + return ( + + + + First name + Last name + Age + + + + {users.map(user => + + {user.firstName} + {user.lastName} + {user.age} + + )} + + + ); + `} + /> + + + --- diff --git a/libs/react-components/src/lib/table/table.tsx b/libs/react-components/src/lib/table/table.tsx index d7704ef42..0e6d1ff24 100644 --- a/libs/react-components/src/lib/table/table.tsx +++ b/libs/react-components/src/lib/table/table.tsx @@ -1,9 +1,10 @@ -import React, { ReactNode } from "react"; +import React, { ReactNode, useEffect, useRef } from "react"; import { Margins } from "../../common/styling"; export type TableVariant = "normal" | "relaxed"; interface WCProps extends Margins { + ref?: React.MutableRefObject; width?: string; stickyheader?: boolean; variant?: TableVariant; @@ -22,14 +23,33 @@ declare global { /* eslint-disable-next-line */ export interface TableProps extends Margins { width?: string; + onSort?: (sortBy: string, sortDir: number) => void; // stickyHeader?: boolean; TODO: enable this later variant?: TableVariant; children: ReactNode; } export function GoATable(props: TableProps) { + const ref = useRef(null); + useEffect(() => { + if (!ref.current) { + return; + } + const current = ref.current; + const sortListener = (e: unknown) => { + const { sortBy, sortDir } = (e as CustomEvent).detail; + props.onSort?.(sortBy, sortDir); + }; + + current.addEventListener("_sort", sortListener); + return () => { + current.removeEventListener("_sort", sortListener); + }; + }, [ref, props.onSort]); + return ( + + + + diff --git a/libs/web-components/src/assets/arrows-both.svg b/libs/web-components/src/assets/arrows-both.svg new file mode 100644 index 000000000..c014cfebf --- /dev/null +++ b/libs/web-components/src/assets/arrows-both.svg @@ -0,0 +1,28 @@ + + + + + + + + + + diff --git a/libs/web-components/src/components/table/Table.svelte b/libs/web-components/src/components/table/Table.svelte index 81e3b4833..0446692f9 100644 --- a/libs/web-components/src/components/table/Table.svelte +++ b/libs/web-components/src/components/table/Table.svelte @@ -1,9 +1,10 @@ - + +