Repository navigation
@chassis-ui/react@0.1.1
Patch Changes
-
02594b4: Fix
Paginationdropping keyboard focus every time the page changes.PaginationItemrendered the active item as a<span>, so activating a page unmounted the focused
<button>and mounted a different element in its place — focus fell to<body>and a keyboard user
had to tab in from the top of the document again (WCAG 2.4.3). The active item now keeps whatever
element it would render anyway (<button>, or<a>when givenhref), so the element identity is
stable across activation. The<span>wasn't inert either: smart mode passes anonClick, so it
picked uprole="button"andtabIndex={0}— a synthetic button in place of the real one.aria-current="page"also moved from the wrapping<li>onto the control itself, which is where
the WAI-ARIA pagination pattern puts it and where a screen reader conveys it when focus lands.
Styling is unchanged: chassis-css matches.pagination-link { &.active, .active > & }andactive
still lands on the<li>. -
02594b4: Adds
DataGrid, a virtualized alternative toTablefor datasets too large to mount all at once — only the rows scrolled into view are ever rendered, so a grid with thousands of rows costs the same to render as one with a dozen. Composed the same way asTable(DataGridHeader/DataGridColumn/DataGridBody/DataGridRow/DataGridCell), plusDataGridSelectAllCell/DataGridSelectionCellfor selection checkboxes.Beyond basic virtualized rows, columns, sorting, and selection, it also supports:
- Pinned columns —
pin="start"/pin="end"on aDataGridColumnkeeps it visible while the grid scrolls horizontally, with multiple pinned columns on the same side stacking in declaration order. - Variable row height —
rowHeight="auto"(paired withestimatedRowHeight) measures each row from its own rendered content instead of a single fixed height shared by every row. - Async/infinite loading —
onLoadMore/isLoading/loadingContentonDataGridBodyfetch and append more rows as the grid scrolls near its last one, for datasets too large to load up front. - Header/footer pinning — the header row stays pinned to the top of the grid's scrollable area as body rows scroll underneath it; a new
footerprop onDataGridrenders content (e.g. a totals row) pinned below the body, outside the grid's own keyboard-navigable structure.
This adds
react-aria-componentsas a runtime dependency —DataGridis built on itsVirtualizer, and it's the only component in the package that uses it. It's externalized from the bundle, so it resolves from your ownnode_moduleslikereact-ariaalready does. Note thatreact-aria-componentspinsreact-ariato an exact version, so an install may end up with a second copy ofreact-ariaalongside the one this package depends on; nothing here relies on the two being the same instance.See the
datagrid.mdxdocs page for the full set of examples and documented scope boundaries (e.g. no stacked variant, no zebra striping, no interactive column resize/reorder — all deliberate given the virtualized rendering model). - Pinned columns —
-
02594b4: Export every component's prop types from the package entry point.
ButtonProps,TableProps,MenuItemDef,Placement, the shared scales (ContextColor,
Sizing,Spacing, ...) and the public hooks' result types were all declared but never exported,
soimport type { ButtonProps } from '@chassis-ui/react'failed and consumers had to re-derive
them withComponentProps<typeof Button>. All 164 type names the component barrels already
export are now reachable from the package root.As a side effect,
TableBodyProps/TableHeaderPropsare no longer emitted under$1-suffixed
names indist/index.d.ts— now that this package exports its own, react-stately's same-named
types are the ones that get suffixed instead. -
02594b4: Fix
PopoverandTooltipdropping the trigger child'sref.Both re-render their child with
cloneElement, whose config replaces the child'srefoutright
rather than merging with it, so<Tooltip><Button ref={mine} /></Tooltip>never populatedmine.
Both now fork the caller's ref with their own.Tooltipadditionally spread its react-aria
triggerPropsover the child's props, silently dropping a handler the caller had put on their own
trigger (e.g.onFocus); both now merge them withmergePropsinstead. -
02594b4: Make every user-facing string the components render themselves translatable.
I18nProviderdrives month names, weekday names, segment order and the calendar system through
react-aria, but a handful of strings these components render themselves had no equivalent in
react-aria's dictionaries and were hardcoded English with no way to override them. Under
<I18nProvider locale="ar-SA">a user got Arabic month names interleaved with an English
"Previous years".Calendar,RangeCalendar,DatePickerandDateRangePickertake a newlabelsprop
(Partial<CalendarLabels>, merged over the English defaults) covering the year view's paging
arrows and live-region announcements, the month/year header buttons, the calendar trigger, and
the clear adornment. ADatePicker'slabelsalso reaches theCalendarit renders internally,
so it's set once. TheCalendarLabelstype is exported.Breadcrumbacceptsaria-labelfor its wrapping<nav>landmark, which previously hardcoded
"breadcrumb"on an element{...rest}never reached. This also lets two breadcrumb trails on
one page be told apart in a screen reader's landmark list.
-
02594b4: Fix six behavioural defects in focus handling, overlay callbacks, handler composition and form
field rendering.focusRedirectcould suppress a focus ring permanently. It saved the element's outline,
forced it off, and restored it on blur — but a second call before that blur captured the
already-suppressednoneas the value to restore. Reachable by clicking twice at the same
disabled end of anends="stop"Carousel. It now delegates the whole suppress/restore cycle
tosuppressFocusRing, which owns the re-entrancy guard, rather than reimplementing it without
one.Popoverstole focus back to its trigger on every close. Dismissing by clicking another
control moved focus off whatever the user had just clicked. Focus is now reclaimed only when the
popover still holds it, or when nothing does.Popover,TooltipandMenureported a hide on mount. The visibility effect ran its
"hidden" branch on the initial commit, soonHide(andMenu'sonHidden) fired for an overlay
that had never been shown, beforeonShowhad fired once. All of these report transitions now,
so none fires on mount — includingonShow/onShownfor an overlay mounted already-open.Carousellet a caller's handler replace its own.onKeyDown,onMouseEnterand
onMouseLeavewere overwritten by the props spread, so passing any of them silently switched off
arrow-key navigation or pause-on-hover. They are composed now, matching the rule
CarouselControlPrev/CarouselControlNext/CarouselPlayPausealready follow foronClick.ModalandDrawerdropped a caller'sonClickentirely. A<dialog>needsonClickfor
its own backdrop detection, and the caller's was spread alongside it. It is chained ahead of the
backdrop handling now, and fires for every click on the dialog.- A field set both
invalidandvalidrendered a duplicate DOM id. Both feedback nodes carry
the same id, leaving every control'saria-describedbypointing at an ambiguous target. At most
one renders now, invalid winning.
Two of these change behaviour beyond restoring the documented contract, hence a minor rather than a
patch:- An overlay mounted already-open (
<Popover visible>) no longer firesonShow/onShownon
mount. Wire up to the transition instead, or read the prop you already control. FormFieldgiven aclassNamebut nolabel/help/feedback now renders its.form-field
wrapper instead of returning the children bare — previously the class was silently dropped, so it
had nowhere to land. Fields with noclassNamestill render bare, unchanged.
-
02594b4: Fix published types and package metadata that were wrong for consumers.
usePagination'sprevRef/nextRefare typedRefObject<T | null>, matching the
useRef<T>(null)they actually come from. They were declared non-null, soprevRef.current.focus()
type-checked and then threw before mount.- The polymorphic components that pick their element from
hrefrather thancomponent—
Button,Chip,Avatar,NavbarBrand,PaginationItem— accept arefcovering the elements
they can actually render.<Button href="/x" ref={anchorRef}>was a type error even though the
runtime populated that ref with the<a>it rendered. - Dropped the
enginesfield. This is a browser library with no Node runtime requirement, and
engines.node: '>=24'warned on install for consumers on older Node and hard-failed under
engine-strict. It also drove the build's output target; the build now pinses2022explicitly,
alongside a newbrowserslistfield documenting the supported floor (Chrome 107+, Edge 107+,
Firefox 104+, Safari 16+). sideEffectsnow listsdist/paths. It named asrc/path no consumer resolves, so the whole
published bundle was flagged side-effect-free while actually carrying the global focus-ring
listener install.- The published bundle carries exactly one
'use client'directive; it previously carried two.
-
02594b4: Stop dev-time misuse warnings from firing in production, and fix arrow-key direction under RTL.
- Every
console.warn/console.errorin the package now goes throughdevWarning/devError,
guarded onprocess.env.NODE_ENV. Nothing is logged in a consumer's production build. They're
also de-duplicated on the message:AccordionItem,Carousel,FormField,Selectand
FloatingInputwarned from a render body, so a warning re-fired on every render and twice per
render under StrictMode. OtpInput's andChipInput's Arrow key handling now mirrors underdir="rtl", matching what
CarouselandMenuSubmenualready did. Arrow keys move by visual direction, so in an RTL field
ArrowLeft moves forward through the boxes; previously the LTR mapping was hardcoded and sent an
RTL user backwards.
- Every