diff --git a/packages/ncids-js/package.json b/packages/ncids-js/package.json index 299053a75..3762e2138 100644 --- a/packages/ncids-js/package.json +++ b/packages/ncids-js/package.json @@ -87,6 +87,16 @@ "import": "./lib/esm/components/usa-modal/index.js", "types": "./lib/esm/components/usa-modal/index.d.js", "require": "./lib/cjs/components/usa-modal/index.js" + }, + "./usa-table": { + "import": "./lib/esm/components/usa-table/index.js", + "types": "./lib/esm/components/usa-table/index.d.js", + "require": "./lib/cjs/components/usa-table/index.js" + }, + "./usa-table/auto-init": { + "import": "./lib/esm/components/usa-table/index.js", + "types": "./lib/esm/components/usa-table/index.d.js", + "require": "./lib/cjs/components/usa-table/index.js" } }, "scripts": { diff --git a/packages/ncids-js/src/components/usa-table/__tests__/usa-table-sortable.dom.ts b/packages/ncids-js/src/components/usa-table/__tests__/usa-table-sortable.dom.ts new file mode 100644 index 000000000..bddcf6e7e --- /dev/null +++ b/packages/ncids-js/src/components/usa-table/__tests__/usa-table-sortable.dom.ts @@ -0,0 +1,474 @@ +export const usaTableSortableDOM = (): HTMLElement => { + const div = document.createElement('div'); + + div.innerHTML = ` +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Recently admitted US states (sortable table example)
+ Name + + Order admitted to union + + Date of ratification vote + + Date admitted to union + + Percent of voters in favor of ratification + + Votes cast in favor of ratification + + Estimated population at time of admission +
Hawaii50thJun. 27, 1959Aug. 21, 195994.3%132,773632,772
Alaska49thApr. 24, 1956Jan. 3, 195968.1%17,477226,167
Arizona48thFeb. 9, 1911Feb. 14, 191278.7%12,187204,354February
New Mexico47thNov. 5, 1911Jan. 6, 191270.3%31,742327,301
Oklahoma46thSep. 17, 1907Nov. 16, 190771.2%180,3331,657,155
Utah45thNov. 5, 1895Jan. 4, 189680.5%31,305210,779
+
+ +
+

Data for illustration purposes only.

+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sortable striped table with various content types
+ Alphabetical + + Month + + Percent + + Count + + Rank (Ordinal) + + Rank (Cardinal) + + Unix Timestamp +
TangoMarch20.6%23,612Third3rdMarch 27, 2012
FoxtrotApril2.6%-32First1stApril 9, 2021
HiloJanuary-3.6%0.002Second2ndJanuary 20, 2021
BravoDecember-300.6%0Fourth4thDecember 16, 2020
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sortable borderless table with various content types
+ Alphabetical + + Month + + Percent + + Count + + Rank (Ordinal) + + Rank (Cardinal) + + Unix Timestamp +
TangoMarch20.6%23,612Third3rdMarch 27, 2012
FoxtrotApril2.6%-32First1stApril 9, 2021
HiloJanuary-3.6%0.002Second2ndJanuary 20, 2021
BravoDecember-300.6%0Fourth4thDecember 16, 2020
+
+
`; + return div; +}; + +export const usaTableSortableSortedDOM = (): HTMLElement => { + const div = document.createElement('div'); + + div.innerHTML = ` +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Recently admitted US states (sortable table example)
+ Name + + Order admitted to union + + Date of ratification vote + + Date admitted to union + + Percent of voters in favor of ratification + + Votes cast in favor of ratification + + Estimated population at time of admission +
Hawaii50thJun. 27, 1959Aug. 21, 195994.3%132,773632,772
Alaska49thApr. 24, 1956Jan. 3, 195968.1%17,477226,167
Arizona48thFeb. 9, 1911Feb. 14, 191278.7%12,187204,354
New Mexico47thNov. 5, 1911Jan. 6, 191270.3%31,742327,301
Oklahoma46thSep. 17, 1907Nov. 16, 190771.2%180,3331,657,155
Utah45thNov. 5, 1895Jan. 4, 189680.5%31,305210,779
+
+ +
+

Data for illustration purposes only.

+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sortable striped table with various content types
+ Alphabetical + + Month + + Percent + + Count + + Rank (Ordinal) + + Rank (Cardinal) + + Unix Timestamp +
TangoMarch20.6%23,612Third3rdMarch 27, 2012
FoxtrotApril2.6%-32First1stApril 9, 2021
HiloJanuary-3.6%0.002Second2ndJanuary 20, 2021
BravoDecember-300.6%0Fourth4thDecember 16, 2020
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sortable borderless table with various content types
+ Alphabetical + + Month + + Percent + + Count + + Rank (Ordinal) + + Rank (Cardinal) + + Unix Timestamp +
TangoMarch20.6%23,612Third3rdMarch 27, 2012
FoxtrotApril2.6%-32First1stApril 9, 2021
HiloJanuary-3.6%0.002Second2ndJanuary 20, 2021
BravoDecember-300.6%0Fourth4thDecember 16, 2020
+
+
`; + return div; +}; diff --git a/packages/ncids-js/src/components/usa-table/__tests__/usa-table-sortable.test.ts b/packages/ncids-js/src/components/usa-table/__tests__/usa-table-sortable.test.ts new file mode 100644 index 000000000..31489dc6b --- /dev/null +++ b/packages/ncids-js/src/components/usa-table/__tests__/usa-table-sortable.test.ts @@ -0,0 +1,156 @@ +import '@testing-library/jest-dom'; +import '@testing-library/jest-dom/extend-expect'; +import userEvent from '@testing-library/user-event'; + +import { SortableTableOptions } from '../usa-table-sortable-options'; +import { + usaTableSortableDOM, + usaTableSortableSortedDOM, +} from './usa-table-sortable.dom'; +import { USATableSortable } from '../usa-table-sortable.component'; + +describe('USATable', () => { + let container: HTMLElement; + let sortableTable: USATableSortable; + const options: SortableTableOptions = { + sortable: true, + }; + + beforeEach(() => { + container = usaTableSortableDOM(); + document.body.appendChild(container); + sortableTable = USATableSortable.create(container, options); + }); + + afterEach(() => { + document.getElementsByTagName('body')[0].innerHTML = ''; + }); + + it('creates a usa table with sortable columns', () => { + expect(sortableTable).toBeDefined(); + const headerRow = container.querySelector('thead') as HTMLElement; + const headings = headerRow.querySelectorAll('th'); + expect(headings[0]).toHaveAttribute('data-sortable', ''); + }); + + it('creates a usa table with sortable columns when there is a pre-sorted column', () => { + document.body.innerHTML = ''; + container = usaTableSortableSortedDOM(); + document.body.appendChild(container); + const sortedTable = USATableSortable.create(container, options); + expect(sortedTable).toBeDefined(); + const headerRow = container.querySelector('thead') as HTMLElement; + const headings = headerRow.querySelectorAll('th'); + expect(headings[0]).toHaveAttribute('data-sortable', ''); + }); + + it('creates a usa table with sortable columns when using createAll()', () => { + document.body.innerHTML = ''; + container = usaTableSortableDOM(); + document.body.appendChild(container); + USATableSortable.createAll(); + const headerRow = container.querySelector('thead') as HTMLElement; + const headings = headerRow.querySelectorAll('th'); + expect(headings[0]).toHaveAttribute('data-sortable', ''); + }); + + it('creates a usa table with sortable columns when utilizing the autoInit function', () => { + document.body.innerHTML = ''; + container = usaTableSortableDOM(); + document.body.appendChild(container); + USATableSortable.autoInit(); + // Simulate DOMContentLoaded event + document.dispatchEvent(new Event('DOMContentLoaded')); + const headerRow = container.querySelector('thead') as HTMLElement; + const headings = headerRow.querySelectorAll('th'); + expect(headings[0]).toHaveAttribute('data-sortable', ''); + }); + + it('sorts a column when the header is clicked', async () => { + const user = userEvent.setup(); + const firstRowFirstCell = container.querySelector( + 'tbody tr:first-child th' + ); + expect(firstRowFirstCell?.textContent).toBe('Hawaii'); + + const nameHeading = container.querySelector( + 'thead th:first-child' + ) as HTMLElement; + const sortHeaderButton = nameHeading.querySelector('button') as HTMLElement; + + await user.click(sortHeaderButton); + const newFirstRowFirstCell = container.querySelector( + 'tbody tr:first-child th' + ); + expect(newFirstRowFirstCell?.textContent).toBe('Alaska'); + }); + + it('sorts a column with numbers correctly', async () => { + const user = userEvent.setup(); + const orderHeading = container.querySelectorAll( + 'thead th' + )[1] as HTMLElement; + const sortHeaderButton = orderHeading.querySelector( + 'button' + ) as HTMLElement; + + await user.click(sortHeaderButton); + const firstRowOrderCell = container.querySelector( + 'tbody tr:first-child td:nth-child(2)' + ); + expect(firstRowOrderCell?.textContent).toBe('45th'); + }); + + it('sorts a column with dates correctly', async () => { + const user = userEvent.setup(); + const orderHeading = container.querySelectorAll( + 'thead th' + )[2] as HTMLElement; + const sortHeaderButton = orderHeading.querySelector( + 'button' + ) as HTMLElement; + + await user.click(sortHeaderButton); + const firstRowOrderCell = container.querySelector( + 'tbody tr:first-child td:nth-child(3)' + ); + expect(firstRowOrderCell?.textContent).toBe('Nov. 5, 1895'); + }); + + it('toggles sort direction when the header is clicked multiple times', async () => { + const user = userEvent.setup(); + const nameHeading = container.querySelector( + 'thead th:first-child' + ) as HTMLElement; + const sortHeaderButton = nameHeading.querySelector('button') as HTMLElement; + + // First click - ascending + await user.click(sortHeaderButton); + let firstRowFirstCell = container.querySelector('tbody tr:first-child th'); + expect(firstRowFirstCell?.textContent).toBe('Alaska'); + + // Second click - descending + await user.click(sortHeaderButton); + firstRowFirstCell = container.querySelector('tbody tr:first-child th'); + expect(firstRowFirstCell?.textContent).toBe('Utah'); + }); + + it('sets aria-sort attribute correctly on header', async () => { + const user = userEvent.setup(); + const nameHeading = container.querySelector( + 'thead th:first-child' + ) as HTMLElement; + const sortHeaderButton = nameHeading.querySelector('button') as HTMLElement; + + // Initial state + expect(nameHeading).not.toHaveAttribute('aria-sort'); + + // First click - ascending + await user.click(sortHeaderButton); + expect(nameHeading).toHaveAttribute('aria-sort', 'ascending'); + + // Second click - descending + await user.click(sortHeaderButton); + expect(nameHeading).toHaveAttribute('aria-sort', 'descending'); + }); +}); diff --git a/packages/ncids-js/src/components/usa-table/auto-init.ts b/packages/ncids-js/src/components/usa-table/auto-init.ts new file mode 100644 index 000000000..8a60e7a10 --- /dev/null +++ b/packages/ncids-js/src/components/usa-table/auto-init.ts @@ -0,0 +1,7 @@ +import { USATableSortable } from './usa-table-sortable.component'; + +/* + * Auto initialize usa-accordion when dom is ready. + * @packageDocumentation + */ +USATableSortable.autoInit(); diff --git a/packages/ncids-js/src/components/usa-table/index.ts b/packages/ncids-js/src/components/usa-table/index.ts new file mode 100644 index 000000000..74658b055 --- /dev/null +++ b/packages/ncids-js/src/components/usa-table/index.ts @@ -0,0 +1,4 @@ +/** + * @packageDocumentation + */ +export { USATableSortable } from './usa-table-sortable.component'; diff --git a/packages/ncids-js/src/components/usa-table/usa-table-sortable-options.ts b/packages/ncids-js/src/components/usa-table/usa-table-sortable-options.ts new file mode 100644 index 000000000..0e096372b --- /dev/null +++ b/packages/ncids-js/src/components/usa-table/usa-table-sortable-options.ts @@ -0,0 +1,8 @@ +/** + * USA Table Options + * Options used for initialization of the sortable usa-table + */ +export type SortableTableOptions = { + /** Determines if the table is sortable */ + sortable: boolean; +}; diff --git a/packages/ncids-js/src/components/usa-table/usa-table-sortable.component.ts b/packages/ncids-js/src/components/usa-table/usa-table-sortable.component.ts new file mode 100644 index 000000000..7aaffbe84 --- /dev/null +++ b/packages/ncids-js/src/components/usa-table/usa-table-sortable.component.ts @@ -0,0 +1,311 @@ +import { SortableTableOptions } from './usa-table-sortable-options'; +/** + * USA (Sortable) Table + * Component for creating sortable tables + * This component should eventually come from the NCIDS + * For now, we're exporting it as a class here to mimick how it + * would come from the NCIDS + */ +export class USATableSortable { + /** The Table Element */ + protected tableElement: HTMLElement; + /** Optional settings for the component */ + protected options: SortableTableOptions; + /** Default options settings */ + private static optionDefaults: SortableTableOptions = { + sortable: false, + }; + + /** Callback for handling table heading button click */ + private tableSortToggleClickEventListener: EventListener = (event: Event) => + this.handleTableToggleClick(event); + + /** + * Sets component properties and initializes component. + * + * @param htmlElement container of content being created as a sortable table + * @param options Table options used for component creation + */ + protected constructor( + htmlElement: HTMLElement, + options: Partial + ) { + this.tableElement = htmlElement; + this.options = { + ...USATableSortable.optionDefaults, + ...options, + }; + + this.initialize(); + } + + /** + * Instantiates this component of the given element. + * + * @param element Element to initialize. + * @param options AccordionOptions for initialization (allow multiple sections, open sections on initialization) + */ + public static create( + element: HTMLElement, + options: SortableTableOptions + ): USATableSortable { + return new this(element, options); + } + + /** + * Initializes the sortable table based on DOM + */ + private initialize(): void { + // Determine if table is sortable based on options and + // add the appropriate class + if (this.options.sortable) { + // The table head element (the row with the headings) + const tableHead = this.tableElement.querySelector('thead') as HTMLElement; + // All of the table's headings + const tableHeadings = Array.from(tableHead.querySelectorAll('th')); + // Make each heading sortable + tableHeadings.forEach((heading) => { + // Only make sortable if not marked with data-fixed + if (!heading.hasAttribute('data-fixed')) { + heading.setAttribute('data-sortable', ''); + this.createHeaderButton(heading as HTMLElement); + } + // If the heading has aria-sort already set, + // sort by that column on initialization + // (aria-sort="descending" can be set in the HTML + // to sort descending on init) Defaults to ascending sort + const isAscending = + !heading.getAttribute('aria-sort') || + heading.getAttribute('aria-sort') === 'ascending'; + if (heading.hasAttribute('aria-sort')) { + this.sortTable(heading as HTMLElement, isAscending); + } + }); + } + } + + /** + * Creates the button element in the heading cell for sorting + * With the SVGs used by usa-table-sortable in USWDS + * @param header the heading cell of the column being sorted + */ + private createHeaderButton = (header: HTMLElement) => { + const buttonEl = document.createElement('button'); + buttonEl.setAttribute('tabindex', '0'); + buttonEl.classList.add('usa-table__header__button'); + // ICON_SOURCE + buttonEl.innerHTML = ` + + + + + + + + + + + + `; + buttonEl.addEventListener('click', this.tableSortToggleClickEventListener); + header.appendChild(buttonEl); + }; + + /** + * Fuction to update aria-sort attribute on all headings in the table + * when a heading is clicked to sort + * @param tableHeading the heading cell of the column being sorted + * @param isAscending whether the sorting is ascending or descending + */ + private updateAllHeadingSortAttributes = ( + tableHeading: HTMLElement, + isAscending: boolean + ) => { + tableHeading.setAttribute( + 'aria-sort', + isAscending ? 'ascending' : 'descending' + ); + + const table = tableHeading.closest('table') as HTMLElement; + const tableHeadings = Array.from(table.querySelectorAll('th')); + tableHeadings.forEach((th) => { + if (th !== tableHeading) { + th.removeAttribute('aria-sort'); + } + }); + }; + + /** + * Function to sort the table row based on the content + * @param row the row being sorted + * @param columnIndex when column is being sorted relative to other columns + * @param isAscending whether the sorting is ascending or descending + */ + private sortTableByColumn = ( + row: HTMLElement, + columnIndex: number, + isAscending: boolean + ) => { + // Get the table and tbody elements for DOM manipulation + const table = row.closest('table') as HTMLTableElement; + const tbody = table.querySelector('tbody') as HTMLTableSectionElement; + + // Create an array from the rows for sorting + const rowsArray = Array.from(tbody.rows); + + // Sort the rows based on the content of the specified column + // The sorting logic checks for numbers, then dates, then strings + rowsArray.sort((a, b) => { + // Get the cell elements to check for data-sortable-type attribute + const aCellElement = a.cells[columnIndex]; + const bCellElement = b.cells[columnIndex]; + + // Get the header cell for the column being sorted + // to check for data-sortable-type attribute + const sortedColumnHeader = + table.querySelector('thead')?.rows[0]?.cells[columnIndex]; + + // Get the text content of the cells for sorting + const aCell = aCellElement?.textContent?.trim() ?? ''; + const bCell = bCellElement?.textContent?.trim() ?? ''; + + // Check if header has data-sortable-type="date" attribute + if ( + sortedColumnHeader?.hasAttribute('data-sortable-type') && + sortedColumnHeader.getAttribute('data-sortable-type') === 'date' + ) { + // Try parsing as dates + const aDate = new Date(aCell).getTime(); + const bDate = new Date(bCell).getTime(); + + // Compare timestamps if both cells were valid dates + if (!isNaN(aDate) && !isNaN(bDate)) { + return isAscending ? aDate - bDate : bDate - aDate; + } + } + + // Check if the cells are numbers by removing '$' and ',' characters + // and trying to parse as float + const isNumber = (cellValue: string) => { + const removeCurrency = cellValue.replace(/[$,]/g, ''); + return !isNaN(parseFloat(removeCurrency)); + }; + + // If the cell values are numbers, compare as numbers + // Strip all non-numeric characters for comparison + if (isNumber(aCell) && isNumber(aCell)) { + const aNum = parseFloat(aCell.replace(/[^0-9.-]/g, '')); + const bNum = parseFloat(bCell.replace(/[^0-9.-]/g, '')); + return isAscending ? aNum - bNum : bNum - aNum; + } + + // Fallback to string comparison + return isAscending + ? aCell.localeCompare(bCell) + : bCell.localeCompare(aCell); + }); + + // Re-append rows in sorted order + for (const sortedRow of rowsArray) { + tbody.appendChild(sortedRow); + + // Remove data-sort-active attribute from all cells in the row + // to clear previous sort indicators + sortedRow + .querySelectorAll('td, th') + .forEach((td) => td.removeAttribute('data-sort-active')); + + // Add attribute to the cell in the sorted column to indicate active sort + // for styling purposes + sortedRow.children[columnIndex].setAttribute('data-sort-active', ''); + } + }; + + /** + * Get the column index of the heading being sorted + * and call the function to sort the rows based on that column + * @param heading the heading cell of the column being sorted + * @param isAscending whether the sorting is ascending or descending + */ + private sortTable = (heading: HTMLElement, isAscending: boolean) => { + // Need to cast as HTMLTableCellElement to get its index among the other headings + const tableHeading = heading as HTMLTableCellElement; + // Get the table so we can find all the headings + const table = tableHeading.closest('table') as HTMLElement; + // Get all the headings in the table and find the index of this heading + // among them + const allHeadings = Array.from(table.querySelectorAll('th')); + const thisHeadingIndex = allHeadings.indexOf(tableHeading); + + // Sort the row associated with this heading + this.sortTableByColumn(heading, thisHeadingIndex, isAscending); + + // Update the aria-sort attribute on all headings + this.updateAllHeadingSortAttributes(heading, isAscending); + }; + + /** + * Handles click on header sort button in table header + * @param e Event passed on from click + */ + private handleTableToggleClick(e: Event): void { + const heading = (e.currentTarget as HTMLElement).closest( + 'th' + ) as HTMLElement; + + // Determine if currently ascending or descending + // to toggle the sort direction + const isAscending = heading.getAttribute('aria-sort') === 'ascending'; + + // If no aria-sort attribute (not sorted), default to ascending sort + // Otherwise, toggle the sort direction + if (!heading.hasAttribute('aria-sort')) { + this.sortTable(heading, true); + } else { + this.sortTable(heading, !isAscending); + } + } + + /** + * Auto initializes sortable table component with default sources. + * @internal + */ + public static autoInit(): void { + document.addEventListener('DOMContentLoaded', () => { + const tables = Array.from(document.getElementsByClassName('usa-table')); + + tables.forEach((element) => { + const htmlElement = element as HTMLElement; + // Check if Table is Sortable + const isSortable = htmlElement.hasAttribute('data-sortable'); + // Return table with default options + // set whether table is sortable + return this.create(htmlElement, { + ...this.optionDefaults, + sortable: isSortable, + }); + }); + }); + } + + /** + * Public function for creating sortable table component with default sources from HTML. + * @return array of initialized sortable tables + */ + public static createAll(): USATableSortable[] { + const tables = Array.from(document.querySelectorAll('.usa-table')); + + const instances = tables.map((element) => { + const htmlElement = element as HTMLElement; + // Check if Table is Sortable + const isSortable = htmlElement.hasAttribute('data-sortable'); + // Return table with default options + // set whether table is sortable + return this.create(htmlElement, { + ...this.optionDefaults, + sortable: isSortable, + }); + }); + return instances; + } +} diff --git a/packages/ncids-js/src/index.ts b/packages/ncids-js/src/index.ts index d45763b4d..198092bb8 100644 --- a/packages/ncids-js/src/index.ts +++ b/packages/ncids-js/src/index.ts @@ -5,3 +5,4 @@ export * from 'src/components/usa-combo-box'; export * from 'src/components/usa-footer'; export * from 'src/components/usa-modal'; export * from 'src/components/usa-site-alert'; +export * from 'src/components/usa-table'; diff --git a/testing/ncids-css-testing/src/stories/components/usa-table/src/index.scss b/testing/ncids-css-testing/src/stories/components/usa-table/src/index.scss new file mode 100644 index 000000000..271469767 --- /dev/null +++ b/testing/ncids-css-testing/src/stories/components/usa-table/src/index.scss @@ -0,0 +1,3 @@ +@use "styles/ncids"; +@forward "uswds-utilities"; +@forward "usa-table"; diff --git a/testing/ncids-css-testing/src/stories/components/usa-table/src/usa-table--ncids-sortable/usa-table--sortable.twig b/testing/ncids-css-testing/src/stories/components/usa-table/src/usa-table--ncids-sortable/usa-table--sortable.twig new file mode 100644 index 000000000..11d72b33d --- /dev/null +++ b/testing/ncids-css-testing/src/stories/components/usa-table/src/usa-table--ncids-sortable/usa-table--sortable.twig @@ -0,0 +1,217 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Recently admitted US states (sortable table example)
+ Name + + Order admitted to union + + Date of ratification vote + + Date admitted to union + + Percent of voters in favor of ratification + + Votes cast in favor of ratification + + Estimated population at time of admission +
Hawaii50thJun. 27, 1959Aug. 21, 195994.3%132,773632,772
Alaska49thApr. 24, 1956Jan. 3, 195968.1%17,477226,167
Arizona48thFeb. 9, 1911Feb. 14, 191278.7%12,187204,354
New Mexico47thNov. 5, 1911Jan. 6, 191270.3%31,742327,301
Oklahoma46thSep. 17, 1907Nov. 16, 190771.2%180,3331,657,155
Utah45thNov. 5, 1895Jan. 4, 189680.5%31,305210,779
+
+ +
+

Data for illustration purposes only.

+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sortable striped table with various content types, shows unsortable columns
+ Alphabetical + + Percent + + Count + + Rank (Ordinal) + + Rank (Cardinal) + + Unix Timestamp +
Tango20.6%23,612Third3rdMarch 27, 2012
Foxtrot2.6%-32First1stApril 9, 2021
Hilo-3.6%0.002Second2ndJanuary 20, 2021
Bravo-300.6%0Fourth4thDecember 16, 2020
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sortable borderless table with various content types, pre-sorted on Count
+ Alphabetical + + Percent + + Count + + Rank (Ordinal) + + Rank (Cardinal) + + Unix Timestamp +
Tango20.6%23,612Third3rdMarch 27, 2012
Foxtrot2.6%-32First1stApril 9, 2021
Hilo-3.6%0.002Second2ndJanuary 20, 2021
Bravo-300.6%0Fourth4thDecember 16, 2020
+
+
diff --git a/testing/ncids-css-testing/src/stories/components/usa-table/src/usa-table.ncids-sortable.stories.jsx b/testing/ncids-css-testing/src/stories/components/usa-table/src/usa-table.ncids-sortable.stories.jsx new file mode 100644 index 000000000..09304b6ff --- /dev/null +++ b/testing/ncids-css-testing/src/stories/components/usa-table/src/usa-table.ncids-sortable.stories.jsx @@ -0,0 +1,15 @@ +import Component from './usa-table--ncids-sortable/usa-table--sortable.twig'; +import css from './index.scss?inline'; + +import { USATableSortable } from '@nciocpl/ncids-js/usa-table'; + +export default { + title: 'Components/Table', + component: Component, + parameters: { + ncidsInitJs: () => USATableSortable.createAll(), + css, + }, +}; + +export const NcidsSortable = {}; diff --git a/testing/ncids-css-testing/src/stories/components/usa-table/src/usa-table.twig b/testing/ncids-css-testing/src/stories/components/usa-table/src/usa-table.twig new file mode 100755 index 000000000..4f07741f9 --- /dev/null +++ b/testing/ncids-css-testing/src/stories/components/usa-table/src/usa-table.twig @@ -0,0 +1,41 @@ +{% if scrollable %} +
+{% endif %} + + + {% if caption %} + + {% endif %} + + {% if scrollable %} + + + + {% endif %} + + + + {% for th in thead %} + + {% endfor %} + + + + {% for row in tbody %} + + {% for cell in row %} + {% if not cell.scope %} + + {% else %} + + {% endif %} + {% endfor %} + + {% endfor %} + +
{{ caption }}
{{ th.title }}
{{ cell.title }}{{ cell.title }}
+ +{% if scrollable %} +
+

* in billions of dollars. Data for illustration purposes only.

+{% endif %}