@prairielearn/ui 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @prairielearn/ui
2
2
 
3
+ ## 1.1.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 0425922: Upgrade all JavaScript dependencies
8
+ - Updated dependencies [0425922]
9
+ - @prairielearn/preact-cjs@1.1.6
10
+
3
11
  ## 1.1.1
4
12
 
5
13
  ### Patch Changes
@@ -119,9 +119,9 @@ export function TanstackTable({ table, title, filters = DEFAULT_FILTER_MAP, rowH
119
119
  useEffect(() => {
120
120
  const selector = `[data-grid-cell-row="${focusedCell.row}"][data-grid-cell-col="${focusedCell.col}"]`;
121
121
  const cell = tableRef.current?.querySelector(selector);
122
- if (!cell) {
122
+ if (!cell)
123
123
  return;
124
- }
124
+ // eslint-disable-next-line react-you-might-not-need-an-effect/no-chain-state-updates
125
125
  cell.focus();
126
126
  }, [focusedCell]);
127
127
  const virtualRows = rowVirtualizer.getVirtualItems();
@@ -1 +1 @@
1
- {"version":3,"file":"TanstackTable.js","sourceRoot":"","sources":["../../src/components/TanstackTable.tsx"],"names":[],"mappings":"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","sourcesContent":["import { flexRender } from '@tanstack/react-table';\nimport { notUndefined, useVirtualizer } from '@tanstack/react-virtual';\nimport type { Header, Row, SortDirection, Table } from '@tanstack/table-core';\nimport clsx from 'clsx';\nimport { useEffect, useRef, useState } from 'preact/hooks';\nimport type { JSX } from 'preact/jsx-runtime';\n\nimport { ColumnManager } from './ColumnManager.js';\nimport {\n TanstackTableDownloadButton,\n type TanstackTableDownloadButtonProps,\n} from './TanstackTableDownloadButton.js';\n\nfunction SortIcon({ sortMethod }: { sortMethod: false | SortDirection }) {\n if (sortMethod === 'asc') {\n return <i class=\"bi bi-sort-up-alt\" aria-hidden=\"true\" />;\n } else if (sortMethod === 'desc') {\n return <i class=\"bi bi-sort-down\" aria-hidden=\"true\" />;\n } else {\n return <i class=\"bi bi-arrow-down-up opacity-75 text-muted\" aria-hidden=\"true\" />;\n }\n}\n\nfunction ResizeHandle<RowDataModel>({\n header,\n setColumnSizing,\n}: {\n header: Header<RowDataModel, unknown>;\n setColumnSizing: Table<RowDataModel>['setColumnSizing'];\n}) {\n const minSize = header.column.columnDef.minSize ?? 0;\n const maxSize = header.column.columnDef.maxSize ?? 0;\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {\n e.preventDefault();\n const currentSize = header.getSize();\n const increment = e.shiftKey ? 20 : 5; // Larger increment with Shift key\n const newSize =\n e.key === 'ArrowLeft'\n ? Math.max(minSize, currentSize - increment)\n : Math.min(maxSize, currentSize + increment);\n\n setColumnSizing((prevSizing) => ({\n ...prevSizing,\n [header.column.id]: newSize,\n }));\n } else if (e.key === 'Home') {\n e.preventDefault();\n header.column.resetSize();\n }\n };\n\n const columnName =\n typeof header.column.columnDef.header === 'string'\n ? header.column.columnDef.header\n : header.column.id;\n\n return (\n <div class=\"py-1 h-100\" style={{ position: 'absolute', right: 0, top: 0, width: '4px' }}>\n {/* separator role is focusable, so these jsx-a11y-x rules are false positives.\n https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/separator_role#focusable_separator\n */}\n {/* eslint-disable-next-line jsx-a11y-x/no-noninteractive-element-interactions */}\n <div\n role=\"separator\"\n aria-label={`Resize '${columnName}' column`}\n aria-valuetext={`${header.getSize()}px`}\n aria-orientation=\"vertical\"\n aria-valuemin={minSize}\n aria-valuemax={maxSize}\n aria-valuenow={header.getSize()}\n // eslint-disable-next-line jsx-a11y-x/no-noninteractive-tabindex\n tabIndex={0}\n class=\"h-100\"\n style={{\n background: header.column.getIsResizing() ? 'var(--bs-primary)' : 'var(--bs-gray-400)',\n cursor: 'col-resize',\n transition: 'background-color 0.2s',\n }}\n onMouseDown={header.getResizeHandler()}\n onTouchStart={header.getResizeHandler()}\n onKeyDown={handleKeyDown}\n />\n </div>\n );\n}\n\nconst DefaultNoResultsState = (\n <>\n <i class=\"bi bi-search display-4 mb-2\" aria-hidden=\"true\" />\n <p class=\"mb-0\">No results found matching your search criteria.</p>\n </>\n);\n\nconst DefaultEmptyState = (\n <>\n <i class=\"bi bi-eye-slash display-4 mb-2\" aria-hidden=\"true\" />\n <p class=\"mb-0\">No results found.</p>\n </>\n);\n\ninterface TanstackTableProps<RowDataModel> {\n table: Table<RowDataModel>;\n title: string;\n filters?: Record<string, (props: { header: Header<RowDataModel, unknown> }) => JSX.Element>;\n rowHeight?: number;\n noResultsState?: JSX.Element;\n emptyState?: JSX.Element;\n}\n\nconst DEFAULT_FILTER_MAP = {};\n\n/**\n * A generic component that renders a full-width, resizeable Tanstack Table.\n * @param params\n * @param params.table - The table model\n * @param params.title - The title of the table\n * @param params.filters - The filters for the table\n * @param params.rowHeight - The height of the rows in the table\n * @param params.noResultsState - The no results state for the table\n * @param params.emptyState - The empty state for the table\n */\nexport function TanstackTable<RowDataModel>({\n table,\n title,\n filters = DEFAULT_FILTER_MAP,\n rowHeight = 42,\n noResultsState = DefaultNoResultsState,\n emptyState = DefaultEmptyState,\n}: TanstackTableProps<RowDataModel>) {\n const parentRef = useRef<HTMLDivElement>(null);\n const tableRef = useRef<HTMLDivElement>(null);\n const rows = [...table.getTopRows(), ...table.getCenterRows()];\n const rowVirtualizer = useVirtualizer({\n count: rows.length,\n getScrollElement: () => parentRef.current,\n estimateSize: () => rowHeight,\n overscan: 10,\n });\n\n // Track focused cell for grid navigation\n const [focusedCell, setFocusedCell] = useState<{ row: number; col: number }>({ row: 0, col: 0 });\n\n const getVisibleCells = (row: Row<RowDataModel>) => [\n ...row.getLeftVisibleCells(),\n ...row.getCenterVisibleCells(),\n ];\n\n const handleGridKeyDown = (e: KeyboardEvent, rowIdx: number, colIdx: number) => {\n const rowLength = getVisibleCells(rows[rowIdx]).length;\n const adjacentCells: Record<KeyboardEvent['key'], { row: number; col: number }> = {\n ArrowDown: {\n row: Math.min(rows.length - 1, rowIdx + 1),\n col: colIdx,\n },\n ArrowUp: {\n row: Math.max(0, rowIdx - 1),\n col: colIdx,\n },\n ArrowRight: {\n row: rowIdx,\n col: Math.min(rowLength - 1, colIdx + 1),\n },\n ArrowLeft: {\n row: rowIdx,\n col: Math.max(0, colIdx - 1),\n },\n };\n\n const next = adjacentCells[e.key];\n\n if (!next) {\n return;\n }\n\n setFocusedCell({ row: next.row, col: next.col });\n // If we are on the leftmost column, we should allow left scrolling.\n if (colIdx === 0 && e.key === 'ArrowLeft') {\n return;\n }\n\n // If we are on the top row, we should allow up scrolling.\n if (rowIdx === 0 && e.key === 'ArrowUp') {\n return;\n }\n\n // If we are on the rightmost column, we should allow right scrolling.\n if (colIdx === rowLength - 1 && e.key === 'ArrowRight') {\n return;\n }\n\n e.preventDefault();\n };\n\n useEffect(() => {\n const selector = `[data-grid-cell-row=\"${focusedCell.row}\"][data-grid-cell-col=\"${focusedCell.col}\"]`;\n const cell = tableRef.current?.querySelector(selector) as HTMLElement | null;\n if (!cell) {\n return;\n }\n cell.focus();\n }, [focusedCell]);\n\n const virtualRows = rowVirtualizer.getVirtualItems();\n const [before, after] =\n virtualRows.length > 0\n ? [\n notUndefined(virtualRows[0]).start - rowVirtualizer.options.scrollMargin,\n rowVirtualizer.getTotalSize() - notUndefined(virtualRows.at(-1)).end,\n ]\n : [0, 0];\n const headerGroups = table.getHeaderGroups();\n const isTableResizing = headerGroups.some((headerGroup) =>\n headerGroup.headers.some((header) => header.column.getIsResizing()),\n );\n const lastColumnId = table.getAllLeafColumns()[table.getAllLeafColumns().length - 1].id;\n\n const tableRect = tableRef.current?.getBoundingClientRect();\n\n // We toggle this here instead of in the parent since this component logically manages all UI for the table.\n // eslint-disable-next-line react-you-might-not-need-an-effect/no-manage-parent\n useEffect(() => {\n document.body.classList.toggle('no-user-select', isTableResizing);\n }, [isTableResizing]);\n\n // Helper function to get aria-sort value\n const getAriaSort = (sortDirection: false | SortDirection) => {\n switch (sortDirection) {\n case 'asc':\n return 'ascending';\n case 'desc':\n return 'descending';\n default:\n return 'none';\n }\n };\n\n const displayedCount = table.getRowModel().rows.length;\n const totalCount = table.getCoreRowModel().rows.length;\n\n return (\n <div style={{ position: 'relative' }} class=\"d-flex flex-column h-100\">\n <div\n ref={parentRef}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n overflow: 'auto',\n overflowAnchor: 'none',\n }}\n >\n <div\n ref={tableRef}\n style={{\n position: 'relative',\n width: `max(${table.getTotalSize()}px, 100%)`,\n }}\n >\n <table\n class=\"table table-hover mb-0 border border-top-0\"\n style={{ tableLayout: 'fixed' }}\n aria-label={title}\n role=\"grid\"\n >\n <thead>\n {headerGroups.map((headerGroup) => (\n <tr key={headerGroup.id}>\n {headerGroup.headers.map((header, index) => {\n const isPinned = header.column.getIsPinned();\n const sortDirection = header.column.getIsSorted();\n const canSort = header.column.getCanSort();\n const canFilter = header.column.getCanFilter();\n const columnName =\n typeof header.column.columnDef.header === 'string'\n ? header.column.columnDef.header\n : header.column.id;\n\n const style: JSX.CSSProperties = {\n width:\n header.column.id === lastColumnId\n ? `max(100%, ${header.getSize()}px)`\n : header.getSize(),\n position: 'sticky',\n top: 0,\n zIndex: isPinned === 'left' ? 2 : 1,\n left: isPinned === 'left' ? header.getStart() : undefined,\n boxShadow:\n 'inset 0 calc(-1 * var(--bs-border-width)) 0 0 rgba(0, 0, 0, 1), inset 0 var(--bs-border-width) 0 0 var(--bs-border-color)',\n };\n\n return (\n <th\n key={header.id}\n class={clsx(isPinned === 'left' && 'bg-light')}\n style={style}\n aria-sort={canSort ? getAriaSort(sortDirection) : undefined}\n role=\"columnheader\"\n >\n <div class=\"d-flex align-items-center justify-content-between gap-2\">\n <button\n class=\"text-nowrap flex-grow-1 text-start\"\n style={{\n cursor: canSort ? 'pointer' : 'default',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n background: 'transparent',\n border: 'none',\n }}\n type=\"button\"\n aria-label={\n canSort\n ? `'${columnName}' column, current sort is ${getAriaSort(sortDirection)}`\n : undefined\n }\n onClick={canSort ? header.column.getToggleSortingHandler() : undefined}\n onKeyDown={\n canSort\n ? (e) => {\n const handleSort = header.column.getToggleSortingHandler();\n if (e.key === 'Enter' && handleSort) {\n e.preventDefault();\n handleSort(e);\n }\n }\n : undefined\n }\n >\n {header.isPlaceholder\n ? null\n : flexRender(header.column.columnDef.header, header.getContext())}\n {canSort && (\n <span class=\"ms-2\" aria-hidden=\"true\">\n <SortIcon sortMethod={sortDirection || false} />\n </span>\n )}\n {canSort && (\n <span class=\"visually-hidden\">\n , {getAriaSort(sortDirection)}, click to sort\n </span>\n )}\n </button>\n\n {canFilter && filters[header.column.id]?.({ header })}\n </div>\n {tableRect?.width &&\n tableRect.width > table.getTotalSize() &&\n index === headerGroup.headers.length - 1 ? null : (\n <ResizeHandle header={header} setColumnSizing={table.setColumnSizing} />\n )}\n </th>\n );\n })}\n </tr>\n ))}\n </thead>\n <tbody>\n {before > 0 && (\n <tr tabIndex={-1}>\n <td colSpan={headerGroups[0].headers.length} style={{ height: before }} />\n </tr>\n )}\n {virtualRows.map((virtualRow) => {\n const row = rows[virtualRow.index];\n const visibleCells = getVisibleCells(row);\n const rowIdx = virtualRow.index;\n\n return (\n <tr key={row.id} style={{ height: rowHeight }}>\n {visibleCells.map((cell, colIdx) => (\n <td\n key={cell.id}\n // You can tab to the most-recently focused cell.\n tabIndex={focusedCell.row === rowIdx && focusedCell.col === colIdx ? 0 : -1}\n // We store this so you can navigate around the grid.\n data-grid-cell-row={rowIdx}\n data-grid-cell-col={colIdx}\n style={{\n width:\n cell.column.id === lastColumnId\n ? `max(100%, ${cell.column.getSize()}px)`\n : cell.column.getSize(),\n position: cell.column.getIsPinned() === 'left' ? 'sticky' : undefined,\n left:\n cell.column.getIsPinned() === 'left'\n ? cell.column.getStart()\n : undefined,\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n }}\n onFocus={() => setFocusedCell({ row: rowIdx, col: colIdx })}\n onKeyDown={(e) => handleGridKeyDown(e, rowIdx, colIdx)}\n >\n {flexRender(cell.column.columnDef.cell, cell.getContext())}\n </td>\n ))}\n </tr>\n );\n })}\n {after > 0 && (\n <tr tabIndex={-1}>\n <td colSpan={headerGroups[0].headers.length} style={{ height: after }} />\n </tr>\n )}\n </tbody>\n </table>\n </div>\n </div>\n\n {table.getVisibleLeafColumns().length === 0 && (\n <div>\n <div\n class=\"d-flex flex-column justify-content-center align-items-center text-muted py-4\"\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n background: 'var(--bs-body-bg)',\n }}\n role=\"status\"\n aria-live=\"polite\"\n >\n <i class=\"bi bi-eye-slash display-4 mb-2\" aria-hidden=\"true\" />\n <p class=\"mb-0\">No columns selected. Use the View menu to show columns.</p>\n </div>\n </div>\n )}\n {displayedCount === 0 && (\n <div\n class=\"d-flex flex-column justify-content-center align-items-center text-muted py-4\"\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n background: 'var(--bs-body-bg)',\n }}\n role=\"status\"\n aria-live=\"polite\"\n >\n {totalCount > 0 ? noResultsState : emptyState}\n </div>\n )}\n </div>\n );\n}\n\n/**\n * A generic component that wraps the TanstackTable component in a card.\n * @param params\n * @param params.table - The table model\n * @param params.title - The title of the card\n * @param params.headerButtons - The buttons to display in the header\n * @param params.globalFilter - State management for the global filter\n * @param params.globalFilter.value\n * @param params.globalFilter.setValue\n * @param params.globalFilter.placeholder\n * @param params.tableOptions - Specific options for the table. See {@link TanstackTableProps} for more details.\n * @param params.downloadButtonOptions - Specific options for the download button. See {@link TanstackTableDownloadButtonProps} for more details.\n */\nexport function TanstackTableCard<RowDataModel>({\n table,\n title,\n headerButtons,\n globalFilter,\n tableOptions,\n downloadButtonOptions = null,\n}: {\n table: Table<RowDataModel>;\n title: string;\n headerButtons: JSX.Element;\n globalFilter: {\n value: string;\n setValue: (value: string) => void;\n placeholder: string;\n };\n tableOptions: Partial<Omit<TanstackTableProps<RowDataModel>, 'table'>>;\n downloadButtonOptions?: Omit<TanstackTableDownloadButtonProps<RowDataModel>, 'table'> | null;\n}) {\n const searchInputRef = useRef<HTMLInputElement>(null);\n\n // Track screen size for aria-hidden\n const mediaQuery = typeof window !== 'undefined' ? window.matchMedia('(min-width: 768px)') : null;\n const [isMediumOrLarger, setIsMediumOrLarger] = useState(false);\n\n useEffect(() => {\n // TODO: This is a workaround to avoid a hydration mismatch.\n // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect\n setIsMediumOrLarger(mediaQuery?.matches ?? true);\n }, [mediaQuery]);\n\n useEffect(() => {\n const handler = (e: MediaQueryListEvent) => setIsMediumOrLarger(e.matches);\n mediaQuery?.addEventListener('change', handler);\n return () => mediaQuery?.removeEventListener('change', handler);\n }, [mediaQuery]);\n\n // Focus the search input when Ctrl+F is pressed\n useEffect(() => {\n function onKeyDown(event: KeyboardEvent) {\n if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'f') {\n if (searchInputRef.current && searchInputRef.current !== document.activeElement) {\n searchInputRef.current.focus();\n event.preventDefault();\n }\n }\n }\n document.addEventListener('keydown', onKeyDown);\n return () => document.removeEventListener('keydown', onKeyDown);\n }, []);\n\n const displayedCount = table.getRowModel().rows.length;\n const totalCount = table.getCoreRowModel().rows.length;\n\n return (\n <div class=\"card d-flex flex-column h-100\">\n <div class=\"card-header bg-primary text-white\">\n <div class=\"d-flex align-items-center justify-content-between gap-2\">\n <div>{title}</div>\n <div class=\"d-flex gap-2\">\n {headerButtons}\n\n {downloadButtonOptions && (\n <TanstackTableDownloadButton table={table} {...downloadButtonOptions} />\n )}\n </div>\n </div>\n </div>\n <div class=\"card-body d-flex flex-column\">\n <div class=\"d-flex flex-row flex-wrap align-items-center mb-3 gap-2\">\n <div class=\"flex-grow-1 flex-lg-grow-0 col-xl-6 col-lg-7 d-flex flex-row gap-2\">\n <div class=\"input-group\">\n <input\n ref={searchInputRef}\n type=\"text\"\n class=\"form-control\"\n aria-label={globalFilter.placeholder}\n placeholder={globalFilter.placeholder}\n value={globalFilter.value}\n onInput={(e) => {\n if (!(e.target instanceof HTMLInputElement)) return;\n globalFilter.setValue(e.target.value);\n }}\n />\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary\"\n aria-label=\"Clear search\"\n title=\"Clear search\"\n data-bs-toggle=\"tooltip\"\n onClick={() => globalFilter.setValue('')}\n >\n <i class=\"bi bi-x-circle\" aria-hidden=\"true\" />\n </button>\n </div>\n {/* We do this instead of CSS properties for the accessibility checker.\n We can't have two elements with the same id of 'column-manager-button'. */}\n {isMediumOrLarger && <ColumnManager table={table} />}\n </div>\n {/* We do this instead of CSS properties for the accessibility checker.\n We can't have two elements with the same id of 'column-manager-button'. */}\n {!isMediumOrLarger && <ColumnManager table={table} />}\n <div class=\"flex-lg-grow-1 d-flex flex-row justify-content-end\">\n <div class=\"text-muted text-nowrap\">\n Showing {displayedCount} of {totalCount} {title.toLowerCase()}\n </div>\n </div>\n </div>\n <div class=\"flex-grow-1\">\n <TanstackTable table={table} title={title} {...tableOptions} />\n </div>\n </div>\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"TanstackTable.js","sourceRoot":"","sources":["../../src/components/TanstackTable.tsx"],"names":[],"mappings":"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","sourcesContent":["import { flexRender } from '@tanstack/react-table';\nimport { notUndefined, useVirtualizer } from '@tanstack/react-virtual';\nimport type { Header, Row, SortDirection, Table } from '@tanstack/table-core';\nimport clsx from 'clsx';\nimport { useEffect, useRef, useState } from 'preact/hooks';\nimport type { JSX } from 'preact/jsx-runtime';\n\nimport { ColumnManager } from './ColumnManager.js';\nimport {\n TanstackTableDownloadButton,\n type TanstackTableDownloadButtonProps,\n} from './TanstackTableDownloadButton.js';\n\nfunction SortIcon({ sortMethod }: { sortMethod: false | SortDirection }) {\n if (sortMethod === 'asc') {\n return <i class=\"bi bi-sort-up-alt\" aria-hidden=\"true\" />;\n } else if (sortMethod === 'desc') {\n return <i class=\"bi bi-sort-down\" aria-hidden=\"true\" />;\n } else {\n return <i class=\"bi bi-arrow-down-up opacity-75 text-muted\" aria-hidden=\"true\" />;\n }\n}\n\nfunction ResizeHandle<RowDataModel>({\n header,\n setColumnSizing,\n}: {\n header: Header<RowDataModel, unknown>;\n setColumnSizing: Table<RowDataModel>['setColumnSizing'];\n}) {\n const minSize = header.column.columnDef.minSize ?? 0;\n const maxSize = header.column.columnDef.maxSize ?? 0;\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {\n e.preventDefault();\n const currentSize = header.getSize();\n const increment = e.shiftKey ? 20 : 5; // Larger increment with Shift key\n const newSize =\n e.key === 'ArrowLeft'\n ? Math.max(minSize, currentSize - increment)\n : Math.min(maxSize, currentSize + increment);\n\n setColumnSizing((prevSizing) => ({\n ...prevSizing,\n [header.column.id]: newSize,\n }));\n } else if (e.key === 'Home') {\n e.preventDefault();\n header.column.resetSize();\n }\n };\n\n const columnName =\n typeof header.column.columnDef.header === 'string'\n ? header.column.columnDef.header\n : header.column.id;\n\n return (\n <div class=\"py-1 h-100\" style={{ position: 'absolute', right: 0, top: 0, width: '4px' }}>\n {/* separator role is focusable, so these jsx-a11y-x rules are false positives.\n https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/separator_role#focusable_separator\n */}\n {/* eslint-disable-next-line jsx-a11y-x/no-noninteractive-element-interactions */}\n <div\n role=\"separator\"\n aria-label={`Resize '${columnName}' column`}\n aria-valuetext={`${header.getSize()}px`}\n aria-orientation=\"vertical\"\n aria-valuemin={minSize}\n aria-valuemax={maxSize}\n aria-valuenow={header.getSize()}\n // eslint-disable-next-line jsx-a11y-x/no-noninteractive-tabindex\n tabIndex={0}\n class=\"h-100\"\n style={{\n background: header.column.getIsResizing() ? 'var(--bs-primary)' : 'var(--bs-gray-400)',\n cursor: 'col-resize',\n transition: 'background-color 0.2s',\n }}\n onMouseDown={header.getResizeHandler()}\n onTouchStart={header.getResizeHandler()}\n onKeyDown={handleKeyDown}\n />\n </div>\n );\n}\n\nconst DefaultNoResultsState = (\n <>\n <i class=\"bi bi-search display-4 mb-2\" aria-hidden=\"true\" />\n <p class=\"mb-0\">No results found matching your search criteria.</p>\n </>\n);\n\nconst DefaultEmptyState = (\n <>\n <i class=\"bi bi-eye-slash display-4 mb-2\" aria-hidden=\"true\" />\n <p class=\"mb-0\">No results found.</p>\n </>\n);\n\ninterface TanstackTableProps<RowDataModel> {\n table: Table<RowDataModel>;\n title: string;\n filters?: Record<string, (props: { header: Header<RowDataModel, unknown> }) => JSX.Element>;\n rowHeight?: number;\n noResultsState?: JSX.Element;\n emptyState?: JSX.Element;\n}\n\nconst DEFAULT_FILTER_MAP = {};\n\n/**\n * A generic component that renders a full-width, resizeable Tanstack Table.\n * @param params\n * @param params.table - The table model\n * @param params.title - The title of the table\n * @param params.filters - The filters for the table\n * @param params.rowHeight - The height of the rows in the table\n * @param params.noResultsState - The no results state for the table\n * @param params.emptyState - The empty state for the table\n */\nexport function TanstackTable<RowDataModel>({\n table,\n title,\n filters = DEFAULT_FILTER_MAP,\n rowHeight = 42,\n noResultsState = DefaultNoResultsState,\n emptyState = DefaultEmptyState,\n}: TanstackTableProps<RowDataModel>) {\n const parentRef = useRef<HTMLDivElement>(null);\n const tableRef = useRef<HTMLDivElement>(null);\n const rows = [...table.getTopRows(), ...table.getCenterRows()];\n const rowVirtualizer = useVirtualizer({\n count: rows.length,\n getScrollElement: () => parentRef.current,\n estimateSize: () => rowHeight,\n overscan: 10,\n });\n\n // Track focused cell for grid navigation\n const [focusedCell, setFocusedCell] = useState<{ row: number; col: number }>({ row: 0, col: 0 });\n\n const getVisibleCells = (row: Row<RowDataModel>) => [\n ...row.getLeftVisibleCells(),\n ...row.getCenterVisibleCells(),\n ];\n\n const handleGridKeyDown = (e: KeyboardEvent, rowIdx: number, colIdx: number) => {\n const rowLength = getVisibleCells(rows[rowIdx]).length;\n const adjacentCells: Record<KeyboardEvent['key'], { row: number; col: number }> = {\n ArrowDown: {\n row: Math.min(rows.length - 1, rowIdx + 1),\n col: colIdx,\n },\n ArrowUp: {\n row: Math.max(0, rowIdx - 1),\n col: colIdx,\n },\n ArrowRight: {\n row: rowIdx,\n col: Math.min(rowLength - 1, colIdx + 1),\n },\n ArrowLeft: {\n row: rowIdx,\n col: Math.max(0, colIdx - 1),\n },\n };\n\n const next = adjacentCells[e.key];\n\n if (!next) {\n return;\n }\n\n setFocusedCell({ row: next.row, col: next.col });\n // If we are on the leftmost column, we should allow left scrolling.\n if (colIdx === 0 && e.key === 'ArrowLeft') {\n return;\n }\n\n // If we are on the top row, we should allow up scrolling.\n if (rowIdx === 0 && e.key === 'ArrowUp') {\n return;\n }\n\n // If we are on the rightmost column, we should allow right scrolling.\n if (colIdx === rowLength - 1 && e.key === 'ArrowRight') {\n return;\n }\n\n e.preventDefault();\n };\n\n useEffect(() => {\n const selector = `[data-grid-cell-row=\"${focusedCell.row}\"][data-grid-cell-col=\"${focusedCell.col}\"]`;\n const cell = tableRef.current?.querySelector(selector) as HTMLElement | null;\n if (!cell) return;\n\n // eslint-disable-next-line react-you-might-not-need-an-effect/no-chain-state-updates\n cell.focus();\n }, [focusedCell]);\n\n const virtualRows = rowVirtualizer.getVirtualItems();\n const [before, after] =\n virtualRows.length > 0\n ? [\n notUndefined(virtualRows[0]).start - rowVirtualizer.options.scrollMargin,\n rowVirtualizer.getTotalSize() - notUndefined(virtualRows.at(-1)).end,\n ]\n : [0, 0];\n const headerGroups = table.getHeaderGroups();\n const isTableResizing = headerGroups.some((headerGroup) =>\n headerGroup.headers.some((header) => header.column.getIsResizing()),\n );\n const lastColumnId = table.getAllLeafColumns()[table.getAllLeafColumns().length - 1].id;\n\n const tableRect = tableRef.current?.getBoundingClientRect();\n\n // We toggle this here instead of in the parent since this component logically manages all UI for the table.\n // eslint-disable-next-line react-you-might-not-need-an-effect/no-manage-parent\n useEffect(() => {\n document.body.classList.toggle('no-user-select', isTableResizing);\n }, [isTableResizing]);\n\n // Helper function to get aria-sort value\n const getAriaSort = (sortDirection: false | SortDirection) => {\n switch (sortDirection) {\n case 'asc':\n return 'ascending';\n case 'desc':\n return 'descending';\n default:\n return 'none';\n }\n };\n\n const displayedCount = table.getRowModel().rows.length;\n const totalCount = table.getCoreRowModel().rows.length;\n\n return (\n <div style={{ position: 'relative' }} class=\"d-flex flex-column h-100\">\n <div\n ref={parentRef}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n overflow: 'auto',\n overflowAnchor: 'none',\n }}\n >\n <div\n ref={tableRef}\n style={{\n position: 'relative',\n width: `max(${table.getTotalSize()}px, 100%)`,\n }}\n >\n <table\n class=\"table table-hover mb-0 border border-top-0\"\n style={{ tableLayout: 'fixed' }}\n aria-label={title}\n role=\"grid\"\n >\n <thead>\n {headerGroups.map((headerGroup) => (\n <tr key={headerGroup.id}>\n {headerGroup.headers.map((header, index) => {\n const isPinned = header.column.getIsPinned();\n const sortDirection = header.column.getIsSorted();\n const canSort = header.column.getCanSort();\n const canFilter = header.column.getCanFilter();\n const columnName =\n typeof header.column.columnDef.header === 'string'\n ? header.column.columnDef.header\n : header.column.id;\n\n const style: JSX.CSSProperties = {\n width:\n header.column.id === lastColumnId\n ? `max(100%, ${header.getSize()}px)`\n : header.getSize(),\n position: 'sticky',\n top: 0,\n zIndex: isPinned === 'left' ? 2 : 1,\n left: isPinned === 'left' ? header.getStart() : undefined,\n boxShadow:\n 'inset 0 calc(-1 * var(--bs-border-width)) 0 0 rgba(0, 0, 0, 1), inset 0 var(--bs-border-width) 0 0 var(--bs-border-color)',\n };\n\n return (\n <th\n key={header.id}\n class={clsx(isPinned === 'left' && 'bg-light')}\n style={style}\n aria-sort={canSort ? getAriaSort(sortDirection) : undefined}\n role=\"columnheader\"\n >\n <div class=\"d-flex align-items-center justify-content-between gap-2\">\n <button\n class=\"text-nowrap flex-grow-1 text-start\"\n style={{\n cursor: canSort ? 'pointer' : 'default',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n background: 'transparent',\n border: 'none',\n }}\n type=\"button\"\n aria-label={\n canSort\n ? `'${columnName}' column, current sort is ${getAriaSort(sortDirection)}`\n : undefined\n }\n onClick={canSort ? header.column.getToggleSortingHandler() : undefined}\n onKeyDown={\n canSort\n ? (e) => {\n const handleSort = header.column.getToggleSortingHandler();\n if (e.key === 'Enter' && handleSort) {\n e.preventDefault();\n handleSort(e);\n }\n }\n : undefined\n }\n >\n {header.isPlaceholder\n ? null\n : flexRender(header.column.columnDef.header, header.getContext())}\n {canSort && (\n <span class=\"ms-2\" aria-hidden=\"true\">\n <SortIcon sortMethod={sortDirection || false} />\n </span>\n )}\n {canSort && (\n <span class=\"visually-hidden\">\n , {getAriaSort(sortDirection)}, click to sort\n </span>\n )}\n </button>\n\n {canFilter && filters[header.column.id]?.({ header })}\n </div>\n {tableRect?.width &&\n tableRect.width > table.getTotalSize() &&\n index === headerGroup.headers.length - 1 ? null : (\n <ResizeHandle header={header} setColumnSizing={table.setColumnSizing} />\n )}\n </th>\n );\n })}\n </tr>\n ))}\n </thead>\n <tbody>\n {before > 0 && (\n <tr tabIndex={-1}>\n <td colSpan={headerGroups[0].headers.length} style={{ height: before }} />\n </tr>\n )}\n {virtualRows.map((virtualRow) => {\n const row = rows[virtualRow.index];\n const visibleCells = getVisibleCells(row);\n const rowIdx = virtualRow.index;\n\n return (\n <tr key={row.id} style={{ height: rowHeight }}>\n {visibleCells.map((cell, colIdx) => (\n <td\n key={cell.id}\n // You can tab to the most-recently focused cell.\n tabIndex={focusedCell.row === rowIdx && focusedCell.col === colIdx ? 0 : -1}\n // We store this so you can navigate around the grid.\n data-grid-cell-row={rowIdx}\n data-grid-cell-col={colIdx}\n style={{\n width:\n cell.column.id === lastColumnId\n ? `max(100%, ${cell.column.getSize()}px)`\n : cell.column.getSize(),\n position: cell.column.getIsPinned() === 'left' ? 'sticky' : undefined,\n left:\n cell.column.getIsPinned() === 'left'\n ? cell.column.getStart()\n : undefined,\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n }}\n onFocus={() => setFocusedCell({ row: rowIdx, col: colIdx })}\n onKeyDown={(e) => handleGridKeyDown(e, rowIdx, colIdx)}\n >\n {flexRender(cell.column.columnDef.cell, cell.getContext())}\n </td>\n ))}\n </tr>\n );\n })}\n {after > 0 && (\n <tr tabIndex={-1}>\n <td colSpan={headerGroups[0].headers.length} style={{ height: after }} />\n </tr>\n )}\n </tbody>\n </table>\n </div>\n </div>\n\n {table.getVisibleLeafColumns().length === 0 && (\n <div>\n <div\n class=\"d-flex flex-column justify-content-center align-items-center text-muted py-4\"\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n background: 'var(--bs-body-bg)',\n }}\n role=\"status\"\n aria-live=\"polite\"\n >\n <i class=\"bi bi-eye-slash display-4 mb-2\" aria-hidden=\"true\" />\n <p class=\"mb-0\">No columns selected. Use the View menu to show columns.</p>\n </div>\n </div>\n )}\n {displayedCount === 0 && (\n <div\n class=\"d-flex flex-column justify-content-center align-items-center text-muted py-4\"\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n background: 'var(--bs-body-bg)',\n }}\n role=\"status\"\n aria-live=\"polite\"\n >\n {totalCount > 0 ? noResultsState : emptyState}\n </div>\n )}\n </div>\n );\n}\n\n/**\n * A generic component that wraps the TanstackTable component in a card.\n * @param params\n * @param params.table - The table model\n * @param params.title - The title of the card\n * @param params.headerButtons - The buttons to display in the header\n * @param params.globalFilter - State management for the global filter\n * @param params.globalFilter.value\n * @param params.globalFilter.setValue\n * @param params.globalFilter.placeholder\n * @param params.tableOptions - Specific options for the table. See {@link TanstackTableProps} for more details.\n * @param params.downloadButtonOptions - Specific options for the download button. See {@link TanstackTableDownloadButtonProps} for more details.\n */\nexport function TanstackTableCard<RowDataModel>({\n table,\n title,\n headerButtons,\n globalFilter,\n tableOptions,\n downloadButtonOptions = null,\n}: {\n table: Table<RowDataModel>;\n title: string;\n headerButtons: JSX.Element;\n globalFilter: {\n value: string;\n setValue: (value: string) => void;\n placeholder: string;\n };\n tableOptions: Partial<Omit<TanstackTableProps<RowDataModel>, 'table'>>;\n downloadButtonOptions?: Omit<TanstackTableDownloadButtonProps<RowDataModel>, 'table'> | null;\n}) {\n const searchInputRef = useRef<HTMLInputElement>(null);\n\n // Track screen size for aria-hidden\n const mediaQuery = typeof window !== 'undefined' ? window.matchMedia('(min-width: 768px)') : null;\n const [isMediumOrLarger, setIsMediumOrLarger] = useState(false);\n\n useEffect(() => {\n // TODO: This is a workaround to avoid a hydration mismatch.\n // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect\n setIsMediumOrLarger(mediaQuery?.matches ?? true);\n }, [mediaQuery]);\n\n useEffect(() => {\n const handler = (e: MediaQueryListEvent) => setIsMediumOrLarger(e.matches);\n mediaQuery?.addEventListener('change', handler);\n return () => mediaQuery?.removeEventListener('change', handler);\n }, [mediaQuery]);\n\n // Focus the search input when Ctrl+F is pressed\n useEffect(() => {\n function onKeyDown(event: KeyboardEvent) {\n if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'f') {\n if (searchInputRef.current && searchInputRef.current !== document.activeElement) {\n searchInputRef.current.focus();\n event.preventDefault();\n }\n }\n }\n document.addEventListener('keydown', onKeyDown);\n return () => document.removeEventListener('keydown', onKeyDown);\n }, []);\n\n const displayedCount = table.getRowModel().rows.length;\n const totalCount = table.getCoreRowModel().rows.length;\n\n return (\n <div class=\"card d-flex flex-column h-100\">\n <div class=\"card-header bg-primary text-white\">\n <div class=\"d-flex align-items-center justify-content-between gap-2\">\n <div>{title}</div>\n <div class=\"d-flex gap-2\">\n {headerButtons}\n\n {downloadButtonOptions && (\n <TanstackTableDownloadButton table={table} {...downloadButtonOptions} />\n )}\n </div>\n </div>\n </div>\n <div class=\"card-body d-flex flex-column\">\n <div class=\"d-flex flex-row flex-wrap align-items-center mb-3 gap-2\">\n <div class=\"flex-grow-1 flex-lg-grow-0 col-xl-6 col-lg-7 d-flex flex-row gap-2\">\n <div class=\"input-group\">\n <input\n ref={searchInputRef}\n type=\"text\"\n class=\"form-control\"\n aria-label={globalFilter.placeholder}\n placeholder={globalFilter.placeholder}\n value={globalFilter.value}\n onInput={(e) => {\n if (!(e.target instanceof HTMLInputElement)) return;\n globalFilter.setValue(e.target.value);\n }}\n />\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary\"\n aria-label=\"Clear search\"\n title=\"Clear search\"\n data-bs-toggle=\"tooltip\"\n onClick={() => globalFilter.setValue('')}\n >\n <i class=\"bi bi-x-circle\" aria-hidden=\"true\" />\n </button>\n </div>\n {/* We do this instead of CSS properties for the accessibility checker.\n We can't have two elements with the same id of 'column-manager-button'. */}\n {isMediumOrLarger && <ColumnManager table={table} />}\n </div>\n {/* We do this instead of CSS properties for the accessibility checker.\n We can't have two elements with the same id of 'column-manager-button'. */}\n {!isMediumOrLarger && <ColumnManager table={table} />}\n <div class=\"flex-lg-grow-1 d-flex flex-row justify-content-end\">\n <div class=\"text-muted text-nowrap\">\n Showing {displayedCount} of {totalCount} {title.toLowerCase()}\n </div>\n </div>\n </div>\n <div class=\"flex-grow-1\">\n <TanstackTable table={table} title={title} {...tableOptions} />\n </div>\n </div>\n </div>\n );\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prairielearn/ui",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -17,7 +17,7 @@
17
17
  },
18
18
  "dependencies": {
19
19
  "@prairielearn/browser-utils": "^2.5.1",
20
- "@prairielearn/preact-cjs": "^1.1.5",
20
+ "@prairielearn/preact-cjs": "^1.1.6",
21
21
  "@tanstack/react-table": "^8.21.3",
22
22
  "@tanstack/react-virtual": "^3.13.12",
23
23
  "@tanstack/table-core": "^8.21.3",
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "devDependencies": {
28
28
  "@prairielearn/tsconfig": "^0.0.0",
29
- "@types/node": "^22.18.8",
29
+ "@types/node": "^22.18.13",
30
30
  "typescript": "^5.9.3",
31
31
  "typescript-cp": "^0.1.9"
32
32
  }
@@ -195,9 +195,9 @@ export function TanstackTable<RowDataModel>({
195
195
  useEffect(() => {
196
196
  const selector = `[data-grid-cell-row="${focusedCell.row}"][data-grid-cell-col="${focusedCell.col}"]`;
197
197
  const cell = tableRef.current?.querySelector(selector) as HTMLElement | null;
198
- if (!cell) {
199
- return;
200
- }
198
+ if (!cell) return;
199
+
200
+ // eslint-disable-next-line react-you-might-not-need-an-effect/no-chain-state-updates
201
201
  cell.focus();
202
202
  }, [focusedCell]);
203
203
 
package/vitest.config.ts CHANGED
@@ -2,10 +2,10 @@ import { defineConfig } from 'vitest/config';
2
2
 
3
3
  export default defineConfig({
4
4
  test: {
5
+ dir: `${import.meta.dirname}/src`,
5
6
  coverage: {
6
7
  reporter: ['html', 'text-summary', 'cobertura'],
7
- all: true,
8
- include: ['src/**'],
8
+ include: ['src/**/*.{ts,tsx}'],
9
9
  },
10
10
  },
11
11
  });