Skip to content

Versioned docs-examples & version-switch correctness in runner#61

Merged
demtario merged 17 commits into
masterfrom
feature/DEV-2099_Versioned-docs-examples
Jul 20, 2026
Merged

Versioned docs-examples & version-switch correctness in runner#61
demtario merged 17 commits into
masterfrom
feature/DEV-2099_Versioned-docs-examples

Conversation

@demtario

@demtario demtario commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Aggregate branch for a following PRs:


Note

Medium Risk
Large generated asset churn and CI that writes PRs can affect authoring previews and version switching if buckets or manifests are wrong; runtime app logic risk is lower than data/sync correctness.

Overview
Introduces versioned documentation examples for the demo runner: guide snippets from the main handsontable repo are imported into apps/authoring/public/docs-examples/ as per-version buckets (e.g. 18.0, next) with manifests and lazy-loadable CatalogEntry JSON, so ?docs= URLs can resolve the right example for the selected Handsontable version.

A new GitHub Actions workflow (Import versioned docs examples) builds a matrix from npm latest plus develop (or a repository_dispatch docs branch), runs pipeline/import-docs.mjs, uploads artifacts, and opens an automated PR to commit the generated buckets.

Alongside the bulk regenerated example payloads, the diff includes minor supporting changes: disable Astro dev toolbar in the Astro example (avoids 504s through the Tier-2 preview proxy), ignore runner/docs/reports/, and content tweaks in imported examples (e.g. accessibility demo checkbox copy/links, context menu separator style in Angular).

Reviewed by Cursor Bugbot for commit 36620d1. Bugbot is set up for automated code reviews on this repo. Configure here.

@demtario demtario self-assigned this Jul 17, 2026
Generate release and next buckets in CI while pinning imported dependencies to concrete npm versions for reproducible snapshots.
@demtario
demtario force-pushed the feature/DEV-2099_Versioned-docs-examples branch from 48504ec to f5ebd88 Compare July 17, 2026 10:38
demtario and others added 6 commits July 17, 2026 12:47
…e-ci

feat(docs): automate versioned example imports
…-css-link

fix(runtime): sync CDN CSS version
chore(docs): update versioned docs examples
Reload clean docs examples from the selected version while preserving edited work with a compatibility warning.
demtario and others added 8 commits July 17, 2026 18:39
… guard

Empirically verifies every starter against Handsontable majors 15-19
against a live instance (DEV-2102 / ADR-0021 decision 10), opt-in via
`pnpm e2e:matrix`. Findings (docs/reports/starter-matrix-2026-07-17.md):
5 React-UI-lib container starters and Angular break at older majors,
Astro fails version-independently (infra issue, not compatibility).

Adds a DEFAULT_MIN_MAJOR=15 floor to validateHandsontableVersion,
mirroring the existing ceiling — previously only GET /api/versions
enforced the 15 floor, so a direct session/API call bypassing the
version dropdown could still request an untested major.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A dated matrix run is a one-off result, not living documentation —
mirrors why test-results/ is already gitignored. Paste the report
script's stdout into the ticket/PR instead of committing the file.
…pat-matrix

Add starter compatibility matrix e2e + version.ts floor guard
…e-example-picker

DEV-2104: make docs examples bucket-aware
Requires bucket-aware generation and client loading before merge.
…-docs-examples

DEV-2100: remove legacy docs-example snapshot
Astro's dev toolbar client entrypoint intermittently 504s through the
Tier-2 proxy on revalidation fetches, breaking the astro starter's
preview at every Handsontable major (15-18). The toolbar has no use
inside an embedded preview iframe anyway.

Confirmed via live Network-tab capture on a real Tier-2 astro session
(request disappears entirely once disabled) and a local astro-scoped
run of e2e/starter-matrix.spec.ts (15/16/17/18 all pass).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…-preview

fix(astro): disable dev toolbar to stop Tier-2 preview 504s (DEV-2109)
@demtario
demtario marked this pull request as ready for review July 20, 2026 09:26
@demtario
demtario merged commit 0c368f9 into master Jul 20, 2026
1 check passed
@demtario
demtario deleted the feature/DEV-2099_Versioned-docs-examples branch July 20, 2026 09:26

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 36620d1. Configure here.

@@ -0,0 +1 @@
{"framework":"react","displayName":"Accessibility · Accessible data grid demo · React (TS)","tier":1,"engine":"sandpack","sandpackTemplate":"react-ts","sandpackEnvironment":"create-react-app-typescript","container":null,"htWrappers":["@handsontable/react-wrapper"],"entry":"/src/main.tsx","htmlEntry":"/index.html","devCommand":null,"buildCommand":"vite build","outputDir":"dist","outputGlob":null,"staticExport":false,"spaMode":false,"port":null,"installCommand":"pnpm install","htCoreRange":"18.0.0","fileCount":6,"assets":[],"skipped":[],"files":{"/package.json":"{\n \"name\": \"handsontable-react-example\",\n \"version\": \"1.0.0\",\n \"private\": true,\n \"packageManager\": \"pnpm@10.34.5\",\n \"dependencies\": {\n \"handsontable\": \"18.0.0\",\n \"@handsontable/react-wrapper\": \"18.0.0\",\n \"react\": \"18.x\",\n \"react-dom\": \"18.x\",\n \"vite\": \"^5.4.0\",\n \"@vitejs/plugin-react\": \"^4.0.0\"\n },\n \"scripts\": {\n \"start\": \"vite\",\n \"build\": \"vite build\"\n }\n}","/vite.config.js":"import { defineConfig } from \"vite\";\nimport react from \"@vitejs/plugin-react\";\n\nexport default defineConfig({ plugins: [react()] });","/index.html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Handsontable React Example</title>\n <link rel=\"stylesheet\" href=\"https://unpkg.com/handsontable@18.0.0/dist/handsontable.full.min.css\" />\n <style>body { padding: 1rem; font-family: system-ui, -apple-system, sans-serif; }</style>\n</head>\n<body>\n <div id=\"example2\"></div>\n <script type=\"module\" src=\"/src/main.tsx\"></script>\n</body>\n</html>","/src/main.tsx":"import React from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport \"./styles.css\";\nimport App from \"./App\";\nconst root = createRoot(document.getElementById(\"example2\"));\nroot.render(React.createElement(App));","/src/App.tsx":"import { useState } from 'react';\nimport { HotTable, HotColumn } from '@handsontable/react-wrapper';\nimport { registerAllModules } from 'handsontable/registry';\n\nregisterAllModules();\n\ninterface Toggle {\n tabNavigation: boolean;\n navigableHeaders: boolean;\n renderAllRows: boolean;\n renderAllColumns: boolean;\n enterBeginsEditing: boolean;\n autoWrapRow: boolean;\n autoWrapCol: boolean;\n enterMoves: { col: number; row: number };\n changeToggleOptions: any;\n}\n\ninterface Product {\n companyName: string;\n productName: string;\n sellDate: string;\n inStock: boolean;\n qty: number;\n orderId: string;\n country: string;\n}\n\n/* start:skip-in-preview */\nconst data: Product[] = [\n {\n companyName: 'Hodkiewicz - Hintz',\n productName: 'Rustic Soft Ball',\n sellDate: '2023-07-05',\n inStock: false,\n qty: 82,\n orderId: '16-3974628',\n country: 'United Kingdom',\n },\n {\n companyName: 'Rath LLC',\n productName: 'Small Frozen Tuna',\n sellDate: '2023-05-31',\n inStock: false,\n qty: 459,\n orderId: '77-7839351',\n country: 'Costa Rica',\n },\n {\n companyName: 'Reichert LLC',\n productName: 'Rustic Soft Ball',\n sellDate: '2023-03-16',\n inStock: false,\n qty: 318,\n orderId: '75-6343150',\n country: 'United States of America',\n },\n {\n companyName: 'Kozey Inc',\n productName: 'Sleek Wooden Bacon',\n sellDate: '2023-04-24',\n inStock: true,\n qty: 177,\n orderId: '56-3608689',\n country: 'Pitcairn Islands',\n },\n {\n companyName: 'Nader - Fritsch',\n productName: 'Awesome Wooden Hat',\n sellDate: '2023-04-29',\n inStock: true,\n qty: 51,\n orderId: '58-1204318',\n country: 'Argentina',\n },\n {\n companyName: 'Gerhold - Rowe',\n productName: 'Tasty Frozen Table',\n sellDate: '2023-03-27',\n inStock: false,\n qty: 439,\n orderId: '62-6066132',\n country: 'Senegal',\n },\n {\n companyName: 'Rath LLC',\n productName: 'Awesome Wooden Hat',\n sellDate: '2022-11-24',\n inStock: false,\n qty: 493,\n orderId: '76-7785471',\n country: 'Cyprus',\n },\n {\n companyName: 'Kozey Inc',\n productName: 'Rustic Soft Ball',\n sellDate: '2023-08-11',\n inStock: false,\n qty: 225,\n orderId: '34-3551159',\n country: 'Saint Martin',\n },\n {\n companyName: 'Hodkiewicz - Hintz',\n productName: 'Awesome Wooden Hat',\n sellDate: '2023-02-07',\n inStock: false,\n qty: 261,\n orderId: '77-1112514',\n country: 'Chile',\n },\n {\n companyName: 'Hegmann Inc',\n productName: 'Tasty Frozen Table',\n sellDate: '2023-05-06',\n inStock: false,\n qty: 439,\n orderId: '12-3252385',\n country: 'Switzerland',\n },\n {\n companyName: 'Weber Inc',\n productName: 'Awesome Wooden Hat',\n sellDate: '2023-04-22',\n inStock: true,\n qty: 235,\n orderId: '71-7639998',\n country: 'Brazil',\n },\n {\n companyName: 'Jacobi - Kutch',\n productName: 'Sleek Wooden Bacon',\n sellDate: '2022-12-13',\n inStock: true,\n qty: 163,\n orderId: '68-1588829',\n country: 'Burkina Faso',\n },\n {\n companyName: 'Jenkins LLC',\n productName: 'Small Rubber Shoes',\n sellDate: '2023-03-26',\n inStock: true,\n qty: 8,\n orderId: '61-6324553',\n country: 'Virgin Islands, U.S.',\n },\n {\n companyName: 'Koepp and Sons',\n productName: 'Sleek Wooden Bacon',\n sellDate: '2023-05-04',\n inStock: true,\n qty: 355,\n orderId: '74-6985005',\n country: 'Mozambique',\n },\n {\n companyName: 'Doyle Group',\n productName: 'Awesome Wooden Hat',\n sellDate: '2023-08-01',\n inStock: false,\n qty: 186,\n orderId: '84-4370131',\n country: 'Cocos (Keeling) Islands',\n },\n {\n companyName: 'Rempel - Durgan',\n productName: 'Tasty Frozen Table',\n sellDate: '2023-09-30',\n inStock: false,\n qty: 284,\n orderId: '13-6461825',\n country: 'Monaco',\n },\n {\n companyName: 'Lesch - Jakubowski',\n productName: 'Small Fresh Bacon',\n sellDate: '2023-09-26',\n inStock: true,\n qty: 492,\n orderId: '13-9465439',\n country: 'Iran',\n },\n {\n companyName: 'Jacobi - Kutch',\n productName: 'Rustic Cotton Ball',\n sellDate: '2023-05-04',\n inStock: true,\n qty: 300,\n orderId: '76-5194058',\n country: 'Indonesia',\n },\n {\n companyName: 'Gerhold - Rowe',\n productName: 'Rustic Cotton Ball',\n sellDate: '2023-07-07',\n inStock: true,\n qty: 493,\n orderId: '61-8600792',\n country: 'Norfolk Island',\n },\n {\n companyName: 'Johnston - Wisozk',\n productName: 'Small Fresh Fish',\n sellDate: '2023-07-14',\n inStock: false,\n qty: 304,\n orderId: '10-6007287',\n country: 'Romania',\n },\n {\n companyName: 'Gutkowski Inc',\n productName: 'Small Fresh Bacon',\n sellDate: '2023-01-10',\n inStock: true,\n qty: 375,\n orderId: '25-1164132',\n country: 'Afghanistan',\n },\n {\n companyName: 'Koepp and Sons',\n productName: 'Small Fresh Fish',\n sellDate: '2023-03-30',\n inStock: false,\n qty: 365,\n orderId: '75-7975820',\n country: 'Germany',\n },\n {\n companyName: 'Zboncak and Sons',\n productName: 'Small Fresh Fish',\n sellDate: '2023-08-17',\n inStock: false,\n qty: 308,\n orderId: '59-6251875',\n country: 'Tajikistan',\n },\n {\n companyName: 'Mills Group',\n productName: 'Rustic Soft Ball',\n sellDate: '2023-09-30',\n inStock: false,\n qty: 191,\n orderId: '67-7521441',\n country: 'Puerto Rico',\n },\n {\n companyName: 'Zboncak and Sons',\n productName: 'Awesome Wooden Hat',\n sellDate: '2023-03-18',\n inStock: false,\n qty: 208,\n orderId: '19-4264192',\n country: 'Bolivia',\n },\n {\n companyName: 'Rath LLC',\n productName: 'Rustic Soft Ball',\n sellDate: '2023-06-14',\n inStock: true,\n qty: 191,\n orderId: '78-5742060',\n country: 'Benin',\n },\n {\n companyName: 'Upton - Reichert',\n productName: 'Tasty Frozen Table',\n sellDate: '2023-02-27',\n inStock: false,\n qty: 45,\n orderId: '26-6191298',\n country: 'Tunisia',\n },\n {\n companyName: 'Carroll Group',\n productName: 'Rustic Soft Ball',\n sellDate: '2022-12-12',\n inStock: true,\n qty: 385,\n orderId: '13-7828353',\n country: 'Switzerland',\n },\n {\n companyName: 'Reichel Group',\n productName: 'Small Frozen Tuna',\n sellDate: '2022-12-12',\n inStock: true,\n qty: 117,\n orderId: '67-9643738',\n country: 'Mongolia',\n },\n {\n companyName: 'Kozey Inc',\n productName: 'Rustic Soft Ball',\n sellDate: '2023-03-24',\n inStock: false,\n qty: 335,\n orderId: '78-1331653',\n country: 'Angola',\n },\n {\n companyName: 'Brown LLC',\n productName: 'Small Rubber Shoes',\n sellDate: '2023-06-13',\n inStock: true,\n qty: 305,\n orderId: '63-2315723',\n country: 'Switzerland',\n },\n {\n companyName: 'Weber Inc',\n productName: 'Rustic Cotton Ball',\n sellDate: '2023-09-07',\n inStock: true,\n qty: 409,\n orderId: '53-6782557',\n country: 'Indonesia',\n },\n {\n companyName: 'OReilly LLC',\n productName: 'Tasty Frozen Table',\n sellDate: '2023-05-18',\n inStock: true,\n qty: 318,\n orderId: '91-7787675',\n country: 'Mayotte',\n },\n {\n companyName: 'Weber Inc',\n productName: 'Sleek Wooden Bacon',\n sellDate: '2023-04-20',\n inStock: false,\n qty: 234,\n orderId: '41-3560672',\n country: 'Switzerland',\n },\n {\n companyName: 'Hodkiewicz Inc',\n productName: 'Tasty Frozen Table',\n sellDate: '2023-10-19',\n inStock: true,\n qty: 136,\n orderId: '48-6028776',\n country: 'Peru',\n },\n {\n companyName: 'Lesch and Sons',\n productName: 'Rustic Cotton Ball',\n sellDate: '2023-09-29',\n inStock: false,\n qty: 187,\n orderId: '84-3770456',\n country: 'Central African Republic',\n },\n {\n companyName: 'Pouros - Brakus',\n productName: 'Small Frozen Tuna',\n sellDate: '2023-01-29',\n inStock: false,\n qty: 350,\n orderId: '08-4844950',\n country: 'Isle of Man',\n },\n {\n companyName: 'Batz - Rice',\n productName: 'Small Rubber Shoes',\n sellDate: '2023-11-06',\n inStock: false,\n qty: 252,\n orderId: '88-4899852',\n country: 'Burundi',\n },\n {\n companyName: 'Kub Inc',\n productName: 'Small Fresh Fish',\n sellDate: '2023-09-05',\n inStock: true,\n qty: 306,\n orderId: '06-5022461',\n country: 'Mauritius',\n },\n {\n companyName: 'Hills and Sons',\n productName: 'Small Frozen Tuna',\n sellDate: '2023-11-07',\n inStock: false,\n qty: 435,\n orderId: '99-5539911',\n country: 'Somalia',\n },\n {\n companyName: 'Shanahan - Boyle',\n productName: 'Small Frozen Tuna',\n sellDate: '2023-06-19',\n inStock: true,\n qty: 171,\n orderId: '82-8162453',\n country: 'Virgin Islands, U.S.',\n },\n {\n companyName: 'Luettgen Inc',\n productName: 'Awesome Wooden Hat',\n sellDate: '2023-09-30',\n inStock: false,\n qty: 6,\n orderId: '02-8118250',\n country: 'Colombia',\n },\n {\n companyName: 'Hegmann Inc',\n productName: 'Small Rubber Shoes',\n sellDate: '2023-02-16',\n inStock: true,\n qty: 278,\n orderId: '07-9773343',\n country: 'Central African Republic',\n },\n {\n companyName: 'Kub Inc',\n productName: 'Small Frozen Tuna',\n sellDate: '2023-08-08',\n inStock: false,\n qty: 264,\n orderId: '66-4470479',\n country: 'Norfolk Island',\n },\n {\n companyName: 'Kub Inc',\n productName: 'Tasty Frozen Table',\n sellDate: '2023-06-06',\n inStock: true,\n qty: 494,\n orderId: '13-1175339',\n country: 'Liechtenstein',\n },\n {\n companyName: 'Hahn - Welch',\n productName: 'Small Frozen Tuna',\n sellDate: '2023-06-12',\n inStock: false,\n qty: 485,\n orderId: '32-9127309',\n country: 'Bahrain',\n },\n {\n companyName: 'Nader - Fritsch',\n productName: 'Small Frozen Tuna',\n sellDate: '2023-04-08',\n inStock: true,\n qty: 332,\n orderId: '41-3774568',\n country: 'Montserrat',\n },\n {\n companyName: 'Crona and Sons',\n productName: 'Small Fresh Bacon',\n sellDate: '2023-06-21',\n inStock: true,\n qty: 104,\n orderId: '48-9995090',\n country: 'Syrian Arab Republic',\n },\n {\n companyName: 'Lind Group',\n productName: 'Rustic Cotton Ball',\n sellDate: '2023-08-17',\n inStock: false,\n qty: 51,\n orderId: '68-9599400',\n country: 'Czech Republic',\n },\n {\n companyName: 'Labadie LLC',\n productName: 'Small Fresh Bacon',\n sellDate: '2023-04-20',\n inStock: true,\n qty: 155,\n orderId: '52-4334332',\n country: 'Croatia',\n },\n {\n companyName: 'Doyle Group',\n productName: 'Sleek Wooden Bacon',\n sellDate: '2023-07-23',\n inStock: false,\n qty: 465,\n orderId: '63-8894526',\n country: 'Indonesia',\n },\n];\n\nconst countries = data.reduce<string[]>((acc, curr) => {\n if (acc.includes(curr.country)) {\n return acc;\n }\n\n return [...acc, curr.country];\n}, []);\n\n/* end:skip-in-preview */\n\n// Handsontable options\nconst hotOptions = {\n data,\n height: 464,\n colWidths: [160, 165, 130, 120, 100, 110, 216],\n autoRowSize: true,\n colHeaders: ['Company name', 'Product name', 'Sell date', 'In stock', 'Qty', 'Order ID', 'Country'],\n dropdownMenu: true,\n hiddenColumns: {\n indicators: true,\n },\n multiColumnSorting: true,\n filters: true,\n rowHeaders: true,\n manualRowMove: true,\n licenseKey: 'non-commercial-and-evaluation',\n};\n\nconst ExampleComponent = () => {\n const [toggleableOptions, setToggleableOptions] = useState<Omit<Toggle, 'changeToggleOptions'>>({\n tabNavigation: true,\n navigableHeaders: true,\n renderAllRows: false,\n renderAllColumns: false,\n enterBeginsEditing: true,\n autoWrapRow: true,\n autoWrapCol: true,\n headerClassName: 'htLeft',\n enterMoves: { col: 0, row: 1 },\n });\n\n return (\n <div className=\"example-container\">\n {/* DemoOptions component for changing Handsontable options */}\n <DemoOptions changeToggleOptions={setToggleableOptions} {...toggleableOptions} />\n\n <input className=\"placeholder-input\" type=\"text\" placeholder=\"Focusable text input\" />\n\n {/* Handsontable component with dynamic options */}\n <HotTable\n // Handsontable needs to reload when changing virtualization\n // by changing the key, we force the component to reload\n key={String(toggleableOptions.renderAllRows)}\n {...hotOptions}\n // Pass in the options which can change for demo\n {...toggleableOptions}\n >\n {/* Define HotColumns for the data */}\n <HotColumn data=\"companyName\" type=\"text\" />\n <HotColumn data=\"productName\" type=\"text\" />\n <HotColumn data=\"sellDate\" locale=\"en-GB\" dateFormat={{ day: '2-digit', month: '2-digit', year: 'numeric' }} type=\"intl-date\" />\n <HotColumn data=\"inStock\" type=\"checkbox\" className=\"htCenter\" headerClassName=\"htCenter\" />\n <HotColumn data=\"qty\" type=\"numeric\" headerClassName=\"htRight\" />\n <HotColumn data=\"orderId\" type=\"text\" />\n <HotColumn data=\"country\" type=\"dropdown\" source={countries} />\n </HotTable>\n <input className=\"placeholder-input\" type=\"text\" placeholder=\"Focusable text input\" />\n </div>\n );\n};\n\n// Demo Options allows you to change the Handsontable options\n// This allows us to change the Handsontable settings from the UI, showcasing\n// the flexibility of Handsontable in configuring according to your needs.\nfunction DemoOptions({\n tabNavigation,\n navigableHeaders,\n renderAllRows,\n renderAllColumns,\n enterBeginsEditing,\n autoWrapRow,\n autoWrapCol,\n enterMoves,\n changeToggleOptions,\n}: Toggle) {\n // on checkbox change, update handsontable option\n const handleCheckboxChange = (checkboxName: string) => {\n switch (checkboxName) {\n case 'enable-tab-navigation':\n changeToggleOptions((existing: any) => ({\n ...existing,\n tabNavigation: !tabNavigation,\n }));\n\n break;\n case 'enable-header-navigation':\n changeToggleOptions((existing: any) => ({\n ...existing,\n navigableHeaders: !navigableHeaders,\n }));\n\n break;\n case 'enable-cell-virtualization':\n changeToggleOptions((existing: any) => ({\n ...existing,\n renderAllRows: !renderAllRows,\n renderAllColumns: !renderAllColumns,\n }));\n\n break;\n case 'enable-cell-enter-editing':\n changeToggleOptions((existing: any) => ({\n ...existing,\n enterBeginsEditing: !enterBeginsEditing,\n }));\n\n break;\n case 'enable-arrow-rl-first-last-column':\n changeToggleOptions((existing: any) => ({\n ...existing,\n autoWrapRow: !autoWrapRow,\n }));\n\n break;\n case 'enable-arrow-td-first-last-column':\n changeToggleOptions((existing: any) => ({\n ...existing,\n autoWrapCol: !autoWrapCol,\n }));\n\n break;\n case 'enable-enter-focus-editing':\n changeToggleOptions((existing: any) => ({\n ...existing,\n enterMoves: enterMoves.row !== 1 ? { col: 0, row: 1 } : { col: 0, row: 0 },\n }));\n\n break;\n default:\n break;\n }\n };\n\n return (\n <>\n <div className=\"checkbox-container\">\n <div>\n <label className=\"option-label\" htmlFor=\"enable-tab-navigation\" id=\"tab-navigation-label\">\n <input\n checked={tabNavigation}\n type=\"checkbox\"\n id=\"enable-tab-navigation\"\n name=\"enable-tab-navigation\"\n aria-label=\"Enable navigation with the Tab key\"\n onChange={() => handleCheckboxChange('enable-tab-navigation')}\n />\n Enable navigation with the Tab key\n </label>\n <a\n href=\"https://handsontable.com/docs/react-data-grid/api/options/#tabnavigation\"\n target=\"_blank\"\n className=\"external-link\"\n rel=\"noopener noreferrer\"\n aria-label=\"Learn more enabling/disabling tab navigation (opens in a new window)\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" className=\"icon outbound\">\n <path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\" />\n <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\" />\n </svg>\n </a>\n </div>\n <div>\n <label className=\"option-label\" htmlFor=\"enable-header-navigation\" id=\"header-navigation-label\">\n <input\n checked={navigableHeaders}\n type=\"checkbox\"\n id=\"enable-header-navigation\"\n name=\"enable-header-navigation\"\n aria-labelledby=\"header-navigation-label\"\n onChange={() => handleCheckboxChange('enable-header-navigation')}\n />\n Enable navigation across headers\n </label>\n <a\n href=\"https://handsontable.com/docs/react-data-grid/api/options/#navigableheaders\"\n target=\"_blank\"\n className=\"external-link\"\n rel=\"noopener noreferrer\"\n aria-label=\"Learn more about enabling/disabling tab navigation across headers (opens in a new window)\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" className=\"icon outbound\">\n <path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\" />\n <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\" />\n </svg>\n </a>\n </div>\n <div>\n <label className=\"option-label\" htmlFor=\"enable-cell-virtualization\" id=\"cell-virtualization-label\">\n <input\n checked={!renderAllRows}\n type=\"checkbox\"\n id=\"enable-cell-virtualization\"\n name=\"enable-cell-virtualization\"\n aria-labelledby=\"cell-virtualization-label\"\n onChange={() => handleCheckboxChange('enable-cell-virtualization')}\n />\n Enable cells virtualization\n </label>\n <a\n href=\"https://handsontable.com/docs/react-data-grid/api/options/#renderAllRows\"\n target=\"_blank\"\n className=\"external-link\"\n rel=\"noopener noreferrer\"\n aria-label=\"Learn more about row virtualization (opens in a new window)\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" className=\"icon outbound\">\n <path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\" />\n <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\" />\n </svg>\n </a>\n </div>\n <div>\n <label className=\"option-label\" htmlFor=\"enable-cell-enter-editing\" id=\"cell-enter-editing-label\">\n <input\n checked={enterBeginsEditing}\n type=\"checkbox\"\n id=\"enable-cell-enter-editing\"\n name=\"enable-cell-enter-editing\"\n aria-labelledby=\"cell-enter-editing-label\"\n onChange={() => handleCheckboxChange('enable-cell-enter-editing')}\n />\n The Enter key begins cell editing\n </label>\n <a\n href=\"https://handsontable.com/docs/react-data-grid/api/options/#enterbeginsediting\"\n target=\"_blank\"\n className=\"external-link\"\n rel=\"noopener noreferrer\"\n aria-label=\"Learn more about Enter key cell editing (opens in a new window)\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" className=\"icon outbound\">\n <path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\" />\n <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\" />\n </svg>\n </a>\n </div>\n <div>\n <label className=\"option-label\" htmlFor=\"enable-arrow-rl-first-last-column\" id=\"arrow-rl-first-last-column-label\">\n <input\n checked={autoWrapRow}\n type=\"checkbox\"\n id=\"enable-arrow-rl-first-last-column\"\n name=\"enableArrowFirstLastColumn\"\n aria-labelledby=\"arrow-rl-first-last-column-label\"\n onChange={() => handleCheckboxChange('enable-arrow-rl-first-last-column')}\n />\n The right/left arrow keys move the focus to the first/last column\n </label>\n <a\n href=\"https://handsontable.com/docs/react-data-grid/api/options/#autowrapcol\"\n target=\"_blank\"\n className=\"external-link\"\n rel=\"noopener noreferrer\"\n aria-label=\"Learn more about right/left arrow key behavior (opens in a new window)\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" className=\"icon outbound\">\n <path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\" />\n <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\" />\n </svg>\n </a>\n </div>\n <div>\n <label className=\"option-label\" htmlFor=\"enable-arrow-td-first-last-column\" id=\"arrow-td-first-last-column-label\">\n <input\n checked={autoWrapCol}\n type=\"checkbox\"\n id=\"enable-arrow-td-first-last-column\"\n name=\"enable-arrow-td-first-last-column\"\n aria-labelledby=\"arrow-td-first-last-column-label\"\n onChange={() => handleCheckboxChange('enable-arrow-td-first-last-column')}\n />\n The up/down arrow keys move the focus to the first/last row\n </label>\n <a\n href=\"https://handsontable.com/docs/react-data-grid/api/options/#autowraprow\"\n target=\"_blank\"\n className=\"external-link\"\n rel=\"noopener noreferrer\"\n aria-label=\"Learn more about up/down arrow key behavior (opens in a new window)\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" className=\"icon outbound\">\n <path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\" />\n <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\" />\n </svg>\n </a>\n </div>\n <div>\n <label className=\"option-label\" htmlFor=\"enable-enter-focus-editing\" id=\"enter-focus-editing-label\">\n <input\n checked={enterMoves.row !== 0}\n type=\"checkbox\"\n id=\"enable-enter-focus-editing\"\n name=\"enable-enter-focus-editing\"\n aria-labelledby=\"enter-focus-editing-label\"\n onChange={() => handleCheckboxChange('enable-enter-focus-editing')}\n />\n The Enter key moves the focus after cell edition\n </label>\n <a\n href=\"https://handsontable.com/docs/react-data-grid/api/options/#entermoves\"\n target=\"_blank\"\n className=\"external-link\"\n rel=\"noopener noreferrer\"\n aria-label=\"Learn more about Enter key focus behavior (opens in a new window)\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" x=\"0px\" y=\"0px\" viewBox=\"0 0 100 100\" width=\"15\" height=\"15\" className=\"icon outbound\">\n <path fill=\"currentColor\" d=\"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z\" />\n <polygon fill=\"currentColor\" points=\"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9\" />\n </svg>\n </a>\n </div>\n </div>\n </>\n );\n}\n\nexport default ExampleComponent;","/src/styles.css":".checkbox-container {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n margin: 0 -1rem 0 !important;\n padding: 0 1rem 0.75rem;\n border-bottom: 1px solid var(--sl-color-gray-5, #e0e0e0);\n}\n\n.checkbox-container > div {\n display: flex;\n}\n\n.checkbox-container > div > label {\n display: flex;\n align-items: center;\n gap: 0.4rem;\n}\n\n.external-link {\n margin-left: 0.5rem;\n position: relative;\n top: 2px;\n color: var(--sl-color-text, #333333);\n}\n\n.external-link:hover {\n color: var(--sl-color-accent, #1A42E8);\n}\n\n.placeholder-input {\n max-width: 20rem;\n padding: 0.4rem 0.625rem;\n font-size: var(--sl-text-sm, 0.875rem);\n line-height: 1.25rem;\n color: var(--sl-color-text, #333333);\n background: none;\n border: 1px solid var(--sl-color-gray-5, #e0e0e0);\n border-radius: 0;\n outline: none;\n}\n\n.placeholder-input::placeholder {\n color: var(--sl-color-gray-3, #777777);\n}\n\n.placeholder-input:focus {\n border-color: var(--sl-color-accent, #1A42E8);\n}\n\n.option-label {\n color: var(--sl-color-text, #333333);\n font-size: var(--sl-text-sm, 0.875rem);\n}\n\n/*\n We want the focus to be around input and label, in order to achieve this,\n we remove focus from the input and add it to the label (wrapper in this case)\n we then use the :focus-within pseudo class plus native focus styles\n https://css-tricks.com/copy-the-browsers-native-focus-styles/\n*/\n.option-label:focus-within {\n outline: 5px auto Highlight;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\n.option-label > input:focus {\n outline: none;\n}\n\n.example-container {\n gap: 1rem;\n display: flex;\n flex-direction: column;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;\n}"},"docsPath":"guides/accessibility/accessibility/react/example2.tsx","breadcrumb":["Accessibility"],"guide":"guides/accessibility/accessibility/accessibility.md","guideTitle":"Accessibility","exampleId":"example2","exampleTitle":"Accessible data grid demo","docPermalink":"/accessibility","lang":"React (TS)"}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arrow wrap toggles wrong settings

Medium Severity

In the accessibility demo, the left/right and up/down arrow checkboxes now describe column vs row wrapping and link to autoWrapCol / autoWrapRow, but the handlers still flip autoWrapRow for the horizontal control and autoWrapCol for the vertical one. Toggling a checkbox changes the opposite Handsontable option from what the label and docs URL imply.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 36620d1. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant