diff --git a/package-lock.json b/package-lock.json index 75fdbd668..0f5b965be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,9 @@ "name": "code-sandbox", "version": "0.0.0", "dependencies": { - "@abgov/react-components": "6.2.1", - "@abgov/ui-components-common": "1.2.1", - "@abgov/web-components": "1.32.1", + "@abgov/react-components": "6.2.2-alpha.4", + "@abgov/ui-components-common": "1.2.2-alpha.2", + "@abgov/web-components": "1.33.0-alpha.2", "@faker-js/faker": "^8.3.1", "highlight.js": "^11.8.0", "js-cookie": "^3.0.5", @@ -67,9 +67,10 @@ } }, "node_modules/@abgov/react-components": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@abgov/react-components/-/react-components-6.2.1.tgz", - "integrity": "sha512-CQ2Grpv5R/QR2UifwGP9VvAGDFxCwCpUlfSkcOKMXFUvwT+IQ6FbHPjgKkGViF2Td813/m7Qz7woo8DvoIIQfQ==", + "version": "6.2.2-alpha.4", + "resolved": "https://registry.npmjs.org/@abgov/react-components/-/react-components-6.2.2-alpha.4.tgz", + "integrity": "sha512-3Du7aDHKryBIeJx+uOmzZxLaqeHEXrmJCK8ErE4h8qZ40fjpp0aOp01WcAua8zMG2jxqOxlqSJGZ85jluFB5Og==", + "license": "Apache-2.0", "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0", "react": "^17.0.0 || ^18.0.0", @@ -77,14 +78,16 @@ } }, "node_modules/@abgov/ui-components-common": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@abgov/ui-components-common/-/ui-components-common-1.2.1.tgz", - "integrity": "sha512-RIvetCp7das7VmxiOqudQ2CGFIsWlt9poL65R03SOhpgHPco5PqDNYLF+QiFlHu1Aeq9YMhIk5miHjpNBUWLSQ==" + "version": "1.2.2-alpha.2", + "resolved": "https://registry.npmjs.org/@abgov/ui-components-common/-/ui-components-common-1.2.2-alpha.2.tgz", + "integrity": "sha512-bknk+IfzGAy+v13khWyUhXN/nIoYYhQ+Yg2C7T5lWy2i53DBUSP/Yq9h1RR2DGwpSi5406QMpq8o8xlYVR9G0g==", + "license": "Apache-2.0" }, "node_modules/@abgov/web-components": { - "version": "1.32.1", - "resolved": "https://registry.npmjs.org/@abgov/web-components/-/web-components-1.32.1.tgz", - "integrity": "sha512-Ezj17LYk5RgnNN3TVazuvlPjyOSfo4ArtpOE2VrnC0XJ0wIBKfmtcXwhdFATtLCtR/ILix829QUU0kxXzkRcSA==", + "version": "1.33.0-alpha.2", + "resolved": "https://registry.npmjs.org/@abgov/web-components/-/web-components-1.33.0-alpha.2.tgz", + "integrity": "sha512-h9wi1of2+wyQx5HYKrkFNGiG5CP+V0w0WRbWAT35nFVlM3V9Bwg8vaw2GVEYDlDVrR817lJl+1a0Ehi3jEU+oA==", + "license": "Apache-2.0", "peerDependencies": { "@sveltejs/vite-plugin-svelte": "3.x", "glob": "10.x", diff --git a/package.json b/package.json index 664fd95f5..17ba79bbe 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,9 @@ "prettier": "npx prettier . --write" }, "dependencies": { - "@abgov/react-components": "6.2.1", - "@abgov/ui-components-common": "1.2.1", - "@abgov/web-components": "1.32.1", + "@abgov/react-components": "6.2.2-alpha.4", + "@abgov/ui-components-common": "1.2.2-alpha.2", + "@abgov/web-components": "1.33.0-alpha.2", "@faker-js/faker": "^8.3.1", "highlight.js": "^11.8.0", "js-cookie": "^3.0.5", diff --git a/src/examples/filter-chip/FilterChipExamples.tsx b/src/examples/filter-chip/FilterChipExamples.tsx index 488c9a5ca..d04771267 100644 --- a/src/examples/filter-chip/FilterChipExamples.tsx +++ b/src/examples/filter-chip/FilterChipExamples.tsx @@ -1,3 +1,4 @@ +import { TableWithGlobalFiltersExample } from "@examples/filter-chip/TableWithGlobalFiltersExample.tsx"; import { FilterChipDeleteEventExample } from "@examples/filter-chip/FilterChipDeleteEventExample.tsx"; import { FilterChipInteractiveExample } from "@examples/filter-chip/FilterChipInteractiveExample.tsx"; import { FilterChipTypedChipExample } from "@examples/filter-chip/FilterChipTypedChipExample.tsx"; @@ -14,8 +15,11 @@ export const FilterChipExamples = () => {

Interactive Example

-

Typed Chips Example

+

Create a chip through typing

+ +

Filter data in a table

+ ) } diff --git a/src/examples/filter-chip/FilterChipTypedChipExample.tsx b/src/examples/filter-chip/FilterChipTypedChipExample.tsx index 2ae8f0f6c..921ec650d 100644 --- a/src/examples/filter-chip/FilterChipTypedChipExample.tsx +++ b/src/examples/filter-chip/FilterChipTypedChipExample.tsx @@ -1,68 +1,62 @@ -import { GoabContainer, GoabFilterChip, GoabInput } from "@abgov/react-components"; +import { GoabContainer, GoabFilterChip, GoabFormItem, GoabInput, GoabInputOnChangeDetail, GoabInputOnKeyPressDetail } from "@abgov/react-components"; import { CodeSnippet } from "@components/code-snippet/CodeSnippet.tsx"; import { useContext, useState } from "react"; import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; export const FilterChipTypedChipExample = () => { const {version} = useContext(LanguageVersionContext); - const [typedChips, setTypedChips] = useState([ - "Typed Chip 1", - "Typed Chip 2", - "Typed Chip 3", - ]); - + const [typedChips, setTypedChips] = useState([]); const [inputValue, setInputValue] = useState(""); - const handleInputChange = (_name: string, value: string) => { - setInputValue(value); - }; - const handleInputKeyDown = (_name: string, value: string, key: string) => { - if (key === "Enter" && value.trim() !== "") { - setTypedChips(prevChips => [...prevChips, value.trim()]); - setTimeout(() => { - setInputValue(""); - }, 0); - } else if (key === "Backspace" && value === "" && typedChips.length > 0) { - setTypedChips(prevChips => prevChips.slice(0, -1)); - } - }; + + const handleInputChange = (detail: GoabInputOnChangeDetail) => { + const newValue = detail.value.trim(); + setInputValue(newValue); + }; + + const handleInputKeyPress = (detail: GoabInputOnKeyPressDetail) => { + const newValue = detail.value.trim(); + if (detail.key === "Enter" && newValue !== "") { + setTypedChips(prevChips => [...prevChips, newValue]); + setTimeout(() => { + setInputValue(""); + }, 0); + } else if (detail.key === "Backspace" && newValue === "" && typedChips.length > 0) { + setTypedChips(prevChips => prevChips.slice(0, -1)); + } + }; const removeTypedChip = (chip: string) => { setTypedChips(prevChips => prevChips.filter(c => c !== chip)); }; + return ( + // NOTE: Input onKeyPress functionality breaks when wrapped in Sandbox component + // <> -
- handleInputChange(detail.name, detail.value)} - onKeyPress={detail => handleInputKeyDown(detail.name, detail.value, detail.key)} - width="30ch" - mr="s" - /> - {typedChips.map((chip, index) => ( - removeTypedChip(chip)} - mr="s" - mt="s" - mb="s" - /> - ))} -
-
+ + + +
+ {typedChips.length > 0 && + typedChips.map((typedChip, index) => ( + removeTypedChip(typedChip)} + /> + ))} +
+ + {version === "old" && ( <> { tags="angular" allowCopy={true} code={` +export class FilterChipComponent { + typedChips: string[] = []; + inputValue = ""; - import { Component } from "@angular/core"; - - @Component({ - selector: "abgov-chip", - templateUrl: "./filter-chip.component.html", - styleUrl: "./filter-chip.component.css", - }) - export class FilterChipComponent { - - typedChips: string[] = ["Typed Chip 1", "Typed Chip 2", "Typed Chip 3"]; - inputValue = ""; + handleInputChange(event: Event): void { + const newValue = (event.target as HTMLInputElement).value.trim(); + this.inputValue = newValue; + } + handleInputKeyPress(event: KeyboardEvent): void { + const newValue = (event.target as HTMLInputElement).value.trim(); + if (event.key === "Enter" && newValue !== "") { + this.addChip(); + } else if (!this.inputValue && this.typedChips.length > 0 && event.key === "Backspace") { + this.typedChips.pop(); + } + } - onInput(event: Event): void { - this.inputValue = (event.target as HTMLInputElement).value; - } - - addChip(): void { - if (this.inputValue.trim()) { - this.typedChips.push(this.inputValue.trim()); - this.inputValue = ""; - } - } - - removeTypedChip(chip: string): void { - this.typedChips = this.typedChips.filter((c) => c !== chip); - } + addChip(): void { + if (this.inputValue.trim()) { + this.typedChips.push(this.inputValue.trim()); + this.inputValue = ""; + } + } - handleBackspace(event: KeyboardEvent): void { - if (!this.inputValue && this.typedChips.length > 0 && event.key === "Backspace") { - this.typedChips.pop(); - event.preventDefault(); - } - } - } - + removeTypedChip(chip: string): void { + this.typedChips = this.typedChips.filter((c) => c !== chip); + } +} `} /> { tags="angular" allowCopy={true} code={` - - - - + + + + +
+ + +
`} /> @@ -144,151 +132,193 @@ export const FilterChipTypedChipExample = () => { tags="angular" allowCopy={true} code={` - export class FilterChipComponent { - typedChips: string[] = ["Typed Chip 1", "Typed Chip 2", "Typed Chip 3"]; - inputValue = ""; +export class FilterChipComponent { + typedChips: string[] = []; + inputValue = ""; - onInput(detail: GoabInputOnChangeDetail): void { - this.inputValue = detail.value; - } + handleInputChange(detail: GoabInputOnChangeDetail): void { + const newValue = detail.value.trim(); + this.inputValue = newValue; + } - addChip(): void { - if (this.inputValue.trim()) { - this.typedChips.push(this.inputValue.trim()); - this.inputValue = ""; - } - } + handleInputKeyPress(detail: GoabInputOnKeyPressDetail): void { + const newValue = detail.value.trim(); + if (detail.key === "Enter" && newValue !== "") { + this.addChip(); + } else if (!this.inputValue && this.typedChips.length > 0 && detail.key === "Backspace") { + this.typedChips.pop(); + } + } - removeTypedChip(chip: string): void { - this.typedChips = this.typedChips.filter((c) => c !== chip); - } + addChip(): void { + if (this.inputValue.trim()) { + this.typedChips.push(this.inputValue.trim()); + this.inputValue = ""; + } + } - handleBackspace(event: KeyboardEvent): void { - if (!this.inputValue && this.typedChips.length > 0 && event.key === "Backspace") { - this.typedChips.pop(); - event.preventDefault(); - } - } - `} + removeTypedChip(chip: string): void { + this.typedChips = this.typedChips.filter((c) => c !== chip); + } +} + `} /> Typed Chip - - - - - `} + + + + +
+ + +
+ `} /> )} - ([ - "Typed Chip 1", - "Typed Chip 2", - "Typed Chip 3", - ]); - const [inputValue, setInputValue] = useState(""); - const handleInputChange = (name: string, value: string) => { - setInputValue(value); - }; + {version === "old" && ( + <> + ([]); + const [inputValue, setInputValue] = useState(""); + + const handleInputChange = (_name: string, value: string) => { + const newValue = value.trim(); + setInputValue(newValue); + }; - const handleInputKeyDown = (name: string, - value: string, - key: string) => { - if (key === "Enter" && value.trim() !== "") { - setTypedChips((prevChips) => [...prevChips, value.trim()]); - setTimeout(() => { - setInputValue(""); - }, 0); - } else if (key === "Backspace" - && value === "" - && typedChips.length > 0) { - setTypedChips((prevChips) => prevChips.slice(0, -1)); - } - }; + const handleInputKeyPress = (_name: string, value: string, key: string) => { + const newValue = value.trim(); + if (key === "Enter" && newValue !== "") { + setTypedChips(prevChips => [...prevChips, newValue]); + setTimeout(() => { + setInputValue(""); + }, 0); + } else if (key === "Backspace" && newValue === "" && typedChips.length > 0) { + setTypedChips(prevChips => prevChips.slice(0, -1)); + } + }; - const removeTypedChip = (chip: string) => { - setTypedChips((prevChips) => prevChips.filter((c) => c !== chip)); - }; - `} - /> - {version === "old" && ( - - {typedChips.map((chip, index) => ( - removeTypedChip(chip)} - mr="s" - mt="s" - mb="s"/> - ))} - `} + const removeTypedChip = (chip: string) => { + setTypedChips(prevChips => prevChips.filter(c => c !== chip)); + }; + `} + /> + + + +
+ {typedChips.length > 0 && + typedChips.map((typedChip, index) => ( + removeTypedChip(typedChip)} + /> + ))} +
+ `} + /> + )} + {version === "new" && ( - handleInputChange(detail.name, detail.value)} - onKeyPress={(detail) => handleInputKeyDown(detail.name, detail.value, detail.key)} - width="30ch" - mr="s" - /> - {typedChips.map((chip, index) => ( - removeTypedChip(chip)} - mr="s" - mt="s" - mb="s"/> - ))} - `} + <> + ([]); + const [inputValue, setInputValue] = useState(""); + + const handleInputChange = (detail: GoabInputOnChangeDetail) => { + const newValue = detail.value.trim(); + setInputValue(newValue); + }; + + const handleInputKeyPress = (detail: GoabInputOnKeyPressDetail) => { + const newValue = detail.value.trim(); + if (detail.key === "Enter" && newValue !== "") { + setTypedChips(prevChips => [...prevChips, newValue]); + setTimeout(() => { + setInputValue(""); + }, 0); + } else if (detail.key === "Backspace" && newValue === "" && typedChips.length > 0) { + setTypedChips(prevChips => prevChips.slice(0, -1)); + } + }; + + const removeTypedChip = (chip: string) => { + setTypedChips(prevChips => prevChips.filter(c => c !== chip)); + }; + `} + /> + + + +
+ {typedChips.length > 0 && + typedChips.map((typedChip, index) => ( + removeTypedChip(typedChip)} + /> + ))} +
+ `} + /> + )} + //
); } diff --git a/src/examples/filter-chip/TableWithGlobalFiltersExample.tsx b/src/examples/filter-chip/TableWithGlobalFiltersExample.tsx new file mode 100644 index 000000000..d469f752f --- /dev/null +++ b/src/examples/filter-chip/TableWithGlobalFiltersExample.tsx @@ -0,0 +1,914 @@ +import { useCallback, useContext, useEffect, useMemo, useState } from "react"; +import { + GoabBadge, + GoabBlock, + GoabButton, + GoabContainer, + GoabFilterChip, + GoabFormItem, + GoabInput, + GoabTable, + GoabText, +} from "@abgov/react-components"; +import type { + GoabBadgeType, + GoabInputOnChangeDetail, + GoabInputOnKeyPressDetail, +} from "@abgov/react-components"; +import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; +import { CodeSnippet } from "@components/code-snippet/CodeSnippet.tsx"; + +export const TableWithGlobalFiltersExample = () => { + const { version } = useContext(LanguageVersionContext); + + const [typedChips, setTypedChips] = useState([]); + const [inputValue, setInputValue] = useState(""); + const [inputError, setInputError] = useState(""); + const errorEmpty = "Empty filter"; + const errorDuplicate = "Enter a unique filter"; + const data = useMemo( + () => [ + { + status: { type: "information" as GoabBadgeType, text: "In progress" }, + name: "Ivan Schmidt", + id: "7838576954", + }, + { + status: { type: "success" as GoabBadgeType, text: "Completed" }, + name: "Luz Lakin", + id: "8576953364", + }, + { + status: { type: "information" as GoabBadgeType, text: "In progress" }, + name: "Keith McGlynn", + id: "9846041345", + }, + { + status: { type: "success" as GoabBadgeType, text: "Completed" }, + name: "Melody Frami", + id: "7385256175", + }, + { + status: { type: "important" as GoabBadgeType, text: "Updated" }, + name: "Frederick Skiles", + id: "5807570418", + }, + { + status: { type: "success" as GoabBadgeType, text: "Completed" }, + name: "Dana Pfannerstill", + id: "5736306857", + }, + ], + [] + ); + const [dataFiltered, setDataFiltered] = useState(data); + + const handleInputChange = (detail: GoabInputOnChangeDetail) => { + const newValue = detail.value.trim(); + setInputValue(newValue); + }; + + const handleInputKeyPress = (detail: GoabInputOnKeyPressDetail) => { + if (detail.key === "Enter") { + applyFilter(); + } + }; + + const applyFilter = () => { + if (inputValue === "") { + setInputError(errorEmpty); + return; + } + if (typedChips.length > 0 && typedChips.includes(inputValue)) { + setInputError(errorDuplicate); + return; + } + setTypedChips([...typedChips, inputValue]); + setTimeout(() => { + setInputValue(""); + }, 0); + setInputError(""); + }; + + const removeTypedChip = (chip: string) => { + setTypedChips(typedChips.filter(c => c !== chip)); + setInputError(""); + }; + + const checkNested = useCallback((obj: object, chip: string): boolean => { + return Object.values(obj).some(value => + typeof value === "object" && value !== null + ? checkNested(value, chip) + : typeof value === "string" && value.toLowerCase().includes(chip.toLowerCase()) + ); + }, []); + + const getFilteredData = useCallback( + (typedChips: string[]) => { + if (typedChips.length === 0) { + return data; + } + const filteredData = data.filter((item: object) => + typedChips.every(chip => checkNested(item, chip)) + ); + + return filteredData; + }, + [checkNested, data] + ); + + useEffect(() => { + setDataFiltered(getFilteredData(typedChips)); + }, [getFilteredData, typedChips]); + + return ( + // NOTE: Input onKeyPress functionality breaks when wrapped in Sandbox component + // + <> + + + + + + Filter + + + + + {typedChips.length > 0 && ( +
+ + Filter: + + {typedChips.length > 0 && + typedChips.map((typedChip, index) => ( + removeTypedChip(typedChip)} + /> + ))} + setTypedChips([])}> + Clear all + +
+ )} + + + + + Status + Name + ID Number + + + + {dataFiltered.map(item => ( + + + + + {item.name} + {item.id} + + ))} + + + + {dataFiltered.length === 0 && data.length > 0 && ( + + No results found + + )} +
+ + {version === "old" && ( + <> + c !== chip); + this.dataFiltered = this.getFilteredData(this.typedChips); + this.inputError = ""; + } + + removeAllTypedChips() { + this.typedChips = []; + this.dataFiltered = this.getFilteredData(this.typedChips); + this.inputError = ""; + } + + getFilteredData(typedChips: string[]) { + if (typedChips.length === 0) { + return this.data; + } + const filteredData = this.data.filter((item) => + typedChips.every((chip) => this.checkNested(item, chip)), + ); + return filteredData; + } + + checkNested(obj: object, chip: string): boolean { + return Object.values(obj).some((value) => + typeof value === "object" && value !== null + ? this.checkNested(value, chip) + : typeof value === "string" && value.toLowerCase().includes(chip.toLowerCase()), + ); + } +} + `} + /> + + + + + + Filter + + + + + + + Filter: + + + + Clear all + + + + + + + Status + Name + ID Number + + + + + + + + {{ item.name }} + {{ item.id }} + + + + + + No results found + + `} + /> + ([]); + const [inputValue, setInputValue] = useState(""); + const [inputError, setInputError] = useState(""); + const errorEmpty = "Empty filter"; + const errorDuplicate = "Enter a unique filter"; + const data = useMemo( + () => [ + { + status: { type: "information" as GoABadgeType, text: "In progress" }, + name: "Ivan Schmidt", + id: "7838576954", + }, + { + status: { type: "success" as GoABadgeType, text: "Completed" }, + name: "Luz Lakin", + id: "8576953364", + }, + { + status: { type: "information" as GoABadgeType, text: "In progress" }, + name: "Keith McGlynn", + id: "9846041345", + }, + { + status: { type: "success" as GoABadgeType, text: "Completed" }, + name: "Melody Frami", + id: "7385256175", + }, + { + status: { type: "important" as GoABadgeType, text: "Updated" }, + name: "Frederick Skiles", + id: "5807570418", + }, + { + status: { type: "success" as GoABadgeType, text: "Completed" }, + name: "Dana Pfannerstill", + id: "5736306857", + }, + ], + [], + ); + const [dataFiltered, setDataFiltered] = useState(data); + + const handleInputChange = (_name: string, value: string) => { + const newValue = value.trim(); + setInputValue(newValue); + }; + + const handleInputKeyPress = (_name: string, _value: string, key: string) => { + if (key === "Enter") { + applyFilter(); + } + }; + + const applyFilter = () => { + if (inputValue === "") { + setInputError(errorEmpty); + return; + } + if (typedChips.length > 0 && typedChips.includes(inputValue)) { + setInputError(errorDuplicate); + return; + } + setTypedChips([...typedChips, inputValue]); + setTimeout(() => { + setInputValue(""); + }, 0); + setInputError(""); + }; + + const removeTypedChip = (chip: string) => { + setTypedChips(typedChips.filter((c) => c !== chip)); + setInputError(""); + }; + + const checkNested = useCallback((obj: object, chip: string): boolean => { + return Object.values(obj).some((value) => + typeof value === "object" && value !== null + ? checkNested(value, chip) + : typeof value === "string" && value.toLowerCase().includes(chip.toLowerCase()), + ); + }, []); + + const getFilteredData = useCallback( + (typedChips: string[]) => { + if (typedChips.length === 0) { + return data; + } + const filteredData = data.filter((item: object) => + typedChips.every((chip) => checkNested(item, chip)), + ); + + return filteredData; + }, + [checkNested, data], + ); + + useEffect(() => { + setDataFiltered(getFilteredData(typedChips)); + }, [getFilteredData, typedChips]); + `} + /> + + + + + + + Filter + + + + + {typedChips.length > 0 && ( +
+ + Filter: + + {typedChips.length > 0 && + typedChips.map((typedChip, index) => ( + removeTypedChip(typedChip)} + /> + ))} + setTypedChips([])} + > + Clear all + +
+ )} + + + + + Status + Name + ID Number + + + + {dataFiltered.map((item) => ( + + + + + {item.name} + {item.id} + + ))} + + + + {dataFiltered.length === 0 && data.length > 0 && ( + No results found + )} + + `} + /> + + )} + + {version === "new" && ( + <> + c !== chip); + this.dataFiltered = this.getFilteredData(this.typedChips); + this.inputError = ""; + } + + removeAllTypedChips() { + this.typedChips = []; + this.dataFiltered = this.getFilteredData(this.typedChips); + this.inputError = ""; + } + + getFilteredData(typedChips: string[]) { + if (typedChips.length === 0) { + return this.data; + } + const filteredData = this.data.filter((item) => + typedChips.every((chip) => this.checkNested(item, chip)), + ); + return filteredData; + } + + checkNested(obj: object, chip: string): boolean { + return Object.values(obj).some((value) => + typeof value === "object" && value !== null + ? this.checkNested(value, chip) + : typeof value === "string" && value.toLowerCase().includes(chip.toLowerCase()), + ); + } +} + `} + /> + + + + + + Filter + + + + + + + Filter: + + + + Clear all + + + + + + + Status + Name + ID Number + + + + + + + + {{ item.name }} + {{ item.id }} + + + + + + No results found + + `} + /> + ([]); + const [inputValue, setInputValue] = useState(""); + const [inputError, setInputError] = useState(""); + const errorEmpty = "Empty filter"; + const errorDuplicate = "Enter a unique filter"; + const data = useMemo( + () => [ + { + status: { type: "information" as GoabBadgeType, text: "In progress" }, + name: "Ivan Schmidt", + id: "7838576954", + }, + { + status: { type: "success" as GoabBadgeType, text: "Completed" }, + name: "Luz Lakin", + id: "8576953364", + }, + { + status: { type: "information" as GoabBadgeType, text: "In progress" }, + name: "Keith McGlynn", + id: "9846041345", + }, + { + status: { type: "success" as GoabBadgeType, text: "Completed" }, + name: "Melody Frami", + id: "7385256175", + }, + { + status: { type: "important" as GoabBadgeType, text: "Updated" }, + name: "Frederick Skiles", + id: "5807570418", + }, + { + status: { type: "success" as GoabBadgeType, text: "Completed" }, + name: "Dana Pfannerstill", + id: "5736306857", + }, + ], + [] + ); + const [dataFiltered, setDataFiltered] = useState(data); + + const handleInputChange = (detail: GoabInputOnChangeDetail) => { + const newValue = detail.value.trim(); + setInputValue(newValue); + }; + + const handleInputKeyPress = (detail: GoabInputOnKeyPressDetail) => { + if (detail.key === "Enter") { + applyFilter(); + } + }; + + const applyFilter = () => { + if (inputValue === "") { + setInputError(errorEmpty); + return; + } + if (typedChips.length > 0 && typedChips.includes(inputValue)) { + setInputError(errorDuplicate); + return; + } + setTypedChips([...typedChips, inputValue]); + setTimeout(() => { + setInputValue(""); + }, 0); + setInputError(""); + }; + + const removeTypedChip = (chip: string) => { + setTypedChips(typedChips.filter(c => c !== chip)); + setInputError(""); + }; + + const checkNested = useCallback((obj: object, chip: string): boolean => { + return Object.values(obj).some(value => + typeof value === "object" && value !== null + ? checkNested(value, chip) + : typeof value === "string" && value.toLowerCase().includes(chip.toLowerCase()) + ); + }, []); + + const getFilteredData = useCallback( + (typedChips: string[]) => { + if (typedChips.length === 0) { + return data; + } + const filteredData = data.filter((item: object) => + typedChips.every(chip => checkNested(item, chip)) + ); + + return filteredData; + }, + [checkNested, data] + ); + + useEffect(() => { + setDataFiltered(getFilteredData(typedChips)); + }, [getFilteredData, typedChips]); + `} + /> + + + + + + + Filter + + + + + {typedChips.length > 0 && ( +
+ + Filter: + + {typedChips.length > 0 && + typedChips.map((typedChip, index) => ( + removeTypedChip(typedChip)} + /> + ))} + setTypedChips([])}> + Clear all + +
+ )} + + + + + Status + Name + ID Number + + + + {dataFiltered.map(item => ( + + + + + {item.name} + {item.id} + + ))} + + + + {dataFiltered.length === 0 && data.length > 0 && ( + No results found + )} + + `} + /> + + )} + + //
+ ); +}; diff --git a/src/routes/components/Badge.tsx b/src/routes/components/Badge.tsx index 5e5ccbaa4..39469c39a 100644 --- a/src/routes/components/Badge.tsx +++ b/src/routes/components/Badge.tsx @@ -18,14 +18,17 @@ const description = const category = Category.FEEDBACK_AND_ALERTS; const relatedComponents = [ { - link: "/components/filter-chip", name: "Filter Chip" + link: "/components/filter-chip", + name: "Filter chip", }, { - link: "/components/icons", name: "Icons" + link: "/components/icons", + name: "Icons", }, { - link: "/components/table", name: "Table" - } + link: "/components/table", + name: "Table", + }, ]; type ComponentPropsType = GoabBadgeProps; @@ -152,7 +155,7 @@ export default function BadgePage() { type: "Spacing(none | 3xs | 2xs | xs | s | m | l | xl | 2xl | 3xl | 4xl)", description: "Apply margin to the top, right, bottom, and/or left of the component.", }, - ] + ]; function onSandboxChange(badgeBindings: ComponentBinding[], props: Record) { setBadgeBindings(badgeBindings); @@ -161,16 +164,25 @@ export default function BadgePage() { return ( <> - + - -

Component

+

+ Component +

- +
@@ -180,8 +192,7 @@ export default function BadgePage() { Design guidelines - } - > + }>
diff --git a/src/routes/components/FilterChip.tsx b/src/routes/components/FilterChip.tsx index 924a9fb20..8ae1920b4 100644 --- a/src/routes/components/FilterChip.tsx +++ b/src/routes/components/FilterChip.tsx @@ -1,9 +1,10 @@ import { Category, ComponentHeader } from "@components/component-header/ComponentHeader.tsx"; import { GoabBadge, - GoabFilterChip, GoabFilterChipProps, + GoabFilterChip, + GoabFilterChipProps, GoabTab, - GoabTabs + GoabTabs, } from "@abgov/react-components"; import { ComponentBinding, Sandbox } from "@components/sandbox"; import { useState } from "react"; @@ -15,7 +16,7 @@ import { ComponentContent } from "@components/component-content/ComponentContent import { LegacyTestIdProperties, MarginProperty, - TestIdProperty + TestIdProperty, } from "@components/component-properties/common-properties.ts"; import { FilterChipExamples } from "@examples/filter-chip/FilterChipExamples.tsx"; @@ -26,6 +27,7 @@ const category = Category.FEEDBACK_AND_ALERTS; const relatedComponents = [ { link: "/components/badge", name: "Badge" }, { link: "/components/popover", name: "Popover" }, + { link: "/components/input", name: "Input" }, { link: "/components/table", name: "Table" }, ]; type ComponentPropsType = GoabFilterChipProps; @@ -130,7 +132,7 @@ export default function FilterChipPage() { oldProperties={oldComponentProperties} /> - + & { +type ComponentPropsType = Omit & { onSort?: (sortBy: string, sortDir: number) => void; }; export default function TablePage() { - const {version} = useContext(LanguageVersionContext); + const { version } = useContext(LanguageVersionContext); const [tableProps, setTableProps] = useState({ - width: "100%" + width: "100%", }); const [tableBindings, setTableBindings] = useState([ { @@ -83,6 +84,7 @@ export default function TablePage() { description: "Apply margin to the top, right, bottom, and/or left of the component.", }, ]; + const componentProperties: ComponentProperty[] = [ { name: "width", @@ -162,24 +164,23 @@ export default function TablePage() { relatedComponents={[ { link: "/components/button", name: "Button" }, { link: "/components/dropdown", name: "Dropdown" }, + { link: "/components/filter-chip", name: "Filter chip" }, { link: "/components/pagination", name: "Pagination" }, { link: "/components/tabs", name: "Tabs" }, ]} /> - { if (tableProps.onSort) { tableProps.onSort(detail.sortBy, detail.sortDir); } - }} - > + }}> Status @@ -233,8 +234,13 @@ export default function TablePage() { - - + +

Sortable columns

@@ -602,6 +608,9 @@ export default function TablePage() { + +

Filter data in a table

+