From b5b5873ea4dde211d4ec0a6d5ff803c09177d09a Mon Sep 17 00:00:00 2001 From: Miguel Date: Fri, 19 Sep 2025 16:06:11 -0600 Subject: [PATCH 1/2] feat(#2120): table - added and modified contextual examples --- ...-a-table-so-they-can-be-scanned-easily.tsx | 22 +- src/examples/filter-data-in-a-table.tsx | 1498 ++++++++++------- src/examples/tables/TablesExamples.tsx | 21 +- src/examples/tables/tables-page-examples.css | 3 + src/examples/zebra-stripes-in-a-table.tsx | 88 + src/routes/components/Table.tsx | 2 +- 6 files changed, 976 insertions(+), 658 deletions(-) create mode 100644 src/examples/tables/tables-page-examples.css create mode 100644 src/examples/zebra-stripes-in-a-table.tsx diff --git a/src/examples/display-numbers-in-a-table-so-they-can-be-scanned-easily.tsx b/src/examples/display-numbers-in-a-table-so-they-can-be-scanned-easily.tsx index ba87d5975..4e57cf46c 100644 --- a/src/examples/display-numbers-in-a-table-so-they-can-be-scanned-easily.tsx +++ b/src/examples/display-numbers-in-a-table-so-they-can-be-scanned-easily.tsx @@ -15,14 +15,24 @@ export const DisplayNumbersInATableSoTheyCanBeScannedEasily = () => { - Item 1 - Item 2 - 54 + Christian + Batz + 54356456 - Item 1 - Item 2 - 4567 + Brian + Wisozk + 23212321 + + + Neha + Jones + 23197213 + + + Tristan + Buckridge + 76312313 diff --git a/src/examples/filter-data-in-a-table.tsx b/src/examples/filter-data-in-a-table.tsx index e683be1c9..c1036e1e5 100644 --- a/src/examples/filter-data-in-a-table.tsx +++ b/src/examples/filter-data-in-a-table.tsx @@ -9,6 +9,9 @@ import { GoabInput, GoabTable, GoabText, + GoabPopover, + GoabRadioGroup, + GoabRadioItem } from "@abgov/react-components"; import type { GoabBadgeType, @@ -17,10 +20,12 @@ import type { } from "@abgov/ui-components-common"; import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; import { CodeSnippet } from "@components/code-snippet/CodeSnippet.tsx"; +import { GoabRadioGroupOnChangeDetail } from "@abgov/ui-components-common"; export const FilterDataInATable = () => { const { version } = useContext(LanguageVersionContext); + const [selectedFilter, setSelectedFilter] = useState(null); const [typedChips, setTypedChips] = useState([]); const [inputValue, setInputValue] = useState(""); const [inputError, setInputError] = useState(""); @@ -63,6 +68,12 @@ export const FilterDataInATable = () => { ); const [dataFiltered, setDataFiltered] = useState(data); + const target = ( + + Filter + + ); + const handleInputChange = (detail: GoabInputOnChangeDetail) => { const newValue = detail.value.trim(); setInputValue(newValue); @@ -103,31 +114,43 @@ export const FilterDataInATable = () => { ); }, []); + function radioGroupOnChange(event: GoabRadioGroupOnChangeDetail) { + setSelectedFilter(event.value); + } + const getFilteredData = useCallback( - (typedChips: string[]) => { - if (typedChips.length === 0) { - return data; + (typedChips: string[], selectedFilter: string | null) => { + let filteredData = data; + + if (typedChips.length > 0) { + filteredData = filteredData.filter((item: any) => + typedChips.every(chip => checkNested(item, chip)) + ); + } + + if (selectedFilter) { + filteredData = filteredData.filter( + (item: any) => item.status && item.status.text === selectedFilter + ); } - const filteredData = data.filter((item: object) => - typedChips.every(chip => checkNested(item, chip)) - ); return filteredData; }, [checkNested, data] ); + useEffect(() => { - setDataFiltered(getFilteredData(typedChips)); - }, [getFilteredData, typedChips]); + setDataFiltered(getFilteredData(typedChips, selectedFilter)); + }, [getFilteredData, typedChips, selectedFilter]); return ( // NOTE: Input onKeyPress functionality breaks when wrapped in Sandbox component // <> - - + + { onChange={handleInputChange} onKeyPress={handleInputKeyPress} /> - - Filter - + + +
+ + + + + + + +
+
- {typedChips.length > 0 && ( + {(typedChips.length > 0 || selectedFilter) && (
- + Filter: - {typedChips.length > 0 && - typedChips.map((typedChip, index) => ( - removeTypedChip(typedChip)} - /> - ))} - setTypedChips([])}> - Clear all - + {typedChips.map((typedChip, index) => ( + removeTypedChip(typedChip)} + /> + ))} + {selectedFilter && ( + { + setSelectedFilter(null); + }} + /> + )} + {(typedChips.length > 0 || selectedFilter) && ( + { + setTypedChips([]); + setSelectedFilter(null); + }}> + Clear all + + )}
)} - + Status @@ -198,102 +246,123 @@ export const FilterDataInATable = () => { tags="angular" allowCopy={true} code={` -export class TableComponent { - typedChips: string[] = []; - inputValue = ""; - inputError = ""; - readonly errorEmpty = "Empty filter"; - readonly errorDuplicate = "Enter a unique filter"; - readonly data = [ - { - status: { type: "information", text: "In progress" }, - name: "Ivan Schmidt", - id: "7838576954", - }, - { - status: { type: "success", text: "Completed" }, - name: "Luz Lakin", - id: "8576953364", - }, - { - status: { type: "information", text: "In progress" }, - name: "Keith McGlynn", - id: "9846041345", - }, - { - status: { type: "success", text: "Completed" }, - name: "Melody Frami", - id: "7385256175", - }, - { - status: { type: "important", text: "Updated" }, - name: "Frederick Skiles", - id: "5807570418", - }, - { - status: { type: "success", text: "Completed" }, - name: "Dana Pfannerstill", - id: "5736306857", - }, - ]; - dataFiltered = this.getFilteredData(this.typedChips); + export class TablePopoverComponent { + typedChips: string[] = []; - handleInputChange(event: Event): void { - const newValue = (event.target as HTMLInputElement).value.trim(); - this.inputValue = newValue; - } + inputValue = ''; + inputError = ''; + readonly errorEmpty = 'Empty filter'; + readonly errorDuplicate = 'Enter a unique filter'; - handleInputKeyPress(event: KeyboardEvent): void { - if (event.key === "Enter") { - this.applyFilter(); - } - } + // Radio filter state + selectedFilter: string | null = null; - applyFilter() { - if (this.inputValue === "") { - this.inputError = this.errorEmpty; - return; - } - if (this.typedChips.includes(this.inputValue)) { - this.inputError = this.errorDuplicate; - return; - } - this.typedChips = [...this.typedChips, this.inputValue]; - this.inputValue = ""; - this.inputError = ""; - this.dataFiltered = this.getFilteredData(this.typedChips); - } + // Table data + popoverValues: PopoverValue[] = [ + { + status: { type: "information", text: "In progress" }, + name: "Ivan Schmidt", + id: "7838576954", + }, + { + status: { type: "success", text: "Completed" }, + name: "Luz Lakin", + id: "8576953364", + }, + { + status: { type: "information", text: "In progress" }, + name: "Keith McGlynn", + id: "9846041345", + }, + { + status: { type: "success", text: "Completed" }, + name: "Melody Frami", + id: "7385256175", + }, + { + status: { type: "important", text: "Updated" }, + name: "Frederick Skiles", + id: "5807570418", + }, + { + status: { type: "success", text: "Completed" }, + name: "Dana Pfannerstill", + id: "5736306857", + }, + ]; - removeTypedChip(chip: string) { - this.typedChips = this.typedChips.filter((c) => c !== chip); - this.dataFiltered = this.getFilteredData(this.typedChips); - this.inputError = ""; - } + get filteredData(): PopoverValue[] { + let filtered = this.popoverValues; - removeAllTypedChips() { - this.typedChips = []; - this.dataFiltered = this.getFilteredData(this.typedChips); - this.inputError = ""; - } + // Apply radio filter + if (this.selectedFilter) { + filtered = filtered.filter(item => item.status === this.selectedFilter); + } - 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; - } + // Apply chip filters (all chips must match) + if (this.typedChips.length > 0) { + filtered = filtered.filter(item => + this.typedChips.every(chip => + Object.values(item).some(val => + typeof val === 'string' && val.toLowerCase().includes(chip.toLowerCase()) + ) + ) + ); + } - 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()), - ); - } -} + return filtered; + } + + handleInputChange(event: any): void { + this.inputValue = event.target.value.trim(); + } + + handleInputKeyPress(event: KeyboardEvent): void { + if (event.key === 'Enter') { + this.applyFilter(); + } + } + + applyFilter() { + if (this.inputValue === '') { + this.inputError = this.errorEmpty; + return; + } + if (this.typedChips.includes(this.inputValue)) { + this.inputError = this.errorDuplicate; + return; + } + this.typedChips = [...this.typedChips, this.inputValue]; + this.inputValue = ''; + this.inputError = ''; + } + + removeTypedChip(chip: string) { + this.typedChips = this.typedChips.filter(c => c !== chip); + this.inputError = ''; + } + + removeAllTypedChips() { + this.typedChips = []; + this.inputError = ''; + } + + radioGroupOnChange(value: string) { + this.selectedFilter = value; + } + + clearRadioFilter() { + this.selectedFilter = null; + } + + 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 - + + + + + +
+ + + + + + + + + Remove filter + +
+
+ + Filter + +
+
+ + Filter + +
+
+ + + + Filter: + + + + + Clear all + + + + + + + Status + Text + Number + + + + + + + + Lorem ipsum + {{ u.key }} + + + + + + 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 [selectedFilter, setSelectedFilter] = useState(null); + 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 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 target = ( + + Filter + + ); - const handleInputKeyPress = (_name: string, _value: string, key: string) => { - if (key === "Enter") { - applyFilter(); - } - }; + const handleInputChange = (_name: string, value: string) => { + const newValue = value.trim(); + setInputValue(newValue); + }; - 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 handleInputKeyPress = (_name: string, _value: string, key: string) => { + if (key === "Enter") { + applyFilter(); + } + }; - const removeTypedChip = (chip: string) => { - setTypedChips(typedChips.filter((c) => c !== chip)); - setInputError(""); - }; + 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 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 removeTypedChip = (chip: string) => { + setTypedChips(typedChips.filter((c) => c !== chip)); + setInputError(""); + }; - const getFilteredData = useCallback( - (typedChips: string[]) => { - if (typedChips.length === 0) { - return data; - } - const filteredData = data.filter((item: object) => - typedChips.every((chip) => checkNested(item, chip)), - ); + 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()), + ); + }, []); - return filteredData; - }, - [checkNested, data], - ); + const handleInputChange = (_name: string, value: string) => { + setSelectedFilter(value); + }; - useEffect(() => { - setDataFiltered(getFilteredData(typedChips)); - }, [getFilteredData, typedChips]); + const getFilteredData = useCallback( + (typedChips: string[], selectedFilter: string | null) => { + let filteredData = data; + + if (typedChips.length > 0) { + filteredData = filteredData.filter((item: any) => + typedChips.every(chip => checkNested(item, chip)) + ); + } + + if (selectedFilter) { + filteredData = filteredData.filter( + (item: any) => item.status && item.status.text === selectedFilter + ); + } + + return filteredData; + }, + [checkNested, data] + ); + + useEffect(() => { + setDataFiltered(getFilteredData(typedChips, selectedFilter)); + }, [getFilteredData, typedChips, selectedFilter]); `} /> @@ -476,78 +583,96 @@ export class TableComponent { tags="react" allowCopy={true} code={` - <> - - - - - 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} - - ))} - - + {(typedChips.length > 0 || selectedFilter) && ( +
+ + Filter: + + {typedChips.map((typedChip, index) => ( + removeTypedChip(typedChip)} + /> + ))} + {selectedFilter && ( + { + setSelectedFilter(null); + }} + /> + )} + {(typedChips.length > 0 || selectedFilter) && ( + { + setTypedChips([]); + setSelectedFilter(null); + }}> + Clear all + + )} +
+ )} - {dataFiltered.length === 0 && data.length > 0 && ( - No results found - )} - + + + + Status + Name + ID Number + + + + {dataFiltered.map(item => ( + + + + + {item.name} + {item.id} + + ))} + + + + {dataFiltered.length === 0 && data.length > 0 && ( + + No results found + + )} +
`} /> @@ -560,102 +685,125 @@ export class TableComponent { tags="angular" allowCopy={true} code={` -export class TableComponent { - typedChips: string[] = []; - inputValue = ""; - inputError = ""; - readonly errorEmpty = "Empty filter"; - readonly errorDuplicate = "Enter a unique filter"; - readonly data = [ - { - 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", - }, - ]; - dataFiltered = this.getFilteredData(this.typedChips); + + export class TablePopoverComponent { + typedChips: string[] = []; - handleInputChange(detail: GoabInputOnChangeDetail): void { - const newValue = detail.value.trim(); - this.inputValue = newValue; - } + inputValue = ''; + inputError = ''; + readonly errorEmpty = 'Empty filter'; + readonly errorDuplicate = 'Enter a unique filter'; - handleInputKeyPress(detail: GoabInputOnKeyPressDetail): void { - if (detail.key === "Enter") { - this.applyFilter(); - } - } + // Radio filter state + selectedFilter: string | null = null; - applyFilter() { - if (this.inputValue === "") { - this.inputError = this.errorEmpty; - return; - } - if (this.typedChips.includes(this.inputValue)) { - this.inputError = this.errorDuplicate; - return; - } - this.typedChips = [...this.typedChips, this.inputValue]; - this.inputValue = ""; - this.inputError = ""; - this.dataFiltered = this.getFilteredData(this.typedChips); - } + // Table data + popoverValues: PopoverValue[] = [ + { + 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", + }, + ]; - removeTypedChip(chip: string) { - this.typedChips = this.typedChips.filter((c) => c !== chip); - this.dataFiltered = this.getFilteredData(this.typedChips); - this.inputError = ""; - } + get filteredData(): PopoverValue[] { + let filtered = this.popoverValues; - removeAllTypedChips() { - this.typedChips = []; - this.dataFiltered = this.getFilteredData(this.typedChips); - this.inputError = ""; - } + // Apply radio filter + if (this.selectedFilter) { + filtered = filtered.filter(item => item.status === this.selectedFilter); + } - 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; - } + // Apply chip filters (all chips must match) + if (this.typedChips.length > 0) { + filtered = filtered.filter(item => + this.typedChips.every(chip => + Object.values(item).some(val => + typeof val === 'string' && val.toLowerCase().includes(chip.toLowerCase()) + ) + ) + ); + } - 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()), - ); - } -} + return filtered; + } + + handleInputChange(detail: GoabInputOnChangeDetail): void { + const newValue = detail.value.trim(); + this.inputValue = newValue; + } + + handleInputKeyPress(detail: GoabInputOnKeyPressDetail): void { + if (detail.key === "Enter") { + this.applyFilter(); + } + } + + applyFilter() { + if (this.inputValue === '') { + this.inputError = this.errorEmpty; + return; + } + if (this.typedChips.includes(this.inputValue)) { + this.inputError = this.errorDuplicate; + return; + } + this.typedChips = [...this.typedChips, this.inputValue]; + this.inputValue = ''; + this.inputError = ''; + } + + removeTypedChip(chip: string) { + this.typedChips = this.typedChips.filter(c => c !== chip); + this.inputError = ''; + } + + removeAllTypedChips() { + this.typedChips = []; + this.inputError = ''; + } + + radioGroupOnChange(event: GoabRadioGroupOnChangeDetail) { + this.selectedFilter = event.value; + } + + clearRadioFilter() { + this.selectedFilter = null; + } + + 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 - + + + + + +
+ + + + + + + +
+
+ + Filter + +
+
+ + Filter + +
+
+ + + + Filter: + + + + + Clear all + + + + + + + Status + Text + Number + + + + + + + + Lorem ipsum + {{ u.key }} + + + + + + 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 [selectedFilter, setSelectedFilter] = useState(null); + 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 target = ( + + Filter + + ); - const handleInputKeyPress = (detail: GoabInputOnKeyPressDetail) => { - if (detail.key === "Enter") { - applyFilter(); - } - }; + const handleInputChange = (detail: GoabInputOnChangeDetail) => { + const newValue = detail.value.trim(); + setInputValue(newValue); + }; - 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 handleInputKeyPress = (detail: GoabInputOnKeyPressDetail) => { + if (detail.key === "Enter") { + applyFilter(); + } + }; - const removeTypedChip = (chip: string) => { - setTypedChips(typedChips.filter(c => c !== chip)); - setInputError(""); - }; + 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 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 removeTypedChip = (chip: string) => { + setTypedChips(typedChips.filter(c => c !== chip)); + setInputError(""); + }; - const getFilteredData = useCallback( - (typedChips: string[]) => { - if (typedChips.length === 0) { - return data; - } - const filteredData = data.filter((item: object) => - typedChips.every(chip => checkNested(item, chip)) - ); + 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()) + ); + }, []); - return filteredData; - }, - [checkNested, data] - ); + function radioGroupOnChange(event: GoabRadioGroupOnChangeDetail) { + setSelectedFilter(event.value); + } - useEffect(() => { - setDataFiltered(getFilteredData(typedChips)); - }, [getFilteredData, typedChips]); + const getFilteredData = useCallback( + (typedChips: string[], selectedFilter: string | null) => { + let filteredData = data; + + if (typedChips.length > 0) { + filteredData = filteredData.filter((item: any) => + typedChips.every(chip => checkNested(item, chip)) + ); + } + + if (selectedFilter) { + filteredData = filteredData.filter( + (item: any) => item.status && item.status.text === selectedFilter + ); + } + + return filteredData; + }, + [checkNested, data] + ); + + + useEffect(() => { + setDataFiltered(getFilteredData(typedChips, selectedFilter)); + }, [getFilteredData, typedChips, selectedFilter]); `} /> @@ -838,72 +1022,96 @@ export class TableComponent { tags="react" allowCopy={true} code={` - <> - - - - - 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} - - ))} - - + {(typedChips.length > 0 || selectedFilter) && ( +
+ + Filter: + + {typedChips.map((typedChip, index) => ( + removeTypedChip(typedChip)} + /> + ))} + {selectedFilter && ( + { + setSelectedFilter(null); + }} + /> + )} + {(typedChips.length > 0 || selectedFilter) && ( + { + setTypedChips([]); + setSelectedFilter(null); + }}> + Clear all + + )} +
+ )} - {dataFiltered.length === 0 && data.length > 0 && ( - No results found - )} - + + + + Status + Name + ID Number + + + + {dataFiltered.map(item => ( + + + + + {item.name} + {item.id} + + ))} + + + + {dataFiltered.length === 0 && data.length > 0 && ( + + No results found + + )} +
`} /> diff --git a/src/examples/tables/TablesExamples.tsx b/src/examples/tables/TablesExamples.tsx index 6bdf55899..3c888b015 100644 --- a/src/examples/tables/TablesExamples.tsx +++ b/src/examples/tables/TablesExamples.tsx @@ -1,16 +1,13 @@ import SortDataInATable from "@examples/sort-data-in-a-table.tsx"; import DisplayNumbersInATableSoTheyCanBeScannedEasily from "@examples/display-numbers-in-a-table-so-they-can-be-scanned-easily.tsx"; +import ZebraStripesInATable from "@examples/zebra-stripes-in-a-table.tsx"; import FilterDataInATable from "@examples/filter-data-in-a-table.tsx"; import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; +import './tables-page-examples.css'; export const TablesExamples = () => { return ( <> - - - { + + + + + + + + ); }; diff --git a/src/examples/tables/tables-page-examples.css b/src/examples/tables/tables-page-examples.css new file mode 100644 index 000000000..3113e8202 --- /dev/null +++ b/src/examples/tables/tables-page-examples.css @@ -0,0 +1,3 @@ +.goa-table-zebra-stripes > tr:nth-child(even) { + background-color: #F8F8F8; +} diff --git a/src/examples/zebra-stripes-in-a-table.tsx b/src/examples/zebra-stripes-in-a-table.tsx new file mode 100644 index 000000000..2b5c2d9f4 --- /dev/null +++ b/src/examples/zebra-stripes-in-a-table.tsx @@ -0,0 +1,88 @@ +import { + GoabButton, + GoabBadge, + GoabTable +} from "@abgov/react-components"; +import type { + GoabBadgeType, +} from "@abgov/ui-components-common"; +import { Sandbox } from "@components/sandbox"; +import { CodeSnippet } from "@components/code-snippet/CodeSnippet.tsx"; + +export const ZebraStripesInATable = () => { + const filteredData = [ + { + status: { type: "information" as GoabBadgeType, text: "In progress" }, + name: "Ivan Schmidt", + id: "76954", + }, + { + status: { type: "success" as GoabBadgeType, text: "Completed" }, + name: "Luz Lakin", + id: "53364", + }, + { + status: { type: "information" as GoabBadgeType, text: "In progress" }, + name: "Keith McGlynn", + id: "41345", + }, + { + status: { type: "success" as GoabBadgeType, text: "Completed" }, + name: "Melody Frami", + id: "56175", + }, + { + status: { type: "important" as GoabBadgeType, text: "Updated" }, + name: "Frederick Skiles", + id: "70418", + }, + { + status: { type: "success" as GoabBadgeType, text: "Completed" }, + name: "Dana Pfannerstill", + id: "06857", + }, + ]; + + return ( + <> + + {/*CSS Code Snippet*/} + tr:nth-child(even) { + background-color: #F8F8F8; + } + `} + /> + + + + Status + Assigned to + Number + Actions + + + + {filteredData.map((item) => ( + + + + + {item.name} + {item.id} + + Action + + + ))} + + + + + ); +}; + +export default ZebraStripesInATable; diff --git a/src/routes/components/Table.tsx b/src/routes/components/Table.tsx index e54a2960b..04b9d007d 100644 --- a/src/routes/components/Table.tsx +++ b/src/routes/components/Table.tsx @@ -199,7 +199,7 @@ export default function TablePage() { heading={ <> Examples - + }> From 9cfc641b7498ff926f362569589a63378becdd29 Mon Sep 17 00:00:00 2001 From: Miguel Date: Wed, 24 Sep 2025 14:50:56 -0600 Subject: [PATCH 2/2] feat(#2120): table - modified table examples --- src/examples/tables/TablesExamples.tsx | 8 -------- src/examples/tables/tables-page-examples.css | 2 +- src/examples/zebra-stripes-in-a-table.tsx | 2 +- src/routes/components/Table.tsx | 2 +- 4 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/examples/tables/TablesExamples.tsx b/src/examples/tables/TablesExamples.tsx index 3c888b015..50bbb70f9 100644 --- a/src/examples/tables/TablesExamples.tsx +++ b/src/examples/tables/TablesExamples.tsx @@ -1,5 +1,4 @@ import SortDataInATable from "@examples/sort-data-in-a-table.tsx"; -import DisplayNumbersInATableSoTheyCanBeScannedEasily from "@examples/display-numbers-in-a-table-so-they-can-be-scanned-easily.tsx"; import ZebraStripesInATable from "@examples/zebra-stripes-in-a-table.tsx"; import FilterDataInATable from "@examples/filter-data-in-a-table.tsx"; import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; @@ -8,13 +7,6 @@ import './tables-page-examples.css'; export const TablesExamples = () => { return ( <> - - - - - diff --git a/src/examples/tables/tables-page-examples.css b/src/examples/tables/tables-page-examples.css index 3113e8202..d34b17df1 100644 --- a/src/examples/tables/tables-page-examples.css +++ b/src/examples/tables/tables-page-examples.css @@ -1,3 +1,3 @@ .goa-table-zebra-stripes > tr:nth-child(even) { - background-color: #F8F8F8; + background-color: var(--goa-color-greyscale-50); } diff --git a/src/examples/zebra-stripes-in-a-table.tsx b/src/examples/zebra-stripes-in-a-table.tsx index 2b5c2d9f4..806c602f7 100644 --- a/src/examples/zebra-stripes-in-a-table.tsx +++ b/src/examples/zebra-stripes-in-a-table.tsx @@ -52,7 +52,7 @@ export const ZebraStripesInATable = () => { allowCopy={true} code={` .goa-table-zebra-stripes > tr:nth-child(even) { - background-color: #F8F8F8; + background-color: var(--goa-color-greyscale-50); } `} /> diff --git a/src/routes/components/Table.tsx b/src/routes/components/Table.tsx index 04b9d007d..e54a2960b 100644 --- a/src/routes/components/Table.tsx +++ b/src/routes/components/Table.tsx @@ -199,7 +199,7 @@ export default function TablePage() { heading={ <> Examples - + }>