diff --git a/packages/core/src/Table/Table.spec.md b/packages/core/src/Table/Table.spec.md index 11785af9104f2..d8cd45918916e 100644 --- a/packages/core/src/Table/Table.spec.md +++ b/packages/core/src/Table/Table.spec.md @@ -11,17 +11,15 @@ approved_at: 2026-09-01 owners: [cixzhang] review_triggers: [public-api, behavior, layout, theming, accessibility] verified_by: - [ - packages/core/src/Table/Table.test.tsx, - packages/core/src/Table/Table.perf.test.tsx, - packages/core/src/Table/plugins/selection/useTableSelection.test.tsx, - packages/core/src/Table/plugins/sortable/useTableSortable.test.tsx, - packages/core/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx, - packages/cli/foundation/discovery/theming-targets.test.mjs, - packages/cli/api/theme/targets/targets.test.mjs, - packages/core/src/theme/themingTargets.test.ts, - scripts/check-knowledge.mjs, - ] + - packages/core/src/Table/Table.test.tsx + - packages/core/src/Table/Table.perf.test.tsx + - packages/core/src/Table/plugins/selection/useTableSelection.test.tsx + - packages/core/src/Table/plugins/sortable/useTableSortable.test.tsx + - packages/core/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx + - packages/cli/foundation/discovery/theming-targets.test.mjs + - packages/cli/api/theme/targets/targets.test.mjs + - packages/core/src/theme/themingTargets.test.ts + - scripts/check-knowledge.mjs modules: [module:Table/useTableRowStatus] families: [] design_specs: [] @@ -30,6 +28,7 @@ architecture: architecture:component-theming-surface, architecture:container-padding, architecture:interaction-modality, + architecture:knowledge-contracts, architecture:public-component-api, ] contributing: [] @@ -40,185 +39,66 @@ system_specs: [] ## Intent -Table presents consistently structured data in semantic rows and columns. This -contract records its current aggregate anatomy, parent-owned target inventory, -stable sorting, selection, expansion, and empty-state parts, and the shared -`TablePlugin` protocol through which public modules compose. Module-local API, -generated anatomy, accessibility, migration, and evidence belong to each listed -`module:*` record. +Table presents structured data in semantic rows and columns. This aggregate contract owns Table anatomy, the eight current Table targets, and the shared `TablePlugin` protocol; independently contractible plugin behavior stays in each listed `module:*` record. ## Compatibility and migration - Released default preserved: `yes` -- Compatibility class: additive documentation only; runtime, DOM, styling, - targets, aliases, and public API remain unchanged -- Controlled/uncontrolled behavior: unchanged +- Compatibility class: documentation-only; runtime, DOM, styling, targets, aliases, public API, and controlled/uncontrolled behavior are unchanged - Migration decision: none -Consumer migration instructions belong in consumer docs and release notes. - ## Ownership boundary **Owns** -- The Table and Scroll region, the aggregate Header section, Body section, - conditional Footer section, Row, Column header cell, and Cell anatomy, and the - eight current `table*` targets mapped below. -- The stable Sort control and Sort priority rendered by useTableSortable. -- The stable Expansion control and Expanded detail panel rendered by - useTableRowExpansion. -- Placement of selection-plugin CheckboxInput controls in generated header and - body cells. -- The shared `TablePlugin` transform surface, phase order, sequential composition, - named-plugin ordering, slot protocol, failure isolation, and stable plugin-array - identity used by every Table module. +- Aggregate Table, Scroll region, section, Row, Column header cell, Cell, stable plugin-part anatomy, and the current target map below. +- Shared `TablePlugin` transforms, slots, ordering, composition, failure isolation, context nesting, and resolved-array identity. **Does not own / non-goals** -- CheckboxInput visuals, Icon glyphs, or the default EmptyState surface; those - remain owned by their respective components. -- Cell values, custom cell renderers, custom empty states, footer content, or - expanded detail content supplied by the caller. -- Pagination, filtering, column-management, tree, grouping, sticky-column, or - context-menu anatomy beyond the stable parts explicitly recorded here. -- The public API, generated columns or other anatomy, internal precedence, - accessibility, migration, performance evidence, or theming decisions of an - independently contractible module. `module:Table/useTableRowStatus` owns those - concerns for `useTableRowStatus`. -- New targets for current untargeted plugin parts, or correction of current - target-reachability gaps. -- New runtime behavior, DOM, API, target, or alias. +- Caller content or delegated CheckboxInput, Icon, and EmptyState internals. +- Pagination, filtering, column management, tree, grouping, sticky-column, context-menu, or other plugin anatomy beyond the stable parts recorded here. +- Module-local API, generated anatomy, precedence, accessibility, migration, or evidence. [The row-status module](./plugins/rowStatus/useTableRowStatus.spec.md) owns those for `useTableRowStatus`. +- New runtime behavior, DOM, API, targets, aliases, or correction of current reachability gaps. ## Public concepts -Table records two aggregate public concepts without duplicating module-local APIs: - -| Concept | Closed values or states | Meaning | Availability by variant/orientation/state | Default | Owner | Stability | Invalid-value behavior | -| ----------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ----------------------------------------- | -------------------------- | ----------------- | --------- | ------------------------------------------------------------------------------------------------------------- | -| Plugin collection | Named `Record>` or absent | Adds ordered transformations to the data-driven Table pipeline. | Data-driven Table | Absent | `component:Table` | Stable | Development warns for unknown transform keys, non-functions, and empty plugins; unsupported keys are ignored. | -| Plugin transform | Column, element-render-prop, scroll-wrapper, or context transform | Changes one owned pipeline phase while preserving the common `TablePlugin` protocol. | According to the transform method | Omitted methods are no-ops | `component:Table` | Stable | A throwing transform is isolated and the prior accumulated value continues. | - -Consumer data, columns, props, module signatures, defaults, and usage remain -documented in `Table.doc.mjs` and the member and module docs. +| Concept | Contract | +| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| Plugin collection | Data-driven Table accepts an optional named `Record>`; consumer syntax remains in [Table.doc.mjs](./Table.doc.mjs). | +| Plugin transform | A plugin may implement any subset of the shared column, element, wrapper, or context transforms; omitted methods are no-ops. | ## Behavioral and layout contract -| ID | Invariant | Basis | Evidence state | -| ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------ | ------------------------------------------------------------------------------- | -| FR1 | Styled Table renders one keyboard-focusable Scroll region around one semantic Table. | Current source, docs, and tests | Verified current behavior; no new behavior decided | -| FR2 | Data-driven mode renders a Body section and a Header section with Column header cells when columns are present. Each data item renders a standard Row and Cells; an empty data array instead renders the configured empty-state row when enabled. It never creates a Footer section. | Current source, docs, and tests | Verified current behavior; no new behavior decided | -| FR3 | Children mode passes caller composition through to the Table. TableHeader, TableBody, and TableFooter provide the respective sections, and TableRow, TableHeaderCell, and TableCell provide standard rows and cells. | Current source, docs, and tests | Verified current composition; no new API or DOM rule | -| FR4 | `Table.doc.mjs` is the canonical aggregate consumer owner for the eight current targets: `table`, `table-scroll-wrapper`, `table-header`, `table-body`, `table-footer`, `table-row`, `table-cell`, and `table-header-cell`. Member docs retain direct lookup metadata through `subComponentOf: 'Table'`. | Current docs and CLI target tests | Verified parent ownership; focused placement coverage is partial | -| FR5 | For a sortable column, useTableSortable renders a Sort control around the column label, an Icon-owned Sort indicator glyph, and a numeric Sort priority only while multi-sort has more than one active entry. | Current source, docs, and tests | Control and priority are tested; glyph presence is source-inspected only | -| FR6 | useTableSelection renders CheckboxInput-owned Selection controls in the generated selection Column header cell and each selectable body Cell; selection remains row state rather than separate anatomy. | Current source, docs, and tests | Verified stable delegated controls; no target or state change | -| FR7 | For an expandable row, useTableRowExpansion renders an Expansion control with an Icon-owned Expansion glyph and conditionally appends an Expanded detail panel whose cell spans the column count captured by that plugin's `transformColumns` step. | Current source, docs, and tests | Verified stable plugin parts; current target gaps remain | -| FR8 | Empty data renders the default compact EmptyState unless the caller supplies replacement content or disables it. | Current source, docs, and tests | Verified conditional delegation; caller content remains outside ownership | -| FR9 | Table converts the caller's named plugin record into one ordered array after its built-in styling plugin. Known names use the current canonical sequence `columnSettings → sort → tree → selection → pagination`; every other name follows that known set while preserving its record insertion order. | Current source and docs | Current shared ordering; canonical-name coverage is source-inspected | -| FR10 | Every applicable non-context transform runs sequentially in the resolved plugin-array order, so a later plugin receives the value returned by every earlier successful plugin. A throwing transform reports a development error and leaves the prior accumulated value in the pipeline. | Current source and tests | Sequential composition is tested; failure isolation is source-inspected | -| FR11 | `transformColumns` completes before element transforms. Table then applies table, header-cell/header-row, body-cell/body-row, scroll-wrapper, and context phases at their render points. Header-cell contributions use `before`, `content`, `after`, `overlay`, and `below` slots. Context transforms run in reverse so the first plugin becomes the outermost provider. | Current source, types, and tests | Transform application is tested; complete cross-phase order is source-inspected | -| FR12 | When built-in and named plugin references are unchanged, Table reuses the resolved plugin array; unknown/custom plugin value identity and insertion order remain stable inputs to memoization. | Current source and performance tests | Current performance contract; focused named-order coverage is partial | - -### Current evidence and gaps - -- The `table`, `table-scroll-wrapper`, `table-row`, `table-cell`, and - `table-header-cell` placements have focused runtime assertions. The - `table-header`, `table-body`, and `table-footer` placements are source-inspected; - existing section tests assert element structure and prop forwarding but not the - target classes. -- Sort tests assert the Sort control, labels, state, and conditional priority, but - the test named `renders sort icon for sortable columns` checks only the buttons. - Sort indicator glyph presence is source-inspected and lacks focused regression - evidence. Selection and expansion suites assert their stable controls, states, - and panel structure; delegated Icon and CheckboxInput target classes also remain - source-inspected. -- The Expanded detail panel uses raw `tr` and `td` elements rather than TableRow - and TableCell, so the existing `table-row` and `table-cell` targets do not reach - that panel wrapper. Its `colSpan` comes from the column count captured when the - expansion plugin runs `transformColumns`; a later custom plugin can add or remove - columns and leave the span stale. The current full-span test covers only the case - where expansion captures the final column set. This contract records both gaps - and does not correct them. +- **FR1 — Aggregate structure.** Styled Table renders one keyboard-focusable Scroll region around one semantic Table. Data-driven mode always generates Body, generates Header with Column header cells when columns exist, renders each item as a standard Row with Cells, and never generates Footer; children mode passes caller-owned section composition through, including optional Footer. Empty data renders the default compact EmptyState unless caller content replaces it or `false` disables it. +- **FR2 — Stable plugin anatomy.** A sortable column always gets a Sort control around its label and an Icon-delegated glyph; numeric priority appears only when multi-sort is enabled with multiple active entries. Selection contributes CheckboxInput-delegated header/body controls; expansion contributes its control, Icon-delegated glyph, and conditional detail panel. These are aggregate parts, not parent ownership of module internals. +- **FR3 — Shared protocol.** Each applicable non-context transform runs sequentially in resolved plugin-array order and receives the prior successful result. A throwing transform reports a development error and leaves that prior result in the pipeline. +- **FR4 — Invalid plugins.** Development warns for unknown transform keys, non-function transform values, and empty plugins. Unknown keys are ignored; invoked invalid transforms remain isolated by FR3. +- **FR5 — Expansion gap.** The detail panel spans the column count captured when expansion runs `transformColumns`; a later custom column transform can make that `colSpan` stale. The existing full-span test covers only expansion receiving the final columns; this current correctness gap is recorded, not fixed. ### Allowed variation -- **AV1 - Rendering mode.** Data-driven mode generates sections, rows, and cells; - children mode uses the caller-supplied composition without changing aggregate - ownership. -- **AV2 - Repetition and content.** Column, Row, and Cell counts and caller-owned - content may vary without creating new anatomy parts. -- **AV3 - Optional parts.** Header section, Footer section, default EmptyState, - Sort, Selection, and Expansion parts may be absent according to columns, - rendering mode, plugin configuration, data, and row eligibility. -- **AV4 - Delegated rendering.** CheckboxInput, Icon, and EmptyState may change - internal element shape while preserving their own public contracts. -- **AV5 - Module participation.** Any named module may omit transform phases it does - not need. Unknown/custom plugin names remain valid and follow the shared fallback - ordering without acquiring module-local semantics here. - -### Representative states - -| State | Required invariant | Allowed variation | -| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | -| Data-driven rows | Scroll region contains Table, Header section when columns exist, Body section, and one standard Row with Cells per data item. | Counts, values, density, dividers, and cell content. | -| Empty data | Body section contains default EmptyState unless replaced or disabled. | Caller replacement content or no empty state. | -| Children mode | Caller-supplied sections are passed directly to Table. | Header, Body, and Footer section presence and content. | -| Sortable column | Column header cell contains Sort control and Sort indicator glyph. | Direction and conditional multi-sort priority. | -| Selectable rows | Selection controls occupy generated header and body cells. | Checked, indeterminate, disabled, or absent per row. | -| Expandable row | Expansion control occupies a generated Cell; open state adds the detail panel after its row using the expansion plugin's captured column count. | Expanded state, caller detail content, and later plugin column transforms. | -| Multiple plugins | Built-in styling runs first, then named plugins compose in the resolved order; later transforms receive earlier results. | Supported transform subset, custom names, and omitted phases. | +Rendering mode, row/column counts, caller content, optional sections and plugin parts, and delegated internals may vary without creating aggregate anatomy or changing the shared protocol. Data-driven rows, empty data, children composition, and one or many named plugins must preserve FR1-FR5; module-specific states remain with their module contracts. ### Transformation and precedence order -- **ORD1 - Plugin array.** Built-in plugins precede named consumer plugins. Known - names sort by the canonical list; unknown/custom names retain record insertion - order after the known set. -- **ORD2 - Sequential transforms.** Each non-context phase reduces over that array - from first to last. A later plugin receives the earlier accumulated value. -- **ORD3 - Render phases.** Column transforms complete before element transforms; - element transforms run at the table, header, body, and scroll-wrapper render - points. Context transforms run from last to first so array priority and provider - nesting agree: the first plugin is outermost. -- **ORD4 - Module boundary.** Module records may rely on this protocol and state the - transforms they contribute, but they do not redefine aggregate phase or - named-plugin ordering. +- **ORD1 — Plugin array.** Built-in styling runs first. Known names resolve as `columnSettings → sort → tree → selection → pagination`; unknown names follow in record insertion order when a new array is resolved. +- **ORD2 — Phase order.** `transformColumns` completes before table, header-cell, header-row, body-cell, body-row, and scroll-wrapper transforms at their render points. Header cells compose `before`, `content`, `after`, `overlay`, and `below`. +- **ORD3 — Context order.** Context transforms run in reverse array order, making the first plugin the outermost provider. +- **ORD4 — Module boundary.** Modules may depend on this protocol but must not redefine relative plugin or phase order. ### Performance and resources -- **PR1 - Stable plugin array.** Table reuses the resolved plugin array while the - built-in array and named plugin values are referentially unchanged, including - when the caller recreates the containing record. -- **PR2 - Module ownership.** Each module owns any stronger render, allocation, - listener, observer, or initialization constraint created by its transforms. - Table owns only the common array and transform pipeline cost. +- **PR1 — Stable plugin array.** Table reuses the resolved array while the built-in array and named plugin key/value identities are unchanged, including a recreated or reordered containing record; a new array preserves unknown-plugin insertion order. +- **PR2 — Resource ownership.** Table owns common array and transform-pipeline cost. Each module owns any stronger render, allocation, listener, observer, or initialization constraint introduced by its transforms. ## Accessibility contract -This contract does not change or extend the current native table semantics, -focusable Scroll region, column-header scope, sort `aria-sort`, selection -`aria-selected`, CheckboxInput labels, or expansion `aria-expanded` behavior. -Each public module owns accessibility introduced by its transforms; the shared -pipeline does not confer correctness on module output. +Table MUST preserve native table semantics, the focusable Scroll region, column-header scope, sort `aria-sort`, selection `aria-selected`, CheckboxInput labels, and expansion `aria-expanded`. Each module owns accessibility introduced by its transforms; the shared pipeline does not make module output correct. Shared platform behavior remains with [interaction modality](../../../../docs/architecture/interaction-modality.md). ## Design relationships -| Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract | -| --------------------- | --------------------------------------------------------------------------------- | ------------------------------ | -------------- | ------------------ | -| Table | Groups the semantic table structure. | Current source and public docs | Supporting | FR1-FR4 | -| Scroll region | Provides keyboard-reachable horizontal overflow around Table. | Current source and public docs | Supporting | FR1, FR4 | -| Header section | Groups Column header cells separately from body data. | Current source and public docs | Supporting | FR2-FR4 | -| Column header cell | Identifies one column and hosts optional header controls. | Current source and public docs | Prominent | FR2-FR6 | -| Sort control | Activates sorting for one sortable column. | Current source and public docs | Prominent | FR5 | -| Sort indicator glyph | Shows the current sort direction through Icon. | `component:Icon` | Supporting | FR5 | -| Sort priority | Shows a column's position in active multi-sort order. | Current source and public docs | Supporting | FR5 | -| Selection control | Selects all eligible rows or one eligible row through CheckboxInput. | `component:CheckboxInput` | Prominent | FR6 | -| Body section | Groups data, empty-state, and expanded-detail rows. | Current source and public docs | Supporting | FR2-FR4, FR7, FR8 | -| Row | Groups standard header, body, or footer cells. | Current source and public docs | Supporting | FR2-FR4, FR6 | -| Cell | Contains one value or caller-provided content in a standard body or footer row. | Current source and public docs | Prominent | FR2-FR4, FR6, FR7 | -| Default empty state | Communicates that the current data array has no rows through EmptyState. | `component:EmptyState` | Prominent | FR8 | -| Expansion control | Expands or collapses one eligible row. | Current source and public docs | Prominent | FR7 | -| Expansion glyph | Shows the current expansion direction through Icon. | `component:Icon` | Supporting | FR7 | -| Expanded detail panel | Presents caller-provided detail content in a spanning row below the expanded row. | Current source and public docs | Prominent | FR7 | -| Footer section | Groups caller-supplied summary or total rows below the body. | Current source and public docs | Supporting | FR3, FR4 | +[Table.doc.mjs](./Table.doc.mjs) owns the aggregate consumer anatomy. This map records each current part's exact target, inheritance, delegation, or factual lack of reachability. ### Theming anatomy @@ -236,7 +116,7 @@ pipeline does not confer correctness on module output. }, "Sort priority": { "none": { - "reason": "unsettled: The multi-sort rank has no direct public target and uses a component-owned accent style; future exposure still needs an owner decision" + "reason": "unsettled: The multi-sort rank has no direct public target and future exposure still needs an owner decision" } }, "Selection control": { @@ -253,7 +133,7 @@ pipeline does not confer correctness on module output. }, "Expansion control": { "none": { - "reason": "unsettled: The expansion button has no direct public target and uses component-owned styles; future exposure still needs an owner decision" + "reason": "unsettled: The expansion button has no direct public target and future exposure still needs an owner decision" } }, "Expansion glyph": { @@ -261,57 +141,34 @@ pipeline does not confer correctness on module output. }, "Expanded detail panel": { "none": { - "reason": "reachability-gap: The plugin renders its detail row and cell as raw elements, so the current table-row and table-cell targets do not reach the panel wrapper" + "reason": "reachability-gap: The raw detail row and cell are not reached by the current table-row and table-cell targets" } }, "Footer section": {"target": "table-footer"} } ``` -The exact map records all eight current non-deprecated Table targets once. The -legacy `base-table` alias is compatibility, not anatomy. TableHeader, TableBody, -TableFooter, TableRow, TableCell, and TableHeaderCell retain direct docs linked by -`subComponentOf: 'Table'`; they do not need independent component specs for this -aggregate ownership. +The deprecated `base-table` alias is compatibility, not anatomy. Member docs keep `subComponentOf: 'Table'`; they do not become independent contract owners. ## Family and system relationships -- `architecture:component-theming-surface` owns anatomy qualification, exact - target mapping, delegation, inheritance, factual `none` classifications, and - the rule that deprecated aliases do not count as anatomy. -- `architecture:container-padding` owns the inherited inset protocol consumed by - the Scroll region and Cell edge compensation. This container-system - participation does not make Table a structural member of - `family:layout-regions`. -- `architecture:interaction-modality` owns shared keyboard and pointer modality; - Table and its plugins retain their current local interactions. -- `architecture:public-component-api` owns the stable props, components, hooks, - exports, and compatibility boundary; this documentation adds no API. -- `module:Table/useTableRowStatus` owns the row-status module's API, generated - anatomy, resolution and warning precedence, accessibility, performance, - migration, and evidence. Table owns only the shared plugin protocol and its - aggregate composition order. +- [Component theming](../../../../docs/architecture/component-theming-surface.md) owns target qualification and mapping; [container padding](../../../../docs/architecture/container-padding.md), [public API](../../../../docs/architecture/public-component-api.md), [interaction modality](../../../../docs/architecture/interaction-modality.md), and [knowledge contracts](../../../../docs/architecture/knowledge-contracts.md) own their shared boundaries. +- [The row-status module](./plugins/rowStatus/useTableRowStatus.spec.md) owns its API, generated anatomy, resolution, accessibility, migration, and evidence. Table owns only the common protocol and aggregate ordering. ## Verification map -| Contract | Verification | Representative states | Mutation or failure expectation | Audit section | -| ------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | -| FR1-FR3 | `Table.test.tsx` render, structure, children-mode, section, and target assertions | Data-driven rows, empty data, and children composition | Removing Table, Scroll region, or configured structure fails existing role, structure, class, or prop-forwarding assertions; Row and Cell are conditional on rendered data or caller composition. | `audit:Table/anatomy` | -| FR4 | CLI discovery and API target tests plus source inspection | Direct member docs and unfiltered/scoped target output | Duplicating a parent/member target or changing the canonical owner fails parent-aware discovery assertions; section target placement remains source-inspected. | `audit:Table/theming` | -| FR5 | `plugins/sortable/useTableSortable.test.tsx` plus source inspection | Unsorted, ascending, descending, and multi-sort | Removing the Sort control, conditional priority, or ARIA state fails existing assertions. Removing the Sort indicator glyph does not currently fail a focused test; glyph presence and delegated Icon target placement remain source-inspected. | `audit:Table/anatomy` | -| FR6 | `plugins/selection/useTableSelection.test.tsx` | Select all, individual, disabled, and non-selectable | Removing header/body Selection controls or row selection state fails existing structure, label, interaction, and ARIA assertions; delegated CheckboxInput target placement remains source-inspected. | `audit:Table/anatomy` | -| FR7 | `plugins/rowExpansion/useTableRowExpansion.test.tsx` plus source inspection | Collapsed, expanded, and non-expandable rows | Removing the Expansion control, conditional detail panel, captured-count `colSpan`, or ARIA state fails existing assertions in the covered plugin order; later column transforms, delegated Icon target placement, and untargeted panel wrappers remain source-inspected. | `audit:Table/anatomy` | -| FR8 | `Table.test.tsx` empty-state suite plus EmptyState public target metadata | Default, custom, disabled, and non-empty | Removing conditional empty behavior fails existing assertions; default EmptyState delegation remains source-inspected. | `audit:Table/anatomy` | -| FR9-FR11 | `Table.test.tsx`, `types.ts`, `BaseTable.tsx`, and `useBaseTablePlugins.ts` | Built-in plus known and custom named plugins; every transform phase | Sequential composition, base-before-user behavior, transform application, and slot output have focused coverage; complete known-name sorting, phase order, exception continuation, and context nesting remain source-inspected. | `audit:Table/plugins` | -| FR12, PR1 | `Table.perf.test.tsx` plus `useBaseTablePlugins.ts` source inspection | Same plugin references, recreated record, and changed plugin value | Unchanged plugin values must preserve the resolved array and representative no-op row-update budgets; focused named-record identity coverage remains partial. | `audit:Table/performance` | -| Module backlink | `scripts/check-knowledge.mjs` | Active parent and colocated module record | A missing, duplicate, mis-parented, wrong-kind, misnamed, or undiscovered module record fails knowledge validation. | `audit:Table/modules` | -| Theming anatomy map | `scripts/check-knowledge.mjs`, `themingTargets.test.ts`, and CLI parent-aware target discovery tests | Canonical anatomy, eight current targets, legacy alias | Missing, extra, duplicated, prefixed, stale, alias-backed, or independently owned member mappings fail repository validation or discovery coverage. | `audit:Table/theming` | +| Contract | Binding evidence and current limit | +| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| FR1-FR2 | [Table.test.tsx](./Table.test.tsx), [sortable](./plugins/sortable/useTableSortable.test.tsx), [selection](./plugins/selection/useTableSelection.test.tsx), and [expansion](./plugins/rowExpansion/useTableRowExpansion.test.tsx) cover structure, controls, state, and ARIA; delegated glyph/target placement remains source-inspected. | +| FR3-FR4, ORD1-ORD4 | [types.ts](./types.ts), [BaseTable.tsx](./BaseTable.tsx), [useBaseTablePlugins.ts](./useBaseTablePlugins.ts), and [Table.test.tsx](./Table.test.tsx) bind composition; complete known-name/phase/context order and exception continuation remain source-inspected. | +| FR5 | [useTableRowExpansion.tsx](./plugins/rowExpansion/useTableRowExpansion.tsx) and its [tests](./plugins/rowExpansion/useTableRowExpansion.test.tsx) bind captured-count behavior; later column transforms remain an uncovered stale-`colSpan` gap. | +| PR1-PR2 | [Table.perf.test.tsx](./Table.perf.test.tsx) binds representative no-op row budgets; named-record identity and module-specific resources retain their stated owners. | +| Target map | [Table.doc.mjs](./Table.doc.mjs), [check-knowledge.mjs](../../../../scripts/check-knowledge.mjs), and the target tests in `verified_by` bind parent ownership; section target placement remains source-inspected. | +| Module backlink | [check-knowledge.mjs](../../../../scripts/check-knowledge.mjs) rejects a missing, duplicate, mis-parented, misnamed, or undiscovered module record. | ## Decision log -None. This current contract records existing Table facts, approved canonical parent -ownership, and the shared plugin protocol without introducing a component-local -visual or runtime change. +None. This current record preserves existing aggregate ownership and shared plugin behavior without adding a component-local decision. ## Open questions @@ -319,7 +176,4 @@ None. ## Content boundary -This file does not duplicate consumer prop tables or examples, module-local API or -generated anatomy, plugin usage recipes, container-padding mechanics, shared -modality rules, current audit results, or implementation steps. It links to their -owners. +Consumer props/examples, module-local behavior, shared rules, audit results, and implementation detail remain with their linked owners. diff --git a/packages/core/src/Table/plugins/rowStatus/useTableRowStatus.spec.md b/packages/core/src/Table/plugins/rowStatus/useTableRowStatus.spec.md index 8600530a7d8ee..29f38d019bb7a 100644 --- a/packages/core/src/Table/plugins/rowStatus/useTableRowStatus.spec.md +++ b/packages/core/src/Table/plugins/rowStatus/useTableRowStatus.spec.md @@ -11,100 +11,47 @@ approved_at: 2026-09-01 owners: [cixzhang] review_triggers: [public-api, behavior, layout, theming, accessibility] verified_by: - [ - packages/core/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx, - scripts/check-knowledge.mjs, - ] + - packages/core/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx + - scripts/check-knowledge.mjs parent_component: component:Table references: - [ - component:Icon, - architecture:component-theming-surface, - architecture:icon-resolution-and-component-slots, - architecture:public-component-api, - spec:AST-002/DEC-6, - ] + - component:Icon + - architecture:component-theming-surface + - architecture:icon-resolution-and-component-slots + - architecture:public-component-api + - architecture:knowledge-contracts + - spec:AST-002/DEC-6 --- # useTableRowStatus module contract ## Intent -`useTableRowStatus` adds an optional generated status gutter that communicates one -outcome or caller-defined marker for each Table row. The module owns its public status -vocabulary, generated column and indicator anatomy, accessible naming, internal -signifier resolution, invalid-input fallback, performance obligations, and -implementation evidence. - -This current contract is authoritative for the module's intended behavior. Runtime, -public types, consumer docs, and release implementation remain explicitly pending -and MUST follow this contract when implemented. +`useTableRowStatus` adds an optional generated status gutter with one semantic outcome or caller-defined marker per Table row. This current contract owns that module behavior; runtime, public types, consumer docs, and implementation evidence remain pending and must conform when they land. ## Compatibility and migration -- Released default preserved: `yes`; every stable `0.5.2` custom marker keeps the - same source shape and rendered dot or caller-selected glyph. -- Compatibility class: additive semantic interface at the `getStatus` callback - boundary plus restoration of stable behavior before the next stable release. -- Migration decision: no stable-consumer codemod and no required stable-consumer - edit. Canary consumers that intentionally adopted the implicit semantic glyph - from [#5671](https://github.com/facebook/astryx/pull/5671) migrate manually from - `{color: 'error', label}` to `{status: 'error', label}`. - -Stable `0.5.2` already defines `{color, label}` as a caller-painted dot and -`{color, icon, label}` as a caller-selected glyph with caller-selected paint. The -exported `TableRowStatus` interface remains byte-for-byte unchanged so existing -annotations, interface extensions, and declaration merging remain valid. - -A value-based codemod would be unsafe because stable `{color: 'error', label}` may -intentionally mean an error-colored dot. If the semantic implementation is not -ready before a stable cut, restore the custom-marker behavior first and defer the -new semantic interface; the overloaded canary behavior MUST NOT become stable. - -This specification-only change carries no package Changeset. The implementation -pull request owns public exports, consumer docs, tests, release notes, and its -package-version Changeset. +- Released default preserved: `yes`; stable `0.5.2` `{color, label}` dots and `{color, icon, label}` caller-selected glyphs keep their source and behavior. +- Compatibility class: additive `TableSemanticRowStatus` at `getStatus`; exported `TableRowStatus` remains byte-for-byte unchanged. +- Migration decision: no stable-consumer edit or codemod. Canary adopters of [#5671](https://github.com/facebook/astryx/pull/5671)'s implicit glyph migrate manually from `color` to `status`. + +If semantic implementation misses the next stable cut, restore stable custom-marker behavior first. This documentation-only compaction changes no runtime, API, consumer docs, or package version and has no Changeset. ## Ownership boundary **Owns** -- The public `useTableRowStatus` hook, `UseTableRowStatusConfig`, unchanged - `TableRowStatus` custom-marker interface, and proposed - `TableSemanticRowStatus` interface. -- The exclusive result boundary of `getStatus`, including `null` for no marker. -- The generated fixed-width column, visually hidden header name, empty-cell - behavior, indicator DOM, and internal `icon | dot` variant resolution. -- Mapping semantic row status to a shared semantic Icon name and tone before - delegating glyph rendering. -- Runtime precedence and warning behavior for untyped semantic/custom conflicts. -- Row-status accessibility, performance constraints, migration, and evidence. +- The hook/config, unchanged custom interface, separate semantic interface, callback-only exclusive union, and `icon | dot` resolution. +- The 28px generated column, semantic mapping, invalid-input precedence, accessibility, migration, and implementation evidence. **Does not own / non-goals** -- Aggregate `TablePlugin` protocol, transform phases, named-plugin ordering, slot - composition, failure isolation, or plugin-array identity — owned by - `component:Table`. -- Icon artwork, Icon's target, or shared icon resolution — owned by - `component:Icon` and `architecture:icon-resolution-and-component-slots`. -- The active theme's semantic token values or concrete semantic artwork. The theme - supplies those through existing contracts; it does not select this module's - internal variant. -- Product-specific meaning for custom colors or custom icons. The caller owns that - meaning and supplies the required label. -- Shared design-feedback vocabulary. A Table row outcome is not authority for - feedback components. -- A public `variant` or `presentation` prop, extensible variant map, reflected - variant data attribute, or row-status theme selector axis. -- No `table-row-status` target is approved by this contract. - [#5754](https://github.com/facebook/astryx/pull/5754) remains outside this - contract and is held for a separate AST-002 proposal proving a theme-author - need, painter placement, and exact guarantees. +- Parent plugin protocol/order/identity — owned by [Table](../../Table.spec.md). +- Icon artwork/resolution/target, theme token values, or shared accessibility/platform evidence rules — owned by linked records. +- Public `variant`/`presentation`, reflected variant axis, component-icon slot, direct `table-row-status` target, product meaning for custom paint or glyph, or shared design-feedback vocabulary; a Table row outcome is not feedback-component authority. ## Public API and concepts -The proposed public interfaces and callback boundary are additive: - ```ts export interface TableRowStatus { color: TableRowStatusColor | (string & {}); @@ -126,229 +73,98 @@ export interface UseTableRowStatusConfig> { } ``` -`TableRowStatus` remains byte-for-byte the existing exported interface. The -exclusive union exists only at the callback boundary, where its custom member is -intersected with `{status?: never}`. This preserves the existing interface for -annotations, extension, and declaration merging without allowing a supported -semantic status to mix with custom marker inputs. `TableRowStatusColor` and the -existing `string & {}` CSS-color escape hatch remain unchanged; this contract does -not newly export the color type. - -| Concept | Closed values or states | Meaning | Default | Owner | Stability | -| ------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------ | ---------------------------- | -------------------------------- | --------------------------------------- | -| Row-status presence | Either public interface or `null` | Adds one indicator for the row or leaves the generated cell empty. | `null` means absent | `module:Table/useTableRowStatus` | Stable presence; additive semantic form | -| Semantic outcome | `success`, `warning`, `error` | Selects one cohesive system-owned outcome whose glyph and tone are derived together. | No implicit outcome | `module:Table/useTableRowStatus` | Additive proposal | -| Custom marker paint | Existing Table row-status colors or a CSS color | Selects paint only; no color value implies a semantic outcome or representation. | Required on `TableRowStatus` | Caller through this module | Stable `0.5.2` | -| Custom marker glyph | `IconName` or absent | Selects the caller-owned glyph; absence selects a dot. | Absent means dot | Caller through this module | Stable `0.5.2` | -| Label | Required string | Names the indicator and supplies supplemental tooltip text. | Required | Caller through this module | Stable | -| Resolved variant | Internal `icon` or `dot` | Names the signifier anatomy derived from the accepted result, not a caller choice. | Derived | `module:Table/useTableRowStatus` | Internal | - -Consumer docs own final import syntax, reference tables, and examples when the -implementation lands. +`TableRowStatusColor` and its `string & {}` escape hatch remain unchanged and are not newly exported. The standalone interface stays extendable and augmentation-compatible; exclusivity exists only at `getStatus`. + +| Concept | Contract | +| ---------------- | ---------------------------------------------------------------------------------------------------- | +| Semantic outcome | `success`, `warning`, or `error` derives one matching shared glyph and semantic tone. | +| Custom marker | `color` controls paint only; absent `icon` means dot and present `icon` means caller-selected glyph. | +| Resolved variant | Internal `icon` or `dot`, derived by the module and never caller- or theme-selected. | +| Label/presence | Required `label` names every marker; `null` leaves the generated cell empty. | ## Behavioral contract -| ID | Invariant | Basis | Implementation/evidence state | -| ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | --------------------------------------------------------------- | -| FR1 | Given a referentially stable `getStatus`, `useTableRowStatus` returns one stable `TablePlugin` that contributes a `transformColumns` function to the parent pipeline. | Current source and stable behavior | Verified baseline; semantic branch pending. | -| FR2 | At its parent-owned transform position, the module prepends one fixed 28 CSS-pixel, non-resizable generated column. The visually blank header has a localized accessible name; `null` leaves that row's generated cell empty. | Stable `0.5.2` behavior | Owner-directed module contract; composition evidence pending. | -| FR3 | `TableRowStatus` remains byte-for-byte the exported stable `{color, icon?, label}` interface. `color` always controls paint only. An absent `icon` resolves to the existing dot; a present `icon` resolves to the caller-selected glyph with caller-selected paint, including when the color string is `success`, `warning`, or `error`. | Stable `0.5.2` compatibility and owner direction | Owner-directed; current canary overload must be reverted. | -| FR4 | `TableSemanticRowStatus` accepts only `success`, `warning`, or `error` plus required `label`. Each status resolves to the matching shared semantic Icon name and semantic tone from the active theme. Caller `color` and `icon` are forbidden. | Owner direction, 2026-09-01; Icon resolution contract | Owner-directed; implementation and theme substitution pending. | -| FR5 | Internal variant resolves to `icon` for every semantic status, `dot` for custom color without icon, and `icon` for custom color with icon. Variant is internal/anatomy terminology only. | Owner direction, 2026-09-01 | Owner-directed; no public or theme exposure approved. | -| FR6 | Only `getStatus` forms the exclusive callback union shown above, intersecting the custom interface with `status?: never`. Typed callers cannot provide a supported `status` together with `color` or `icon`; the standalone stable interface is not narrowed. | Owner direction and AST-002 FR15-FR16 | Type shape verified independently; repository fixtures pending. | -| FR7 | For an untyped object with a supported `status` plus `color` and/or `icon`, semantic status wins, custom fields are ignored, and development emits at most one warning for the loaded row-status module instance. Production emits no warning and renders the same semantic result. | Owner direction, 2026-09-01 | Owner-directed; runtime coverage pending. | -| FR8 | `label` remains required on both interfaces. The outer indicator exposes one accessible image name; a nested Icon is decorative. Tooltip text is supplemental and not the sole communication path. | Stable behavior and owner direction | Naming is owner-directed; browser and AT evidence pending. | -| FR9 | The semantic interface supplies a non-color cue through distinguishable themed glyphs. The custom dot makes no promise that caller-selected paint is a shared semantic outcome or an independently non-color cue. | Owner direction and accessibility boundary | Owner-directed; theme and forced-colors evidence pending. | -| FR10 | The module composes through the parent `TablePlugin` protocol and does not redefine or depend on a private second transform order. It prepends to the columns received at its resolved position and remains valid with supported selection, expansion, grouping, empty-data, and custom-plugin combinations. | Parent protocol and owner direction | Composition matrix pending. | +| ID | Invariant | +| ---- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| FR1 | Stable `getStatus` returns one stable `TablePlugin` contributing only `transformColumns`. | +| FR2 | At its parent-owned position, the module prepends one fixed 28 CSS-pixel, non-resizable column with a localized visually hidden header name; `null` renders an empty cell. | +| FR3 | Stable custom `color` is paint-only, including `success`, `warning`, and `error`; no `icon` renders the dot and explicit `icon` renders that glyph with caller-selected paint. | +| FR4 | Semantic status derives the matching shared Icon name and active-theme semantic tone; custom `color` and `icon` are forbidden. | +| FR5 | Internal variant is `icon` for semantic status or explicit custom icon and `dot` for custom color without icon; it is not public or theme API. | +| FR6 | Only the callback boundary intersects the custom branch with `{status?: never}`; the exported stable interface is not narrowed. | +| FR7 | For untyped supported `status` plus `color` and/or `icon`, status wins, custom fields are ignored, development warns once per loaded module instance, and production renders identically without warning. | +| FR8 | The outer indicator has one accessible image name from `label`; nested Icon is decorative and tooltip text is supplemental. | +| FR9 | Semantic statuses provide distinguishable glyphs as a non-color cue. A custom dot promises paint and a programmatic label, not a shared semantic outcome or visual non-color cue. | +| FR10 | The module prepends to columns received at its resolved parent position and composes with selection, expansion, grouping, empty data, and custom plugins without defining or depending on another order. | ### Transformation and precedence order -- **ORD1 — Result presence.** `null` produces no indicator. A non-null result moves - to branch resolution. -- **ORD2 — Semantic branch.** A supported `status` selects semantic outcome before - custom-marker resolution, deriving matching tone and shared glyph together. -- **ORD3 — Invalid semantic/custom mixture.** When untyped input also carries - `color` or `icon`, ignore those fields. In development, a module-level dedupe - emits only the first warning for the loaded module instance; repeated rows and - renders do not spam the console. Production renders the semantic result without - warning. -- **ORD4 — Custom branch.** Without supported semantic status, custom `color` - selects paint; `icon` presence selects caller glyph versus dot. -- **ORD5 — Icon delegation.** The module selects a shared semantic Icon name, then - the active theme's existing shared registry selects artwork. The theme does not - select internal variant. -- **ORD6 — Parent pipeline.** The module prepends its column at the transform - position supplied by `component:Table`; the parent owns all relative ordering - and later render phases. +- `null` means no marker; supported `status` resolves before custom fields. Invalid untyped mixtures keep the semantic result and deduped warning; otherwise custom `color` chooses paint and `icon` presence chooses glyph or dot. +- Semantic names use shared Icon/theme resolution. The theme supplies artwork and tokens, not internal variant. ### Performance and resources -- **PR1 — Constant row work.** Result and variant resolution perform constant work - per rendered row and add no layout measurement, listener, observer, timer, or - asynchronous resource. -- **PR2 — Stable identity.** A referentially stable `getStatus` preserves the - module's `TablePlugin` identity. Adding semantic resolution MUST NOT make - unchanged rows rerender solely because the branch moved from `color` to - `status`. -- **PR3 — Existing theme path.** Semantic glyph and tone resolution use existing - synchronous Icon/theme paths rather than a parallel registry or per-row theme - subscription. -- **PR4 — Warning bound.** Invalid-input diagnostics retain only one module-level - boolean-equivalent dedupe state and emit at most once per loaded module instance. - -Current measurements belong in audit evidence. Implementation verification must -add semantic/custom cases to representative Table render and no-op update budgets. +- **PR1 — Constant row work.** Resolution is constant per row and adds no measurement, listener, observer, timer, or asynchronous resource. +- **PR2 — Stable identity.** Stable `getStatus` preserves plugin identity; semantic resolution MUST NOT rerender unchanged rows solely because input moved from `color` to `status`. +- **PR3 — Existing theme path.** Semantic glyph/tone resolution uses the synchronous shared Icon/theme path, not a parallel registry or per-row subscription. +- **PR4 — Warning bound.** Diagnostics retain only module-level boolean-equivalent dedupe state and warn at most once per loaded module instance. ## Accessibility contract -- **AR1 — Named gutter.** The visually blank generated column header MUST keep one - localized accessible name identifying the row-status column. -- **AR2 — One indicator name.** Every non-null result MUST expose exactly one - accessible image name from required `label`; any nested Icon remains decorative. -- **AR3 — Semantic non-color cue.** Semantic outcomes MUST use distinguishable - shared glyphs as well as semantic tone across supported themes and forced-colors - behavior. -- **AR4 — Honest custom contract.** A custom dot remains a caller-selected - paint-only marker. Required `label` supplies its programmatic name, but the module - MUST NOT claim that the dot itself communicates custom meaning without color. -- **AR5 — Supplemental tooltip.** Tooltip may repeat `label` as pointer help, but - the indicator's accessible name MUST remain sufficient without opening it. The - current indicator is not focusable, so tooltip presence is not keyboard evidence. -- **AR6 — Row context.** Assistive-technology verification MUST confirm the named - status is encountered in its row context without duplicate announcements. +- **AR1 — Named gutter.** The visually blank generated column header MUST keep one localized accessible name. +- **AR2 — One indicator name.** Every non-null result MUST expose exactly one image name from `label`; nested Icon remains decorative. +- **AR3 — Semantic non-color cue.** Semantic outcomes MUST use distinguishable shared glyphs plus tone across supported themes and forced-colors behavior. +- **AR4 — Honest custom contract.** A custom dot promises caller-selected paint and a programmatic label, not visual meaning without color. +- **AR5 — Supplemental tooltip.** Tooltip may repeat `label`, but the indicator name MUST suffice without opening it; the current non-focusable tooltip is not keyboard evidence. +- **AR6 — Row context.** AT verification MUST encounter the named status in row context without duplicate announcements. + +Browser, forced-colors, tooltip-modality, and AT evidence follow the shared [Accessibility Checklist](https://github.com/facebook/astryx/wiki/Accessibility-Checklist) and remain pending with implementation. ## Design relationships -| Anatomy or state | Design requirement | Representation authority | Module contract | -| ----------------------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------- | -------------------- | -| Row-status column | Keeps optional row outcomes aligned in one narrow generated gutter. | This module; relative column order belongs to `component:Table`. | FR1, FR2, FR10, AR1 | -| Row-status indicator | Owns the row's accessible status name and contains one resolved signifier. | `module:Table/useTableRowStatus` | FR3-FR9, AR2-AR6 | -| `icon` signifier | Uses a semantic or caller-selected glyph through Icon. | This module selects meaning; `component:Icon` renders. | FR3-FR5, AR2, AR3 | -| `dot` signifier | Uses the stable custom paint-only dot. | This module with caller-selected paint. | FR3, FR5, AR4 | -| Invalid untyped mixture | Preserves cohesive semantic outcome without combining custom axes or flooding diagnostics. | `module:Table/useTableRowStatus` | FR6, FR7, ORD2, ORD3 | +| Anatomy/state | Representation authority | +| ------------------------------------ | -------------------------------------------------------------------------- | +| Generated column and outer indicator | This module; relative order belongs to `component:Table`. | +| Semantic `icon` | This module selects status meaning; `component:Icon` renders the glyph. | +| Custom `icon` | Caller selects glyph and meaning; this module delegates rendering to Icon. | +| Custom `dot` | This module renders caller-selected paint only. | +| Invalid untyped mixture | This module preserves semantic cohesion and bounds diagnostics. | -No direct row-status target or variant selector is approved. Semantic icon rendering -delegates to the existing `icon` target. The custom dot has no direct public target. -Because this specification PR does not change consumer anatomy metadata, it does -not add an `anatomy-theming:v1` block. A later direct target proposal requires its -own AST-002 review and synchronized consumer anatomy. +No direct target or variant selector is approved, so this record has no `anatomy-theming:v1` block. [#5754](https://github.com/facebook/astryx/pull/5754) remains held outside this contract; any direct target needs a separate AST-002 proposal proving theme-author need, painter placement, and exact guarantees. ## Parent and system relationships -- `component:Table` owns the aggregate `TablePlugin` transform protocol, - base-before-user ordering, canonical named-plugin order, slot composition, - failure isolation, context nesting, and plugin-array identity. This module owns - only the row-status contribution and MUST NOT duplicate or override that order. -- `component:Icon` owns glyph presentation, size, color, target, and decorative - versus labelled Icon semantics. The row-status indicator keeps the outer name. -- `architecture:icon-resolution-and-component-slots` owns shared semantic-key - resolution. This module selects `success`, `warning`, or `error`; it creates no - Table-specific component-icon slot. -- `architecture:component-theming-surface` owns target qualification and requires a - separate decision before any `table-row-status`, `variant`, or `presentation` - selector is public. -- `architecture:public-component-api` and `spec:AST-002/DEC-6` require the stable - custom interface, additive semantic interface, and callback-only exclusive - boundary to keep each public input's responsibility coherent. +- [Table](../../Table.spec.md) owns the shared plugin protocol and aggregate order. +- [Icon](../../../Icon/Icon.spec.md) and [icon resolution](../../../../../../docs/architecture/icon-resolution-and-component-slots.md) own glyph presentation and shared-name artwork resolution; this module creates no Table-specific slot. +- [AST-002 DEC-6](../../../../../../docs/specs/AST-002/spec.md#dec-6--public-inputs-keep-one-semantic-responsibility) and [public API](../../../../../../docs/architecture/public-component-api.md) own input coherence and released compatibility. +- [Component theming](../../../../../../docs/architecture/component-theming-surface.md) owns target admission; [knowledge contracts](../../../../../../docs/architecture/knowledge-contracts.md) own this module/parent split and current authority. ## Verification map -| Contract | Verification | Representative states | Mutation or failure expectation | -| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| FR1, FR2, FR10 | Extend `useTableRowStatus.test.tsx` with fixed column metadata, `null`, empty data, grouped rows, selection/expansion combinations, and parent-order permutations. | Module alone and with supported first-party/custom plugins. | The column drops, duplicates, resizes, loses its header name, or assumes a second/private plugin order. | -| FR3, FR4, FR6 | Add public-subpath compile fixtures for the unchanged interface, declaration merging and extension, semantic interface, callback union, missing labels, unknown statuses, and forbidden mixtures. | Stable custom annotations/extensions; all semantic statuses. | `TableRowStatus` changes or stops merging, semantic usage is inaccessible, or supported status mixes with custom fields. | -| FR3-FR5 | Extend row-status runtime tests with semantic-looking custom colors, raw/palette dots, explicit custom icons, all semantic statuses, and active-theme substitution. | Custom dot, custom icon, and three semantic outcomes. | Custom color selects an implicit glyph, semantic status bypasses shared resolution, or variant becomes caller/theme input. | -| FR7, ORD2-ORD4, PR4 | Runtime tests under development and production modes with repeated mixed objects, rows, and renders. | `status + color`, `status + icon`, and all three fields. | Custom input overrides supported status, forbidden fields affect output, production warns, or development warns more than once per loaded module instance. | -| AR1-AR6 | Existing role/name tests plus browser axe, forced-colors, tooltip modality, and VoiceOver table-navigation evidence. | Semantic glyphs, custom icon, custom dot, `null`, light/dark, and every shipped theme. | Header or indicator loses its name, nested glyph is announced twice, glyph distinctions disappear, tooltip becomes sole meaning, or row context duplicates status. | -| PR1-PR3 | Extend `Table.perf.test.tsx` with semantic/custom initial render and no-op update cases. | Stable rerender, mixed rows, and theme substitution. | Resolution adds measurement/resources, creates a parallel registry, or exceeds existing representative Table budgets. | -| Structural ownership | `scripts/check-knowledge.mjs`. | Colocated module, canonical filename/id, and parent backlink. | A missing, duplicate, orphaned, mis-parented, misnamed, or undiscovered record passes validation. | -| Public hygiene and scope | Prettier, `check:knowledge`, checker tests, `check:repo`, changed-file scan, and public-content scan. | Parent backlink plus one module specification; no Changeset. | Runtime/docs files, private context, invalid record shape, or unrelated changes enter this contract proposal. | - -Implementation and browser/assistive-technology evidence are pending. Passing this -specification PR's checks proves record integrity only, not future runtime behavior. +| Contract | Binding implementation evidence | +| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| FR1-FR2, FR10 | Extend [useTableRowStatus.test.tsx](./useTableRowStatus.test.tsx) for fixed metadata, `null`, empty/grouped rows, selection/expansion, and parent-order permutations. | +| FR3-FR6 | Add public-subpath compile fixtures for unchanged interface extension/merging, semantic exports, callback exclusivity, required labels, unknown statuses, and forbidden mixtures. | +| FR3-FR5 | Extend [useTableRowStatus.test.tsx](./useTableRowStatus.test.tsx) for semantic-looking custom colors, raw/palette dots, custom icons, all semantic statuses, and theme substitution. | +| FR7, precedence, PR4 | Test repeated mixed objects in development and production: status wins, custom fields do not affect output, and only development warns once per loaded module. | +| AR1-AR6 | Keep current role/name assertions, then add browser axe, forced-colors, tooltip-modality, and VoiceOver row-navigation evidence across semantic glyphs, custom glyph/dot, `null`, light/dark, and shipped themes. | +| PR1-PR3 | Extend [Table.perf.test.tsx](../../Table.perf.test.tsx) with semantic/custom initial and no-op updates; fail on new resources, a parallel registry, or representative budget regression. | +| Ownership | Current [source](./useTableRowStatus.tsx) and [tests](./useTableRowStatus.test.tsx) prove the canary baseline, including the color overload FR3/DEC-3 require reverting; they do not prove the approved target behavior. [check-knowledge.mjs](../../../../../../scripts/check-knowledge.mjs) binds record identity. Implementation and browser/AT evidence remain pending. | ## Decision log -These entries record the approved owner direction. Runtime implementation and its -evidence remain pending. - -### DEC-1 — Preserve the custom interface; union only at the callback boundary - -**Reference:** `module:Table/useTableRowStatus/DEC-1` -**Direction owner:** cixzhang, 2026-09-01 - -The exported `TableRowStatus` interface remains byte-for-byte the stable custom -marker contract so existing annotations, extensions, and declaration merging keep -working. A separate exported `TableSemanticRowStatus` interface owns semantic -outcomes. Only `getStatus` forms their exclusive union by intersecting the custom -interface with `{status?: never}`. - -Rejected: replacing `TableRowStatus` with a union alias or adding semantic fields to -it. Either choice changes the extension surface stable consumers already use. - -### DEC-2 — Derive variant and delegate semantic artwork - -**Reference:** `module:Table/useTableRowStatus/DEC-2` -**Direction owner:** cixzhang, 2026-09-01 - -The module derives internal `icon | dot` variant from semantic versus custom intent. -Semantic status selects the matching shared Icon name and tone; the active theme -supplies concrete artwork and token values. Custom color keeps caller-owned paint -and optional glyph. - -Rejected: public `variant` or `presentation`, semantic caller overrides, a -Table-specific semantic icon registry, and exposing the derived branch as a theme -axis or data-attribute guarantee. - -### DEC-3 — Stable behavior wins over the canary overload - -**Reference:** `module:Table/useTableRowStatus/DEC-3` -**Direction owner:** cixzhang, 2026-09-01 - -Stable `0.5.2` custom-marker behavior is preserved without a codemod. The canary -behavior introduced by [#5671](https://github.com/facebook/astryx/pull/5671), where -semantic-looking `color` values choose icons, must be reverted before stable. -Canary adopters of that implicit glyph migrate manually to `status`. - -Rejected: changing stable custom colors, guessing semantic intent with a codemod, -or allowing the overload to reach stable while semantic implementation is -unfinished. - -### DEC-4 — Direct row-status theming is not approved - -**Reference:** `module:Table/useTableRowStatus/DEC-4` -**Direction owner:** cixzhang, 2026-09-01 - -The semantic interface uses Icon's existing target, registry, and theme-owned -artwork and tone. This contract records generated anatomy but does not add -`table-row-status`, `presentation`, `variant`, or an equivalent selector surface. - -Owner direction holds [#5754](https://github.com/facebook/astryx/pull/5754) -as written. A future direct target requires separate AST-002 review after painter -placement, theme-author need, and exact property guarantees are settled. - -### DEC-5 — Supported semantic status wins invalid untyped mixtures - -**Reference:** `module:Table/useTableRowStatus/DEC-5` -**Direction owner:** cixzhang, 2026-09-01 - -For untyped input containing a supported `status` plus `color` and/or `icon`, resolve -the semantic branch, ignore custom fields, and emit one development warning -deduplicated for the loaded row-status module instance. Production renders the same -semantic result without warning. - -Rejected: allowing custom fields to override semantic intent, combining axes, or -warning once per row or render and flooding the console. +| Decision | Approved direction | +| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `module:Table/useTableRowStatus/DEC-1` | **Preserve the custom interface; union only at the callback boundary.** Owner cixzhang, 2026-09-01. Keep `TableRowStatus` byte-for-byte stable and extendable; export the semantic interface and form the exclusive union only in `getStatus`. | +| `module:Table/useTableRowStatus/DEC-2` | **Derive variant and delegate semantic artwork.** Owner cixzhang, 2026-09-01. Derive internal `icon` or `dot`; status selects shared meaning/tone, theme supplies artwork/tokens, and custom markers keep caller paint/glyph. | +| `module:Table/useTableRowStatus/DEC-3` | **Stable behavior wins over the canary overload.** Owner cixzhang, 2026-09-01. Preserve stable `0.5.2` markers without codemod; revert #5671's color overload before stable and migrate only canary adopters. | +| `module:Table/useTableRowStatus/DEC-4` | **Direct row-status theming is not approved.** Owner cixzhang, 2026-09-01. Use Icon's existing target/theme paths and hold #5754 until a separate AST-002 proposal justifies a target and guarantees. | +| `module:Table/useTableRowStatus/DEC-5` | **Supported semantic status wins invalid untyped mixtures.** Owner cixzhang, 2026-09-01. Resolve status, ignore custom fields, warn once per loaded module in development, and render identically without warning in production. | ## Open questions -None. The owner approved every module-local judgment in this contract. -Implementation and its evidence remain pending. +None. All module-local judgments are approved; implementation and evidence remain pending. ## Content boundary -This record does not duplicate consumer import/reference docs, the parent Table -plugin protocol or ordering, shared Icon/theme algorithms, shared feedback -language, current audit results, or implementation steps. Runtime code, consumer -docs, release notes, Changeset, and final browser/assistive-technology evidence -land separately as implementation of this approved contract. +Consumer syntax/examples, parent protocol, shared Icon/theme/accessibility rules, audit history, and implementation steps remain with linked owners.