feat: Modernize to React 19 + current toolchain (Vite 8, Vitest 4, ESLint 10, TypeScript 7) - #3194
feat: Modernize to React 19 + current toolchain (Vite 8, Vitest 4, ESLint 10, TypeScript 7)#3194Niznikr wants to merge 110 commits into
Conversation
- Upgrade React to v19, replace Webpack/Karma with Vite/Vitest - Add TypeScript configuration and tooling - Convert components to TypeScript functional components: - Button, Badge, Spinner, Card, Alert - Checkbox, Textarea, Icon, IconSettings, UtilityIcon - Migrate Storybook to v8 with Vite builder - Convert stories to Component Story Format (CSF) - Add dark mode support with storybook-dark-mode addon - Update design-tokens to ES modules - Add type declarations for components still in JSX - Create MODERNIZATION_PLAN.md with roadmap
Storybook Upgrade (8.x -> 10.1.4): - Upgraded Storybook from 8.6.14 to 10.1.4 - Ran automigrations for consolidated imports and package updates - Removed incompatible storybook-dark-mode addon (will revisit) - Removed @storybook/blocks and @storybook/test (consolidated in v10) - Added type: module to package.json to fix Vite CJS warning New TypeScript Components: - BrandBand (CSF stories) - BuilderHeader (TypeScript + CSF stories) - Files (TypeScript + CSF stories) - GlobalNavigationBar (TypeScript + CSF stories) - Expression (TypeScript + CSF stories) - LocationMap (TypeScript + CSF stories) - AppLauncher (TypeScript + CSF stories) Type Definitions: - Updated Modal.d.ts with missing props - Added BuilderHeader utilities/nav-link type declarations - Added Expression group type declarations Total: 52 components migrated to TypeScript
- Installed @vueless/storybook-dark-mode (Storybook 10 compatible fork) - Configured dark mode with SLDS color scheme classes - Updated MODERNIZATION_PLAN.md
Carousel: - Converted from class to functional component with hooks - Added useState, useEffect, useRef, useCallback for state management - Created type declarations for private components - Added CSF Storybook stories Popover: - Converted from class to functional component with hooks - Removed react-onclickoutside HOC (Dialog handles click-outside) - Added comprehensive TypeScript types - Added CSF Storybook stories with all variants - Added future work note: migrate from Popper.js to @floating-ui/react Total: 54 components migrated to TypeScript
- Converted Tooltip from class to functional component - Added useState, useEffect, useRef, useCallback hooks - Converted FieldLevelHelpTooltip private component - Added comprehensive TypeScript types - Created CSF Storybook stories with all variants - Added check-props.d.ts type declaration Total: 55 components migrated to TypeScript
…ies to CSF format Components converted to TypeScript functional components: - ColorPicker (main + 7 private subcomponents) - TimePicker (main + dropdown-trigger) - DatePicker stories updated (removed moment.js dependency) Story files renamed to ComponentName.stories.jsx/tsx format: - Renamed 64+ story files from storybook-stories.jsx - Updated .storybook/main.ts with all new paths - Added proper 'Components/ComponentName' title format Other improvements: - Fixed check-props.js null checks for optional props - Updated useClickOutside hook - Added type definitions for various components - Updated MODERNIZATION_PLAN.md with progress
Major TypeScript conversions: - DataTable: Full conversion with typed props, contexts, and sub-components - Filter: Converted to functional component with hooks - MenuPicklist: Converted with proper TypeScript interfaces - Tree: Full conversion including branch/item rendering Additional improvements: - Added displayName to 40+ components for better debugging - Updated Stories to CSF format with proper TypeScript - Added shared types.ts files for each converted component - Registered new stories in Storybook main.ts This continues the React 19 + TypeScript modernization effort.
- Update README.md to reflect React 19, Vite, Vitest, Storybook 10 - Remove outdated React 16 references and Babel preset info - Add Node.js 20.19.0+ requirement - Move internal docs (SLDS2_ANALYSIS.md, MODERNIZATION_PLAN.md) to .planning/ - Add .planning/ to .gitignore (internal work product) - Add cursor rule for planning file conventions
Deleted (obsolete): - docs/browser-compatibility.md - IE11/polyfill references - docs/create-react-app.md - CRA 1.x deprecated - docs/create-react-app-2x.md - CRA 2.x deprecated - docs/webpack.md - Now using Vite - docs/getting-started.md - Duplicated README, template vars - docs/faq.md - Links to deleted files - docs/legacy-release-branches.md - Outdated process Moved to .planning/ (internal): - docs/release.md - Internal release process - RELEASENOTES.md - 2000+ lines of legacy history Updated: - CONTRIBUTING.md - Vitest, TypeScript, modern workflow - docs/codebase-overview.md - Functional components, hooks patterns - docs/README-dist.md - React 19, modern usage - docs/SUPPORT.md - Simplified Added: - CHANGELOG.md - Simplified changelog for modernization
Deleted (replaced by Vite): - webpack.config.js - webpack.config.test.js Deleted (Heroku deployment - not used): - app.js - app.json - Procfile Deleted (old CI/tooling): - .circleci/ - Used Node 12, old test commands - preset/ - Babel preset for IE11/Node 8 - .jest/ - Old Jest configuration - .snyk - Old vulnerability ignores Deleted (deprecated testing): - tests/enzyme-helpers.js - Enzyme replaced by React Testing Library
- Add highlights section featuring SLDS 2, dark mode, React 19, TypeScript - Add dark mode usage instructions in Styling section
- Create ROADMAP.md with reader-friendly status summary - Add callout banner to README linking to roadmap - Document what's ready, in progress, and known limitations
- Change stylesheet reference to bundled slds-plus.css - Add note that this is temporary until SLDS 2 is on npm - Add collapsible section for SLDS 1 fallback - Update install command (SLDS styles now bundled) - Add SLDS+ CSS to known limitations in ROADMAP.md
…st status Full 21-agent audit of the feat/typescript-modernization branch found the ROADMAP's completion claims (React 19 done, 63 TS-converted, Vitest replacing Karma) do not survive contact with the tree: 1 of 71 components genuinely migrated, no root tsconfig, 1 collectable test, ~47 Enzyme suites, broken publish pipeline. Adds the detailed audit + dependency-ordered roadmap and rewrites ROADMAP.md to reflect verified reality.
…ports - Add root tsconfig.json covering components/**, utilities/**, types/** with path aliases and strict mode; tsc --noEmit now actually type-checks the tree (was inert with no root config). Passes clean. - Fix Storybook startup crash: remove self-referential optimizeDeps.include ['design-system-react'] that forced Vite to resolve the unbuilt dist/. - Fix package exports map: order types first; emit real .cjs for CommonJS (was .umd.js under type:module → ERR_REQUIRE_ESM); add sideEffects:false. - Convert utilities/index.js to standard ESM re-exports (Babel-only 'export X from' syntax broke tsc parsing). - Fix radio/index.tsx type error (Swatch color: string | undefined). - Regenerate package-lock against public npm registry (was Nexus-bound).
- Replace react-onclickoutside HOC in lookup/index.jsx with the existing React-19-compatible useClickOutside hook (only real import of the lib; the ~25 other references are just the ignore-react-onclickoutside CSS class name and are unaffected). react-onclickoutside capped its peer at React 18 and was the sole cause of the ERESOLVE install failure. - Declare prop-types explicitly (was a phantom transitive dep via react-modal; imported directly in 129 files). - Remove dead deps with zero imports: create-react-class, react-required-if, react-text-truncate. Verified: npm install now succeeds with 0 ERESOLVE errors and no --legacy-peer-deps; tsc --noEmit passes; Storybook boots.
- vite-plugin-dts now emits declarations for the real published surface (components barrel + sidecar .d.ts/.tsx, utilities, types) instead of only the shared types/ folder; copyDtsFiles picks up hand-written .d.ts. The emitted dist/types/components/index.d.ts now matches components/index.js. - Point package types + exports.types at the emitted component barrel (was dist/types/index.d.ts, which re-exported a nonexistent path). - Add files field (dist + README/LICENSE/RELEASENOTES) so publish ships only build output — verified npm pack: 275 files, 0 source/test leaks. - Add prepack -> build so the Vite artifact is always fresh before publish. Verified: npm run build (tsc --noEmit && vite build) exits 0, emits .es.js + .cjs + full type tree; npm pack --dry-run ships a clean dist-only package.
- Add .github/workflows/ci.yml: on push/PR, Node 20.19, npm ci, then typecheck + test + build as blocking gates (all pass today). Lint runs non-blocking (continue-on-error) because the repo has ~379 pre-existing lint errors tracked as P4; flip to blocking once cleared. - Rewrite npm-publish.yml: Node 14 -> 20.19; drop the nonexistent build:npm-package script and .tmp-npm Babel artifact; run typecheck/test/build then > design-system-react@0.10.64 prepack > npm run build > design-system-react@0.10.64 build > tsc --noEmit && vite build vite v5.4.21 building for production... transforming... ✓ 393 modules transformed. rendering chunks... [vite:dts] Start generate declaration files... computing gzip size... dist/design-system-react.es.js 906.53 kB │ gzip: 222.50 kB │ map: 3,426.06 kB [vite:dts] Declaration files built in 1595ms. dist/design-system-react.cjs 542.24 kB │ gzip: 159.29 kB │ map: 3,315.37 kB ✓ built in 2.45s (prepack rebuilds dist, files field scopes the tarball). Add id-token permission for provenance. Verified locally: typecheck exits 0, vitest run exits 0 (1 file/8 tests — the true current suite), build exits 0.
…guide - Migrate spinner and alert test suites to Vitest + React Testing Library - Delete legacy .browser-test.jsx for both (they were never collected/ran) - Add enzyme-to-rtl-migration-guide.md as the canonical pattern for the remaining ~52 suites - Enhance vitest.setup.ts polyfills (IntersectionObserver, scrollIntoView, getBoundingClientRect) for overlay-based components
Fan-out migration of every legacy .browser-test.jsx to .test.jsx using
React Testing Library + Vitest, following enzyme-to-rtl-migration-guide.md.
Full suite: 660 passed | 46 skipped | 0 failed across 56 files (~3.3s).
typecheck clean. All 54 legacy .browser-test.jsx removed (they were never
collected by the vitest glob and imported a nonexistent tests/enzyme-helpers,
so they had not actually run).
The 46 skips are documented in migration-discovered-bugs.md and fall into:
- jsdom limitations (focus traps, real layout/scroll, canvas truncation)
- Enzyme instance/state access with no RTL equivalent (data-table keyboard nav)
- genuine pre-existing React 19 bugs surfaced by the migration:
* components/utilities/dialog/portal.jsx uses removed React APIs
(unstable_renderSubtreeIntoContainer, unmountComponentAtNode)
* lookup menu crashes on React 19 ref validation (15 skips)
* menu-dropdown keyboard nav TypeError (5 skips)
* data-table/visual-picker leak invalid props to React.Fragment
* react-highlighter-ts incompatible with React 19 (tree, data-table, app-launcher)
These are tracked for P2 (real TS + hooks conversion).
The Enzyme suites are fully migrated to Vitest+RTL, so the legacy runner and its generated artifacts are now dead weight and actively misleading: - karma.conf.js (references removed webpack.config + karma-* packages) - 54 *.storyshot DOM snapshots + 415 *.png image snapshots (storyshots/ jest-image-snapshot visual regression — tooling already removed) - 2 *.dom-snapshot-test.jsx + their .snap (imported the deleted tests/snapshot-helpers) - tests/ story-based + browser-test harness (story-based-tests.*, browser-tests*, storyshots-helpers, axe-*, snapshot-helpers, exclude-story-config, settings, url, index.html, __mocks__, __image_snapshots__) — all referenced removed deps (enzyme/chai/sinon/puppeteer/mocha/karma) and nothing live imported them - drop the now-empty tests/** glob from vitest.config.ts - rewrite tests/README.md for the Vitest + RTL workflow Full suite unchanged: 660 passed | 46 skipped | 0 failed (56 files). typecheck clean.
components/utilities/dialog/portal.jsx used unstable_renderSubtreeIntoContainer and unmountComponentAtNode, both removed in React 19 — a real runtime crash for any overlay rendered through the dialog overflowBoundaryElement path, not just a test-env issue. Rewritten as a function component over ReactDOM.createPortal: - useEffect creates/appends the portal container node on mount and removes it on unmount (React auto-unmounts the portal children) - id/className/style sync + onUpdate preserved via a second effect - onMount/onOpen/onUpdate/renderTag/renderTo semantics preserved; PortalSettings context renderTo precedence preserved - dropped the Enzyme-only portalMount internal branch (dead with Enzyme removed); the public portalMount prop pass-through is retained/ignored Re-enabled the 2 filter suite tests (previously skipped for this exact bug); they pass stably. Full suite: 662 passed | 44 skipped | 0 failed. typecheck clean.
components/lookup/private/item.jsx attached a string ref (ref={id}) to the menu
item anchor. String refs were removed in React 19, so opening the Lookup menu
threw 'Expected ref to be a function, an object returned by React.createRef(),
or undefined/null' and crashed the whole component.
The string ref was dead code: this.refs is never read anywhere in the lookup
tree, and the anchor already sets id={id} independently (that's what drives the
input's aria-activedescendant). Removed the ref line.
Re-enabled all 15 lookup tests that open the menu (keyboard focus, filtering,
selection, esc-to-close, aria-expanded). Lookup suite: 21/21 pass.
Full suite: 677 passed | 29 skipped | 0 failed. typecheck clean.
Three compounding React-19 causes broke keyboard navigation in menu-dropdown and (latently) menu-picklist: - menu-list attached a ref to the ListItem *class component*, so itemRefs got the instance instead of the <li> DOM node -> focusMenuItem's getElementsByTagName threw. Added a nodeRef callback prop to ListItem, wired it to all three <li> roots, and switched menu-list/index.jsx to nodeRef=. - keyboard-navigate.js used ReactDOM.findDOMNode (removed in React 19); replaced with a resolveTriggerNode helper (trigger is already a DOM node). - openMenuKeys omitted KEYS.DOWN, so Down-arrow-opens-menu never fired. Re-enabled 5 menu-dropdown + 3 menu-picklist skipped tests. Full suite: 685 passed, 21 skipped, 0 failed. tsc --noEmit clean.
Both components cloneElement()'d props onto a React.Fragment, which only accepts key/children — React warned and silently dropped the props, so the intended styling/props never applied (latent bugs, not warning-only). - data-table header-cell: getHeaderCellContent wrapped its single child in a Fragment on the fixedLayout path; callers injected `style` via cloneElement. Return the sub-render element directly instead. - visual-picker: React.Children.map cloned picker props onto Fragment-grouped children. Added a decorateOption helper that recurses into Fragment children and clones onto the real elements. 0 Fragment warnings in both suites. Full suite: 685 passed, 21 skipped, 0 failed. tsc --noEmit clean.
react-highlighter-ts@2.2.0 declares react@^17 as a hard dependency (not a peer), so npm installed a nested React 17 under it. Its react/jsx-runtime produced React-17 elements that React 19 rejects with 'A React Element from an older version of React was rendered', crashing every highlight render path (tree searchTerm, data-table HighlightCell). - Added a package.json `overrides` block pinning react/react-dom to ^19.2.0, so no nested copy is installed (verified: react-highlighter-ts no longer has its own node_modules; lockfile regenerated). - Added resolve.dedupe ['react','react-dom'] to vite + vitest configs as a belt-and-suspenders single-instance guarantee. Re-enabled the tree highlight test and the data-table HighlightCell test. Full suite: 687 passed, 19 skipped, 0 failed. tsc --noEmit clean.
- Add @salesforce-ux/design-system-2@2.264.0 as a dependency; Storybook now serves the bundled Lightning Blue SLDS 2 CSS from the npm package (mapped to /slds2) instead of the committed static slds-plus.css. - Point README styling instructions at the npm package path. - Rewrite ROADMAP.md to reflect actual state (~68/71 components on .tsx with stories, 56 passing Vitest files, popper.js the sole source blocker) and re-prioritize remaining work (P1 floating-ui, P2 test/ story gaps, P3 SLDS 2 delivery, P4 toolchain majors + GH Pages). - npm update: in-range bumps (React 19.2.8, Storybook 10.5.5, types).
… SB) - Add Theme toolbar global (Lightning Blue / Cosmos / Glass) + a preview decorator that swaps the SLDS 2 stylesheet <link> inside the iframe; Glass layers sub-themes/glass.css over the Cosmos base (matches upstream). - Add Color Scheme global (light/dark/system); decorator sets slds-color-scheme--* on the iframe <body>, so dark mode now affects the story canvas (not just chrome). Removes @vueless/storybook-dark-mode. - Extend a11y ruleset (wcag2a/aa, wcag21, wcag22aa, best-practice), modeled on SLDS 2. - Refactor Accordion stories to the argTypes/Controls pattern as the P2 template.
48 matched React<->SLDS2 pairs inventoried (6-way parallel sweep): story counts/names, controls, features, and API/behavior diffs per pair. Distilled the highest-leverage gaps into ROADMAP P2 (token-theming stories, unwired __examples__, missing state stories, and real component feature gaps).
Adds WithToolbar, WithUtilities, SuccessfulSave, AfterSuccessfulSave, and FailedSave stories from the existing __examples__ (previously unwired), closing the SLDS 2 WithToolbar / HasStatusText* story gaps identified in the 2026-08-07 comparison. tsc + eslint clean.
Surfaces three high-value variants that existed only in __examples__ (not exported as stories): onRenderMenuItem custom rendering, per-option disabled state, and RTL direction. Closes gaps flagged in the 2026-08-07 SLDS 2 comparison. tsc clean.
CI has never actually run on this fork (zero workflow runs), so two gaps went unnoticed: `npm run test:coverage` was missing @vitest/coverage-v8 entirely, and `npm run lint` picks up a stale local storybook-static/ build if one exists, inflating errors 0 -> 2161. Lint is genuinely 0 errors on real source, so make it a blocking CI step instead of continue-on-error.
Adds a Pages deploy workflow (build-storybook -> actions/deploy-pages) and enables Pages on the fork (source: GitHub Actions). Live at https://showerbee.github.io/design-system-react/ once master picks up this branch. Refreshes ROADMAP status snapshot and closes out the combobox/builder-header story-wiring checklist items already shipped.
CI's first-ever run on this fork failed at the Typecheck step because tsconfig.json didn't exist in the checkout — a stale VSCode-section .gitignore rule from before this became a TypeScript project was silently excluding it. It only worked locally because the file already existed on disk. Force-added and removed the ignore rule.
@vitest/coverage-v8@1.6.1's coverage-merge step (@bcoe/v8-coverage) hit an unhandled "undefined is not iterable" crash on GitHub's runner after all 696 tests had already passed — reproducible on CI, not locally. Coverage reporting isn't a stated CI gate; run plain `vitest run` instead. `npm run test:coverage` stays available for local/manual use.
…design-system-2) - @storybook/* and eslint-plugin-storybook 10.2.1 -> 10.5.10 - @testing-library/react 16.1 -> 16.3.3, user-event 14.5 -> 14.6.6, jest-dom 6.6 -> 6.9.1 - @salesforce-ux/design-system-2 2.264.0 -> 2.264.1 - @types/react-dom 19.2.0 -> 19.2.5 Typecheck, 696 tests, and storybook build all green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Coupled upgrade — vitest 4 requires vite >=6, so both move together: - vite 5.2 -> 8.2.2, @vitejs/plugin-react 4.2 -> 6.1, vite-plugin-dts 3.8 -> 5.0.3 - vitest + @vitest/coverage-v8 1.6 -> 4.1.11 - jsdom 24 -> 30, @testing-library/jest-dom 6 -> 7 vite.config: drop dead optimizeDeps.esbuildOptions JSX-in-.js loader override (deprecated under vite 8's Rolldown optimizer; the TS modernization already removed all JSX-authored .js source it served). Verified: 696 tests pass, typecheck clean, lib build + storybook build + storybook dev smoke test all green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Upgrade the lint stack and migrate .eslintrc.cjs -> eslint.config.mjs
(ESLint 10 drops eslintrc support entirely):
- eslint 8.57 -> 10.9.1, @eslint/js 10, typescript-eslint meta 8.68
(replaces separate @typescript-eslint/{eslint-plugin,parser} 7.x)
- eslint-plugin-react-hooks 4.6 -> 7.1.1, eslint-config-prettier 9 -> 10.1.8
- eslint-plugin-jsx-a11y -> eslint-plugin-jsx-a11y-x 0.2.0, and
eslint-plugin-import -> eslint-plugin-import-x 4.17.1: the original
plugins cap their eslint peer at ^9 and have no v10 support; the
maintained -x forks support eslint 10.
Flat-config specifics:
- Re-key each -x fork's recommended ruleset onto the legacy jsx-a11y/ and
import/ prefixes so the ~80 inline eslint-disable comments and the rule
overrides keep working without a tree-wide rewrite.
- Pin react.version instead of 'detect' (eslint-plugin-react's detection
calls context.getFilename(), removed in eslint 10).
- reportUnusedDisableDirectives: off, restoring prior eslintrc behavior
(flat config defaults it to warn; source has ~300 stale max-len disables).
- Port .eslintignore globs (browser-tests, icons, storybook) into ignores.
- Demote rules newly introduced by react-hooks 7 (React Compiler:
refs/set-state-in-effect/immutability) and eslint 10 / ts-eslint 8
(no-useless-assignment, no-constant-condition, preserve-caught-error,
no-unused-expressions, no-empty-object-type) to warnings, matching the
config's gradual-migration 'zero errors' philosophy.
- Remove 6 unsupported /* eslint-env */ comments (globals now provided by
the flat config).
lint: 0 errors (was 0), 275 warnings. Typecheck clean, 696 tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…or to 24 - @babel/core 7 -> 8.0.1 (dev-only; used by doc/build scripts, not the lib) - nanoid 5 -> 6.0.1 - @types/node 20 -> 24.13.3 nanoid 6 requires Node ^22 || ^24 || >=26, which is incompatible with the prior engines.node >=20.19.0. Raise the Node floor to >=24 to match, and move the version pins that tracked it: .nvmrc and the three GitHub Actions workflows 20.19.0 -> 24.19.0. Typecheck clean, 696 tests pass, lib build + lint (0 errors) all green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Both were the only deprecated direct dependencies remaining.
popper.js v1 (deprecated in favor of @popperjs/core v2) -> @floating-ui/dom:
- Only consumer is the private Dialog utility (components/utilities/dialog).
Floating UI shares popper v1's placement vocabulary (bottom-start, etc.)
and offset-parent positioning model, so mapPropToPopperPlacement and the
nubbin/offset math in dialog-helpers carry over unchanged.
- createPopper now uses computePosition + autoUpdate; a computeFloatingData
adapter reshapes Floating UI's { x, y, placement } output back into the
popperData shape the render logic and helpers already consume
(offsets.popper, offsets.reference, placement, flipped, style).
- flip()/shift() middleware replace popper's flip/preventOverflow modifiers,
disabled together when hasStaticAlignment is set (prior behavior). autoUpdate
replaces eventsEnabled; its cleanup fn is stored for teardown.
lodash.isequal (deprecated) -> dequal (maintained, deep-equal drop-in) across
combobox, combobox/private/menu, lookup, data-table, and dialog. Drop the
@types/lodash.isequal devDependency (dequal ships its own types).
vitest.setup: ResizeObserver/IntersectionObserver mocks changed from
arrow-returning vi.fn() to real classes — Floating UI's autoUpdate constructs
them with new, which threw 'is not a constructor' against the arrow spies.
696 tests pass (incl. popover/tooltip/dropdown/combobox/datepicker/lookup
overlays), typecheck clean, lib build + lint (0 errors) all green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Follows Microsoft's TS 7 side-by-side guidance (devblogs.microsoft.com/typescript/announcing-typescript-7-0). TS 7 ships without a programmatic API until 7.1, so tools that import the compiler API (typescript-eslint, vite-plugin-dts, the editor language server) still need 6.0. Rather than block on typescript-eslint's `typescript <6.1.0` peer, run both concurrently via npm aliases: "@typescript/native": "npm:typescript@^7.0.2" -> provides `tsc` (7.0) "typescript": "npm:@typescript/typescript6@^6.0.2" -> 6.0 API + `tsc6` Result: `npm run typecheck`/`build` invoke the fast TS 7 native `tsc`, while typescript-eslint and vite-plugin-dts resolve `typescript` to the 6.0 API package and keep working. `npx tsc6` remains available for anything needing 6.0's CLI. tsconfig for TS 7: - Remove `baseUrl` (removed in TS 7; `paths` already resolve relative to the config file, so aliases are unaffected). - Add `"types": ["node"]` — TS 7 no longer implicitly includes every installed @types/* package's globals, and a few files use `process.env.NODE_ENV`. Verified: typecheck (TS 7) clean, lib build + dts generation (6.0 API) green, lint 0 errors (typescript-eslint via 6.0 API), 696 tests pass. Note: vitest 5 was requested too but is not installable in this environment yet — stable 5.0.0 published after the registry's publish-date cutoff (npm install errors ETARGET); held at 4.1.11. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…s without it
ExpressionCondition's render dereferenced `this.props.events.onChangeResource`
(and onChangeOperator/onChangeValue/onDelete) with no guard, but `events` had
no default. Rendering the component without an `events` prop — as the Storybook
stories do — threw "Cannot read properties of undefined (reading
'onChangeResource')".
All fields in ExpressionConditionEvents are already optional, so:
- add `events: {}` to defaultProps and mark the prop optional in the type;
- read via a local `const events = this.props.events || {}` in render, so the
callbacks pass through as undefined (which Combobox/Input tolerate).
Adds a regression test rendering ExpressionCondition with no `events` prop.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The DataTable "Resizable Columns" story threw "ColumnResizer is not a
constructor" in the Storybook dev server after the vite 5 -> 8 upgrade.
`column-resizer` ships a UMD bundle whose `module.exports` is
`{ __esModule: true, default: ColumnResizer }`. vite 5's esbuild dependency
optimizer collapsed this to expose the constructor as the ESM default, but
vite 8's Rolldown optimizer honors the `__esModule` flag and pre-bundles it
as `export default require_column_resizer()` — i.e. the default is the whole
wrapper object, so `import ColumnResizer from 'column-resizer'` binds to an
object and `new ColumnResizer(...)` throws. (The library build and vitest
resolve the interop correctly and are unaffected; verified against the real
pre-bundled dep that the shim's default resolves to the constructor.)
Fix is Storybook-scoped, no change to the component or shared build/test
config:
- .storybook/shims/column-resizer.js re-exports the real constructor,
unwrapping nested `default`s until it reaches the callable.
- viteFinal aliases the bare `column-resizer` specifier to the shim using the
array/regex alias form with an anchored `/^column-resizer$/` pattern, so
only the bare specifier is rewritten — the object alias form does prefix
matching and would also rewrite the shim's own dist subpath import.
storybook dev + production build both succeed; 697 tests, typecheck, lint
(0 errors) all green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Carousel stories pointed `src` at https://via.placeholder.com, a placeholder-image service that shut down in 2024, so no images rendered. Point them at the carousel images already bundled with @salesforce-ux/design-system and served by Storybook at /assets/images/carousel/ (the same assets the __examples__ use). Verified all three resolve HTTP 200 from the dev server. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…GlobalNavigationBar story Two related fixes so the GlobalNavigationBar story renders (it showed only an empty bar). Root cause: react-docgen-typescript was emitting `Component.displayName = "<name derived from the identifier>"`, overwriting the static `displayName` several components set to an SLDS constant. GlobalNavigationBar filters its children with `child.type.displayName === GLOBAL_NAVIGATION_BAR_REGION` (value 'SLDSGlobalNavigationBarRegion'), but docgen had clobbered the region's displayName to 'GLOBAL_NAVIGATION_BAR_REGION', so no child matched and the bar rendered empty. Set `setDisplayName: false` in the docgen options to preserve the real runtime displayName. (Verified in a headless browser: the bar now renders the app launcher, app name, nav links, and the Menu Item dropdown; jsdom tests never caught this because docgen only runs in Storybook.) Also rewrite the GlobalNavigationBar stories to use the real subcomponents (AppLauncher trigger + GlobalNavigationBarLink + GlobalNavigationBarDropdown), mirroring __examples__/default.jsx, instead of hand-rolled context-bar markup. typecheck clean; global-navigation-bar/app-launcher/data-table tests pass; storybook production build succeeds. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Toast close button used `iconSize="large"`, making the close icon visibly oversized. The sibling Alert component — same `slds-notify__close` inverse icon-button — uses `iconSize="medium"`, and the SLDS notify blueprints put no icon-size modifier on the close button. Align Toast to `medium`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Thanks for opening this pull request! 💯 This is a community-driven project, and we can't do it without your participation. Please check out our contributing guidelines and review the Contributor Checklist if you haven't already, to make sure everything is squared away. CircleCI will take about 10 minutes to run through the same items that are on the Contributor checklist with a pass/fail check below. Please fix any issues that cause CircleCI to fail or ask for clarification--we try, but sometimes the errors can be unclear. |
|
Great first PR! @Niznikr 🚀 Nothing like starting with a bang!
|
|
@Niznikr Would it be helpful for me to get open source Chromatic set up? |
|
On the a11y tests, are we using the |
|
I've added the |
|
If any agents are following this PR, this PR has been moved to an internal branch in the repo in order to support Chromatic CI. #3195 |
This PR has been moved to an internal branch in the repo in order to support Chromatic CI. #3195
Summary
Modernizes
@salesforce/design-system-reactto React 19 with a current toolchain (Vite 8, Vitest 4, ESLint 10 flat config, TypeScript 7, Storybook 10, Node 24), migrates the test suite from Enzyme/Mocha to Vitest + React Testing Library, incrementally converts components to TypeScript, and removes deprecated dependencies. Along the way it fixes the React-19 removed-API breakages and a number of component/Storybook bugs surfaced by the upgrades.End state: 697 tests passing, typecheck clean, lint 0 errors, library build + Storybook (dev & production) build successfully, and no deprecated direct dependencies remain.
Highlights
React 19
Portalrewritten oncreatePortal; removedfindDOMNode/unstable_renderSubtreeIntoContainer/ReactDOM.renderusages; fixed a dead string ref (lookup menu), Fragment-invalid-prop leaks, and keyboard nav in menu-dropdown/menu-picklist.Reactimports (jsx-runtime).Toolchain
@vitejs/plugin-react4 -> 6,vite-plugin-dts3 -> 5, Vitest 1 -> 4 (+coverage), jsdom 24 -> 30, jest-dom 6 -> 7.typescript-eslint7 -> 8; moved to the eslint-10-compatibleeslint-plugin-jsx-a11y-x/eslint-plugin-import-xforks (re-keyed to legacy namespaces so inline disables keep working).@typescript/native(=typescript@7) drivestscwhiletypescriptresolves to@typescript/typescript6for API consumers (typescript-eslint, vite-plugin-dts, editor).@types/node-> 24, Node engine floor -> 24, Storybook 10.5.10.Testing
TypeScript conversion
.jsxto.tsx, removing dozens of stale ambient.d.tssidecars that shadowed real sources.Deprecated dependency removal
popper.js(v1) ->@floating-ui/dom(adapter preserves the dialog helpers' expected data shape).lodash.isequal->dequal.Storybook / component fixes
ColumnResizer is not a constructor).setDisplayName: falsesoreact-docgen-typescriptstops clobbering staticdisplayName(was breaking GlobalNavigationBar's child filtering, rendering an empty bar); GlobalNavigationBar stories rewritten to use real subcomponents.eventsto{}soExpressionConditionrenders without it.via.placeholder.comURLs with bundled SLDS assets.medium(was oversizedlarge) to match the notify pattern.CI
Deferred (documented)
Test plan
npm run typecheck- clean (TS 7)npm test- 697 passingnpm run lint- 0 errorsnpm run buildandnpm run build-storybook- succeedCONTRIBUTOR checklist (do not remove)
Please complete for every pull request
npm run lint:fixhas been run and linting passes.components/component-docs.jsonCI tests pass (npm test).REVIEWER checklist (do not remove)
components/component-docs.jsontests.Required only if there are markup / UX changes
last-slds-markup-reviewinpackage.jsonand push.last-accessibility-review, topackage.jsonand push.npm run local-updatewithin locally cloned site repo to confirm the site will function correctly at the next release.