feat!: Refactor Alert component for composability#3497
Conversation
- AlertHeading and AlertText are now composable components - CTA (Call to action) customization removed since it is not part of base USWDS. The storybook example has been preserved for assistance with migration - A className can now be passed to the Alert body - Storybook examples have been updated to more closely match USWDS - Storybook now allows interactivity for this component via controls
Is there a need/desire for this to be more composable? Is there a reason to deviate from the straightforward solution of just making the prop optional and checking if both the heading and heading level is present before rendering the heading? (This would be a non breaking change.) If we had other plans to make breaking changes it may be worth codifying this logic into a discriminated union such that you get actual type errors, but otherwise might feel a bit heavy handed since the headingLevel is required and it would make all instances of not having a heading error. |
When I on boarded as a maintainer, I wanted to always be very rigid about making the components produce only the markup as USWDS recommended. But I learned through feedback and issues raised, that consumers of the library generally prefer the flexibility to compose components. Our more-recently added components tend to follow that pattern, while our oldest components are more rigid. The more rigid ones, like the Alert implementation generate more issues.
Yes (and you've landed on one alternative I considered below, but first): I think that compile-time checks should prevent consumers from improperly configuring components. That's a high-value developer experience add. Allowing props to be used in such a way that
makes the component less intuitive to use, and builds on top of a component design that already wasn't working for us. I also think we're overdue for some healthy breaking changes, I do have some bias there 😆
I started with this approach. It was fine. There was some storybook kludge that I had to add because CSF3 does not work well with component props that are unions. I was prepared to accept that, though. Ultimately, since this was already a breaking change (consumers would have to remove the redundant and previously required |
Summary
emergencyalert typecta(Call to action) customization removed since it is not part of base USWDS. The storybook example has been preserved for assistance with migrationclassNamecan now be passed to the Alert bodyValidationstory has been removed since the matching implementation is part of theValidationstorybook component. This is also slated for removal from USWDS due to usability and accessibility issuesRelated Issues or PRs
closes: #3244
closes: #3496 (supersedes)
Migration guide
The
Alertcomponent moved from a prop-driven API to a compound component API. Instead of describing an alert's contents through props (heading,headingLevel,cta) and relying onAlertto wrap your text, you now compose the alert's contents fromAlert,AlertHeading, andAlertText.This unlocks flexible composition: you control the structure and ordering of an alert's contents, and you can pass
className/standard DOM attributes to each part.What changed at a glance
<Alert>)heading="..."<AlertHeading level="...">...</AlertHeading>childheadingLevel="h4"levelprop on<AlertHeading>childrentext<AlertText>...</AlertText>to getusa-alert__textcta={...}New, non-breaking additions you may want to use: a new
emergencyvalue fortype, a newbodyClassNameprop on<Alert>, and the newAlertHeading/AlertTextexports (withAlertHeadingProps/AlertTextProps).Breaking changes
headingandheadingLevelhave been removedAlertno longer renders a heading for you. Render an<AlertHeading>child instead, and pass the heading level via its requiredlevelprop.If your alert previously had no heading, you no longer pass
headingLevelat all (it used to be required even without a heading):Body text is no longer auto-wrapped
Previously,
childrenwas automatically wrapped it in a<p className="usa-alert__text">unless using thevalidationproperty. NowAlertrenders itschildrendirectly insideusa-alert__bodywith no wrapping, which enables non-text children to be used more intuitively. To get the USWDS text paragraph, use<AlertText>:A bare string child will still render, but it will not have
usa-alert__textstyling unless you wrap it in<AlertText>.ctaThe
ctaprop has been removed. A call-to-action is not part of base USWDS. It was a project-specific customization that lived in React USWDS as a one-off. Instead of inroducing an opinionated approach to implementing a CTA, we now offer the flexibility to compose elements however your project requires.If you were using the
ctaprop, you can still build exactly the same UI as before by composing it as a child, and you now have full control over its layout and styling:validationvalidationpreviously changed howchildrenwere rendered (skipping the<p>wrapper so you could supply elements that produce semantically correct html). It was not intuitive without looking at the source code, that this was the purpose of this prop. That is especially true in the context of USWDS deprecating the Validation component.Now,
validationonly toggles theusa-alert--validationmodifier class; the inner markup is entirely yours to compose:Resulting markup
For reference, equivalent alerts produce effectively the same USWDS markup before and after — the difference is that you now author the inner structure explicitly:
How To Test
Screenshots
Author & Maintainer checklist