Skip to content

feat(dev-validation): apply shared validation helpers across all Gen2 components - #6510

Open
rubencarvalho wants to merge 22 commits into
ruben/feat-dev-warning-validation-foundationfrom
ruben/feat-dev-warning-validation-components
Open

feat(dev-validation): apply shared validation helpers across all Gen2 components#6510
rubencarvalho wants to merge 22 commits into
ruben/feat-dev-warning-validation-foundationfrom
ruben/feat-dev-warning-validation-components

Conversation

@rubencarvalho

@rubencarvalho rubencarvalho commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Description

Applies the shared dev-validation helpers introduced in #6508 (the foundation branch this PR targets) across the Gen2 component set — core base classes, shared mixins, and swc subclasses.

What this does:

  • Retrofits ad hoc checks onto the shared helpers. Replaces hand-rolled window.__swc.warn + includes() call sites with validateEnum / warnIf / validateRequiredSlot / validateAllowedChildren. This reaches beyond the core base classes into the shared mixins (PendingMixin, LinearProgressMixin) and the concrete swc subclasses (Button, CloseButton, ActionButton), which each carried their own inline checks.

  • Adds validation where there was none — Accordion (density), Popover (size), Avatar (numeric size), plus new enum checks on Tooltip (variant/placement), ActionButton (staticColor), and ProgressCircle / LinearProgressMixin (staticColor / labelPosition).

  • Guards expensive checks with isDebug(). Where a condition is a DOM traversal (ProgressCircle's light-DOM check, Card's actions-slot check), the call site short-circuits when validation is off, so the traversal isn't paid on every update.

  • Fixes doc URLs on every touched file (and the style-guide examples from the foundation branch): stale opensource.adobe.com/spectrum-web-components/components/* → the Gen2 docs site spectrum-web-components.adobe.com/?path=/docs/components-*--docs.

  • Fixes 7 missing helper imports (Accordion, AlertBanner, Asset, Avatar, Badge, ButtonGroup, Card): used but not imported, so they transpiled under Vite but threw ReferenceError at runtime — only tsc --noEmit caught them.

Full per-component breakdown (all 20)
Component What changed
Badge, Tabs, IllustratedMessage enum/required/mutually-exclusive/allowed-children checks retrofitted onto the shared helpers
ProgressCircle, LinearProgressMixin (Meter) deprecation/required/value-range checks retrofitted; new staticColor/labelPosition enum checks added
Button, Card, Tooltip, ActionButton combo/required checks retrofitted; new staticColor (ActionButton), variant/placement (Tooltip) enum checks added; Card actions-slot check retrofitted
Asset, AlertBanner, StatusLight, Popover enum/deprecation/required checks retrofitted; new size enum check added (Popover)
Divider, Avatar, ButtonGroup enum checks retrofitted; new numeric size check added (Avatar)
PendingMixin, CloseButton hand-rolled window.__swc.warn call sites retrofitted onto warnIf/validateEnum
Accordion new density enum check added (previously undeclared)
ColorLoupe, ColorHandle, Icon reviewed, confirmed no changes needed (booleans/free-text only, per the "don't validate" list)

Motivation and context

Extends the helper-based pattern from the foundation branch across the entire component set — including the mixins and subclasses that validated inline — completing the "retrofit existing ad hoc call sites" goal and closing the gap on components that had no dev-mode validation at all.

Related issue(s)

Screenshots (if appropriate)

N/A — no visual changes; only dev-mode console warning behavior.

Author's checklist

  • I have read the CONTRIBUTING and PULL_REQUESTS documents.
  • I have reviewed at the Accessibility Practices for this feature, see: Aria Practices
  • I have added automated tests to cover my changes.
  • I have included a well-written changeset if my change needs to be published.
  • I have included updated documentation if my change required it.

Reviewer's checklist

  • Includes a Github Issue with appropriate flag or Jira ticket number without a link
  • Includes thoughtfully written changeset if changes suggested include patch, minor, or major features
  • Automated tests cover all use cases and follow best practices for writing
  • Validated on all supported browsers
  • All VRTs are approved before the author can update Golden Hash

Manual review test cases

  • Existing warning behavior is unchanged across the whole package

    1. Run yarn vitest --run --project storybook from 2nd-gen/packages/swc
    2. Expect all tests to pass, matching pre-retrofit behavior (no runtime behavior change)
  • Console warnings still fire correctly in Storybook

    1. Run Storybook locally (yarn storybook from 2nd-gen/packages/swc)
    2. Open any touched component's playground and set an invalid enum value (e.g. variant, size, density, placement, orientation) via Controls
    3. Expect a console warning referencing the correct doc URL (spectrum-web-components.adobe.com/?path=/docs/components-*--docs)

Device review

  • Did it pass in Desktop?
  • Did it pass in (emulated) Mobile?
  • Did it pass in (emulated) iPad?

Accessibility testing checklist

  • Keyboard: No behavioral changes; confirm no regressions in keyboard interaction across touched components (unchanged by this PR).
  • Screen reader: No ARIA/DOM changes; confirm accessible-name-related warnings (Tabs, Avatar, Button, Popover) still fire correctly for their respective missing-label cases (unchanged checks, moved to warnIf).

…elpers

First representative batch applying the shared dev-validation helpers
from the foundation branch:

- Badge: variant enum check -> validateEnum; outline/non-semantic-
  variant combo -> warnIf.
- Tabs: keyboard-activation/density/direction enum checks -> validateEnum
  (kept in their property setters alongside the existing fallback-value
  logic); required accessible-label check -> warnIf.
- IllustratedMessage: size/orientation enum checks -> validateEnum;
  heading slot allowed-children check -> validateAllowedChildren
  (previously a one-off inline tag allowlist).

Also fixes the docs URLs in these three components (and this session's
style-guide examples) from the stale opensource.adobe.com/spectrum-web-
components/components/* pattern to the actual Gen2 docs site:
spectrum-web-components.adobe.com/?path=/docs/components-*--docs.

No new required-slot check was added for IllustratedMessage's heading
slot: an existing test explicitly asserts no warning fires when it's
empty, so that's left alone rather than overridden.

All existing tests for these three components pass unchanged (97 tests
across badge/tabs/illustrated-message/dev-validation), since the new
helpers preserve enough of each original message's wording for the
existing assertions to still match.
@changeset-bot

changeset-bot Bot commented Jul 15, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 453e87c

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

VARIANTS_SEMANTIC has a fixed initializer directly on BadgeBase
(SHARED API section), unlike VARIANTS (API TO OVERRIDE, no
initializer, genuinely meant to differ per concrete subclass per its
JSDoc). Going through this.constructor for VARIANTS_SEMANTIC was
redundant indirection; the already-imported BADGE_VARIANTS_SEMANTIC
constant is identical. The VARIANTS lookup stays, since that one is
by design abstract and subclass-supplied.
@github-actions

Copy link
Copy Markdown
Contributor

📚 Branch Preview Links

🔍 Gen1 Visual Regression Test Results

When a visual regression test fails (or has previously failed while working on this branch), its results can be found in the following URLs:

Deployed to Azure Blob Storage: pr-6510

If the changes are expected, update the current_golden_images_cache hash in the circleci config to accept the new images. Instructions are included in that file.
If the changes are unexpected, you can investigate the cause of the differences and update the code accordingly.

…eter

- ProgressCircle: light-DOM-children deprecation and missing-
  accessible-name checks -> warnIf. New: staticColor enum check via
  validateEnum (previously undeclared/unvalidated despite STATIC_COLORS
  existing for styling purposes).
- LinearProgressMixin (shared by Meter): value-out-of-range and
  missing-accessible-name checks -> warnIf, keeping the mixin's own
  instance-level _hasWarned* flags as-is (they implement a distinct
  "warn once per bad-state entry, reset on recovery" policy layered on
  top of the shared warn() dedup, not something the helpers replace).
  New: labelPosition and staticColor enum checks via validateEnum
  (previously undeclared/unvalidated).
- Meter: variant enum check -> validateEnum.

All existing tests for progress-circle and meter pass unchanged (48
tests).
- Button: pending+disabled combo and icon-only-without-label checks ->
  warnIf (the latter now tagged type: 'accessibility', matching how
  every other accessible-name check in this effort is categorized).
- ActionButton: new staticColor enum check via validateEnum (previously
  undeclared/unvalidated; no core base class owns this property, so the
  check lives directly on the swc concrete class).
- Card: variant/density enum checks -> validateEnum; title-as-link-
  without-a-link-element check -> warnIf.
- Tooltip: dangling for="" reference check -> warnIf. New: variant and
  placement enum checks via validateEnum (both had a VARIANTS/PLACEMENTS
  static already declared but never actually validated against).

color-loupe was reviewed and needs no changes (boolean + free-text CSS
color property only, no enums/required/slots).

All existing tests for action-button, button, button-group, tooltip,
and card pass unchanged (142 tests).
- Asset: variant enum check -> validateEnum.
- AlertBanner: variant enum check (in the property setter) ->
  validateEnum.
- StatusLight: 'accent' deprecation and disabled-attribute-removed
  checks -> warnIf; variant enum check -> validateEnum. Fixed the
  variant check's stale opensource.adobe.com URL.
- Popover: dangling for="" reference check and required accessible-
  label-on-show check -> warnIf; placement enum check -> validateEnum.
  New: size enum check via validateEnum (VALID_SIZES existed but was
  never validated).

Updated one test (status-light) that asserted the exact old warning
message text via .toBe() rather than .toContain(); switched it to
match the new standardized validateEnum message format instead of
hand-tuning the helper's wording to preserve a stale test string.

All existing tests for asset, alert-banner, status-light, and popover
pass (90 tests).
- Divider: staticColor enum check -> validateEnum.
- Avatar: missing-alt accessibility check -> warnIf. New: numeric size
  enum check via warnIf (VALID_SIZES existed and the setter already
  fell back to the default on an invalid value, but never warned about
  it).
- ButtonGroup: orientation and align enum checks -> validateEnum.

Icon was reviewed and needs no changes (free-text label, no required
slot semantics beyond a graceful no-op when nothing is slotted).

All existing tests for divider, avatar, icon, and button-group pass
unchanged (78 tests).
ACCORDION_DENSITIES existed as the canonical valid-values source but
was never actually validated anywhere. Added via validateEnum.

ColorLoupe and ColorHandle were both already reviewed earlier this
effort and confirmed to need no changes (booleans + free-text CSS
color only).

All existing tests for accordion, color-handle, and color-loupe pass
unchanged (67 tests).
@rubencarvalho rubencarvalho changed the title feat(dev-validation): retrofit Badge, Tabs, IllustratedMessage onto shared helpers feat(dev-validation): apply shared validation helpers across all Gen2 components Jul 15, 2026
@@ -68,16 +69,12 @@ export abstract class MeterBase extends LinearProgressMixin(
protected override willUpdate(changes: PropertyValues): void {

@miwha-adobe miwha-adobe Jul 27, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In my third PR, I want to refactor this to an update or updated

I think we need more conversations in regards to our fallback behaviors.

warnIf(
this,
true,
`<${this.localName}> requires an accessible name. A default label of "${ProgressCircleBase.DEFAULT_LABEL}" has been applied, but a more specific label should be provided via:`,

@miwha-adobe miwha-adobe Jul 27, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we be coercing warnings and mutating the consumers input? This is another candidate for a refactor PR that removes these coercions and puts the responsibility of correctly setting up the components on the consumer.

}
warnIf(
this,
!this.accessibleLabel,

@miwha-adobe miwha-adobe Jul 29, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

warnIf(this, !this.accessibleLabel, …) only reads accessibleLabel. If a consumer names the tablist via aria-labelledby (or aria-label) on the host instead, this still warns even though the element has an accessible name, a false positive. Do we support those as valid naming paths for Tabs? If so, the condition should treat them as satisfied, example:

!this.accessibleLabel &&
  !this.hasAttribute('aria-label') &&
  !this.hasAttribute('aria-labelledby')


@state()
private _ariaExpanded?: string;
private _ariaExpanded?: 'true' | 'false';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Increase type safety

@miwha-adobe
miwha-adobe marked this pull request as ready for review July 29, 2026 20:43
@miwha-adobe
miwha-adobe requested a review from a team as a code owner July 29, 2026 20:43
@miwha-adobe miwha-adobe added Status:Ready for review PR ready for review or re-review. gen2 These issues or PRs map to our 2nd generation work to modernizing infrastructure. run_vrt Triggers the Chromatic VRT run for 2nd-gen labels Jul 30, 2026
}
this._hasWarnedNoAccessibleName = true;
window.__swc?.warn(
warnIf(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Dropped _hasWarnedNoAccessibleName here. warnIf's dedup is permanent for the session (keyed by localName:type:level), so fix-then-re-break within a session no longer re-warns. LinearProgressMixin kept its _hasWarned* and the same fix should apply here. ✨

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Great catch!

@miwha-adobe miwha-adobe Aug 3, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Alrighttt, so I dug into this a bit more, and both _hasWarned checks (LinearProgressMixin and Dropzone) don't actually seem to do anything 🤔.

The logic reads like it should recall the warning, but the dedup underneath swallows any identical message that already fired this session. That's 2nd gen wide, not specific to these two, so the flag never gets to re-surface anything.

For Dropzone there's a second reason on top of that: the accessible name lives in aria-label / aria labelledby, which aren't reactive, so changing them after connect doesn't trigger a re-render and the check never re-runs. The changes.has('dragged') bit looks like a re-check but it's really just a side effect of dragging, not a direct check that the name changed.

(For LinearProgressMixin the inputs are reactive, so it does re-render, but the dedup still swallows the repeat. The one warning that reappears there is value-out-of-range, and only because the value is in the message text, so it's technically a different message. That's not the flag doing anything either.)

For PR 3 I'd like to add a controller that watches aria changes so we surface the errors we expect when the name actually changes. Caveat: same message still won't re-surface because of dedup. Open to discussing whether re-surfacing is a pattern we want to support, but these one-offs feel like attempts to work around this that don't really succeed.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Added Dropzone and LinearProgressMixin refactor here: #6561

return;
}
window.__swc.warn(
warnIf(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

hasMeaningfulLightDomChildren(this) is evaluated as an arg, so it runs unconditionally every updated() — in prod too. Lost the old isDebug() early-return. ✨

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this too, great catch, ty!

@miwha-adobe
miwha-adobe requested a review from cdransf August 3, 2026 17:04
@coveralls

coveralls commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 30847227211

Warning

No base build found for commit 4efeab9 on ruben/feat-dev-warning-validation-foundation.
Coverage changes can't be calculated without a base build.
If a base build is processing, this comment will update automatically when it completes.

Coverage: 96.239%

Details

  • Patch coverage: No coverable lines changed in this PR.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

Requires a base build to compare against. How to fix this →


Coverage Stats

Coverage Status
Relevant Lines: 39268
Covered Lines: 37993
Line Coverage: 96.75%
Relevant Branches: 6465
Covered Branches: 6020
Branch Coverage: 93.12%
Branches in Coverage %: Yes
Coverage Strength: 460.58 hits per line

💛 - Coveralls

@pfulton
pfulton requested review from Rajdeepc and nikkimk August 5, 2026 19:34
@rubencarvalho rubencarvalho removed the run_vrt Triggers the Chromatic VRT run for 2nd-gen label Aug 6, 2026
@@ -140,15 +141,12 @@ export abstract class ButtonBase extends SizedMixin(SpectrumElement, {

protected override update(changedProperties: PropertyValues): void {
super.update(changedProperties);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm still trying to make sure I follow the docs correctly, but super.update(changedProperties) is called first, then the check. Per the docs the check should run before super.update()?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

My understanding is that it depends on when we want to call the warning. This one is on hasIcon, which are slot derived and so they aren't reliable until the slot is rendered, which is why it is after super.update()

@@ -475,6 +475,24 @@ export abstract class TooltipBase

protected override updated(changedProperties: PropertyValues): void {
super.updated(changedProperties);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Actually... I'm not sure what to think... Shouldn't we want to let any super class finish first before validation?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think that is a discussion worth having. Certainly the ones that rely on a render should (slots, etc). Do we lose anything from moving all warnings to after the render? It would keep a consistent pattern. The benefits to a pre-render seem somewhat marginal, though we would need to update our docs to support this pattern.

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

Labels

gen2 These issues or PRs map to our 2nd generation work to modernizing infrastructure. Status:Ready for review PR ready for review or re-review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants