Skip to content

Refactor(web-react): Standalone Label, HelperText & ValidationText #DS-2398#2547

Merged
crishpeen merged 6 commits intorelease/v5-essencefrom
refactor/DS-2398-react-standalone-form-fields
Mar 26, 2026
Merged

Refactor(web-react): Standalone Label, HelperText & ValidationText #DS-2398#2547
crishpeen merged 6 commits intorelease/v5-essencefrom
refactor/DS-2398-react-standalone-form-fields

Conversation

@crishpeen
Copy link
Copy Markdown
Member

Description

Additional context

Issue reference

@crishpeen crishpeen added the run-visual-tests Runs visual regression testing on this PR label Mar 17, 2026
@crishpeen crishpeen requested review from a team, adamkudrna and dlouhak as code owners March 17, 2026 15:44
@netlify
Copy link
Copy Markdown

netlify bot commented Mar 17, 2026

Deploy Preview for spirit-design-system canceled.

Name Link
🔨 Latest commit 9c34369
🔍 Latest deploy log https://app.netlify.com/projects/spirit-design-system/deploys/69c50d299d93710008cf5115

@github-actions github-actions bot added the refactoring A code change that neither fixes a bug nor adds a feature label Mar 17, 2026
@github-actions
Copy link
Copy Markdown
Contributor

@github-actions
Copy link
Copy Markdown
Contributor

@crishpeen
Copy link
Copy Markdown
Member Author

when #2548 is merged, the e2e should be ok

@github-actions
Copy link
Copy Markdown
Contributor

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors form-field subcomponents in web-react by extracting Label, HelperText, and ValidationText into standalone components (instead of being bundled under Field), and updates the web (vanilla) docs/demos to use the new “default Label” styling (no Label--box).

Changes:

  • Introduces standalone Label, HelperText, and ValidationText components in packages/web-react and wires them into existing form controls via PropsProvider.
  • Updates multiple vanilla packages/web component demos/READMEs to use class="Label" (and related modifiers) consistently.
  • Removes the Field subpath export and related types/tests, and adds supporting hooks/types (useAriaIds, FormFieldVariants, shared form-field context types).

Reviewed changes

Copilot reviewed 135 out of 143 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/web/src/scss/components/** Updates HTML demos/READMEs to use Label base class instead of Label--box and adjusts related markup formatting.
packages/web/src/scss/components/Label/_Label.scss Removes the explicit “box variant” selector and makes “box” the default (non-inline, non-item).
packages/web-react/src/components/** Replaces Field/* usage with standalone Label, HelperText, ValidationText and introduces styling hooks for these components.
packages/web-react/src/context/PropsContext.ts Filters undefined from provider values to avoid unintentionally overwriting parent context.
packages/web-react/package.json Removes ./components/Field export from the package export map.
Comments suppressed due to low confidence (1)

packages/web-react/package.json:328

  • The package export map removes the ./components/Field subpath export, but there are no new subpath exports for the new standalone components (./components/Label, ./components/HelperText, ./components/ValidationText). Given the pattern used for other components, this likely makes the new standalone components unavailable via subpath imports and introduces a breaking change for existing .../components/Field consumers. Consider adding explicit exports for the new component subpaths (and/or a compatibility re-export for Field) and ensuring this is handled as a documented breaking change.
      "require": "./dist/components/EmptyState/index.cjs",
      "default": "./dist/components/EmptyState/index.js"
    },
    "./components/FieldGroup": {
      "types": "./dist/components/FieldGroup/index.d.ts",
      "development": "./src/components/FieldGroup/index.ts",
      "production": "./dist/components/FieldGroup/index.js",
      "import": "./dist/components/FieldGroup/index.js",
      "require": "./dist/components/FieldGroup/index.cjs",
      "default": "./dist/components/FieldGroup/index.js"
    },

@netlify
Copy link
Copy Markdown

netlify bot commented Mar 19, 2026

Deploy Preview for spirit-design-system-storybook canceled.

Name Link
🔨 Latest commit 9c34369
🔍 Latest deploy log https://app.netlify.com/projects/spirit-design-system-storybook/deploys/69c50d2959ce8800089c2923

@github-actions
Copy link
Copy Markdown
Contributor

@crishpeen
Copy link
Copy Markdown
Member Author

I updated the PR and tried to resolve all your comments; hopefully, I didn't miss anything or resolve any unresolved issues. If it happened, I'm sorry, and let me know.

@crishpeen crishpeen requested a review from dlouhak March 19, 2026 16:55
Copy link
Copy Markdown
Contributor

@dlouhak dlouhak left a comment

Choose a reason for hiding this comment

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

Significant: Could you please add stories for Label and ValidationText?
Nitpicking: A few comments about query selectors. Looks like we should write down the rules for writing unit tests for coding agents. 🙈

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions
Copy link
Copy Markdown
Contributor

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Security review update: I re-validated the current diff and checked prior unresolved review threads; no unresolved security findings remain applicable.

I reviewed only modified code paths and traced attacker-controlled inputs to sinks in the new standalone Label, HelperText, and ValidationText integrations (PropsProvider propagation, aria registration, and rendering in form components). I did not find any medium/high/critical vulnerability introduced by this PR:

  • No new HTML/JS injection sink (dangerouslySetInnerHTML, eval, dynamic script construction) was introduced.
  • User-provided label/helper/validation content remains rendered through React nodes (framework escaping applies for string content).
  • No auth/authz boundary changes, request-forgery surfaces, SSRF/path traversal/file-system sinks, or secret logging paths were introduced in changed runtime code.
  • No dependency/supply-chain risk was introduced by this diff.

Inline finding comments: none (no high-confidence security vulnerability).

Slack summary: PR #2547 security review complete — no medium/high/critical vulnerabilities found in modified code paths; no active security findings to address.

Open in Web View Automation 

Sent by Cursor Automation: Find vulnerabilities

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Stale comment

Code Review: refactor/DS-2398-react-standalone-form-fields

Scope: 12 commits, 165 files (+7410 / −2723)

Overview

  1. Splits Label, HelperText, and ValidationText out of the removed Field package into first-class components with their own package.json subpath exports and barrel exports.
  2. Moves useAriaIds into hooks, adds focused tests, and wires form controls (TextFieldBase, Checkbox, Select, Slider, Toggle, FieldGroup, file uploaders, etc.) to the standalone pieces plus PropsProvider for shared field context.
  3. Tightens PropsProvider merging so explicit null / undefined values remove keys from inherited context (documented in JSDoc).
  4. Carries related SCSS demo/README updates, dropdown placement default handling, and refreshed visual snapshots.

1. packages/web-react/package.json — Subpath exports

suggestion (non-blocking): Document removal of @alma-oss/spirit-web-react/components/Field and map consumers to @alma-oss/spirit-web-react/components/Label, HelperText, and ValidationText (or the main package barrel).

Fix: Add a short entry to the next release notes / migration doc (e.g. docs/migrations/web-react/ or CHANGELOG.md) with before/after import examples.

Why: The ./components/Field export entry was removed; anyone using that deep import will get a resolution failure on upgrade. Calling it out avoids silent breakage during version bumps.


Summary

Finding type Count
issue 0
suggestion 1
question 0
note 0

Verdict: APPROVE

No blocking issues; the refactor is coherent, tests and snapshots were updated, and accessibility registration for helper/validation text is preserved (with a small improvement: cleanup no longer calls remove when id is missing).


praise: Solid extraction: shared FormFieldContextValue typing, PropsProvider + useContextProps precedence documented, dedicated stories/demos for the new components, and broad test updates across inputs and provider tests—good regression safety for a structural change like this.

Open in Web View Automation 

Sent by Cursor Automation: Spirit Cursor Code Review

@github-actions
Copy link
Copy Markdown
Contributor

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Stale comment

Code Review: refactor/DS-2398-react-standalone-form-fields

Scope: 15 commits, 168 files (+7376 / −2731)

Overview

  1. Extracts Label, HelperText, and ValidationText as standalone components with dedicated style hooks, demos, stories, and tests.
  2. Removes the old Field subcomponents and wires form controls (TextFieldBase, Checkbox, Radio, Select, Slider, Toggle, FieldGroup, FileUploader, UNSTABLE_FileUpload, etc.) to compose these pieces with PropsProvider and useAriaIds for aria-describedby.
  3. Moves useAriaIds to shared hooks and expands formFieldContextPropsTest for consistent context vs. direct-prop behavior.
  4. Updates Dropdown so useDropdownContext always yields a defined placement, and stops setting data-spirit-placement from useDropdownAriaProps (placement still drives classes via useDropdownStyleProps).
  5. Refreshes web SCSS demos/docs and visual snapshots for the new composition patterns.

Summary

Category Count
Blocking 0
Non-blocking 2
Questions 1
Praise 1

Findings by file

1. packages/web-react/src/components/ValidationText/ValidationText.tsx — List rendering

issue (non-blocking): Array validationText items use key={\validationText_${item}`}`, which collides when two entries share the same string.

File:

{Array.isArray(validationText) ? (
<ul>
{validationText.map((item) => (
<li key={`validationText_${item}`}>{item}</li>
))}

Fix: Use the index (or a stable id if messages can be reordered) as part of the key, e.g. key={\validationText_${index}_${String(item)}`}`.

Why: Duplicate messages produce duplicate React keys, which can cause incorrect reconciliation and confusing updates.


2. packages/web-react/src/hooks/useAriaIds.ts — External aria-describedby

suggestion (non-blocking): otherAriaIds is only read on the initial render; if a parent passes a new aria-describedby string later, internal state does not resync.

File:

export const useAriaIds = (otherAriaIds?: string): [string[], RegisterType] => {
const [ids, setIds] = useState<string[]>(otherAriaIds ? otherAriaIds.split(' ') : []);

Fix: Consider syncing when otherAriaIds changes, e.g. useEffect(() => { setIds(otherAriaIds ? otherAriaIds.split(' ') : []); }, [otherAriaIds]); — only if controlled updates to aria-describedby are in scope; otherwise document that the hook treats the initial string as static.

Why: Avoids stale aria-describedby when the prop is driven by parent state.


3. packages/web-react/src/components/Dropdown/useDropdownAriaProps.tsdata-spirit-placement

question (non-blocking): data-spirit-placement was removed from popover contentProps; placement classes still come from useDropdownStyleProps.

File:

}
export const useDropdownAriaProps = (props: UseDropdownAriaPropsProps): UseDropdownAriaPropsReturn => {
const { fullWidthMode, id, isOpen, toggleHandler } = props;
const triggerProps = {
[NAME_ARIA_EXPANDED]: isOpen,
[NAME_ARIA_CONTROLS]: String(id),
onClick: toggleHandler,
};
const contentProps = {
id,
[NAME_DATA_FULLWIDTHMODE]: fullWidthMode,
};

Why: Confirm this matches product/docs expectations for scripts or tests that read data-spirit-placement on the React-rendered popover.


Verdict

COMMENT — One non-blocking issue (list keys); no blocking findings.


praise: Strong refactor: clear separation of Label / HelperText / ValidationText, consistent PropsProvider + useAriaIds wiring across inputs, thorough formFieldContextPropsTest coverage, and good README/demo alignment for consumers building custom fields.

Open in Web View Automation 

Sent by Cursor Automation: Spirit Cursor Code Review

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Code Review: refactor/DS-2398-react-standalone-form-fields

Scope: 16 commits, 168 files (+7519 / −2731)

Overview

  1. Extracts Label, HelperText, and ValidationText into standalone components with style hooks, demos, stories, and package exports; removes the Field barrel and ./components/Field export map entry.
  2. Moves useAriaIds to hooks/, wires registerAria from form controls into helper/validation text for aria-describedby, and adds shared formFieldContextPropsTest coverage.
  3. Updates PropsProvider merge semantics so nested providers can clear inherited keys (e.g. null), with JSDoc aligned to direct-prop precedence.
  4. Adjusts Dropdown so useDropdownContext always exposes placement (with default) and stops duplicating placement on the popover via data-spirit-placement (placement remains on the styled popover path).

Summary

Severity Count
Blocking 0
Non-blocking 2
Questions 0
Notes 1

Verdict: APPROVE

No blocking findings. One suggestion on list item keys in ValidationText (duplicate string messages) and a note on documenting the Field removal for consumers. Nice work on tests and context precedence coverage.


Full review (Conventional Comments, file-grouped)

See automation output / thread for grouped findings with permalinks.

Open in Web View Automation 

Sent by Cursor Automation: Spirit Cursor Code Review

… all components #DS-2398

Drop Field components as they are all already split into standalone components.
…lacement demo

- web-react: resolve placement in useDropdownContext; stop setting data-spirit-placement on popover
- web: add placement-bottom-start on modal dropdown popover demo
@crishpeen crishpeen force-pushed the refactor/DS-2398-react-standalone-form-fields branch from 971a82b to 9c34369 Compare March 26, 2026 10:40
@crishpeen crishpeen merged commit bc6a810 into release/v5-essence Mar 26, 2026
37 checks passed
@crishpeen crishpeen deleted the refactor/DS-2398-react-standalone-form-fields branch March 26, 2026 11:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

refactoring A code change that neither fixes a bug nor adds a feature run-visual-tests Runs visual regression testing on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants