va-maintenance-banner: Prevent additional slot from rendering in light DOM#1988
va-maintenance-banner: Prevent additional slot from rendering in light DOM#1988RyanMunsch merged 10 commits intomainfrom
Conversation
Also refactor approach to move logic out of component's render method.
There was a problem hiding this comment.
Pull request overview
This pull request refactors the va-maintenance-banner web component to improve its rendering logic, code organization, and accessibility. The component displays maintenance or warning banners on VA.gov based on date/time conditions and user interactions.
Changes:
- Introduced
componentWillLoadlifecycle method to centralize banner display logic, determine warning vs error state, and remove unused slots for accessibility - Refactored the
rendermethod to use cleaner conditional logic with pre-computed flags instead of nested conditions - Updated e2e tests to reflect that only the active slot remains in the light DOM
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| packages/web-components/src/components/va-maintenance-banner/va-maintenance-banner.tsx | Added componentWillLoad method for early logic execution, added isWarning and preventRender flags, implemented slot removal for accessibility, and refactored render method for clearer conditional logic |
| packages/web-components/src/components/va-maintenance-banner/test/va-maintenance-banner.e2e.ts | Updated test expectations to verify only the appropriate slot (warn-content or maintenance-content) remains in the light DOM based on banner state |
| if ( | ||
| isDateBefore(now, new Date(upcomingWarnStartDateTime)) || | ||
| isDateAfter(now, new Date(maintenanceEndDateTime)) || | ||
| window.localStorage.getItem('MAINTENANCE_BANNER') === bannerId | ||
| ) { | ||
| this.preventRender = true; | ||
| } |
There was a problem hiding this comment.
The refactored render method improves code readability by removing the nested conditional check for localStorage. However, there's now a subtle behavior change: in the old code, the localStorage check was done at render time (which happens after componentWillLoad), but now it's done in componentWillLoad. If the component is constructed and componentWillLoad runs, but the user dismisses the banner via some other mechanism before render happens, this could lead to inconsistent state. While this is a very unlikely edge case, it's worth noting the timing difference.
There was a problem hiding this comment.
For @department-of-veterans-affairs/platform-design-system-fe, I'm not sure that this is actually an issue. I was testing setting/removing that banner ID value from local storage in the Chrome devtools console and it has the same behavior here on the Chromatic deploy that it does on production.
| <div slot="maintenance-content"> | ||
| We’re working on VA.gov right now. If you have trouble signing in or using tools, check back after we’re finished. Thank you for your patience. | ||
| </div> | ||
| <div slot="warn-content"> | ||
| We’ll be doing some work on VA.gov. The maintenance will last X hours. During that time, you won’t be able to sign in or use tools. | ||
| </div> | ||
| </va-maintenance-banner> | ||
| `); | ||
| }); |
There was a problem hiding this comment.
The tests for non-rendering scenarios (when the banner is expired or before the warning start date) use attribute-based content rather than slotted content. This means there's no test coverage for verifying the slot removal behavior when preventRender is true. Consider adding a test that uses slotted content (like the rendering tests do) to verify that slot removal works correctly even when the component doesn't ultimately render.
| isWarning: boolean = false; | ||
| preventRender: boolean = false; |
There was a problem hiding this comment.
The property preventRender is declared with an initial value of false but is never reset back to false after being set to true. This could cause issues if the component instance is reused or if props change dynamically. Consider using @State() decorator for this property since it affects rendering, or ensure it's properly managed through the component lifecycle when props change.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Thanks for working on this @RyanMunsch. I was able to confirm that the component successfully leaves only the appropriate warning OR maintenance slot in the light DOM when one of them is active.
However, I found that when the date is outside both the maintenance and warning windows (for example, if the windows are in the past), the component leaves everything in the DOM.
Here are the values I used (which should match the current implementation on va.gov):
- 'upcoming-warn-start-date-time': "Mon Jun 08 2020 19:00:00 GMT-0700 (Mountain Standard Time)"
- 'maintenance-start-date-time': "Tue Jun 09 2020 07:00:00 GMT-0700 (Mountain Standard Time)"
- 'maintenance-end-date-time': "Wed Jun 10 2020 07:00:00 GMT-0700 (Mountain Standard Time)"
Can you look into methods for preventing that content from being accessible when there is no active maintenance or warning alerts?
Let me know if I am misunderstanding anything here!
|
Good catch, @amyleadem. I pushed an update that should resolve that issue. Good for another look when you have the chance. |
There was a problem hiding this comment.
This is looking good to me from an accessibility perspective! I reviewed the rendered code and found that the component now removes everything from the light DOM except for what currently applies to the component.
I had a couple of open questions related to refining the logic and code. Let me know if you have any questions.
|
Thanks for the suggestions, @amyleadem. This is ready for another round when you are. |
There was a problem hiding this comment.
LGTM! I had one open question about some possible redundant logic, but I don't think it is hurting anything. Mostly just wanting to understand.
Since there are few rounds of changes here, I'd like to one more engineering review/re-review before merge. @jamigibbs @powellkerry
| warningStartInFuture || | ||
| maintenanceEndInPast || | ||
| bothWindowsInPast || | ||
| bothWindowsInFuture || |
There was a problem hiding this comment.
question(non-blocking): Why are bothWindowsInPast and bothWindowsInFuture needed? Curious if I am missing a configuration gap that this is helping with, or if they are redundant. If they are doing some safety checks, can we add a comment explaining?
There was a problem hiding this comment.
Wow, this should have been obvious to me but just now realizing what you meant in your previous round of comments. bothWindowsInFuture is indeed not necessary due to warningStartInFuture alone being enough to prevent render. Thank you, @amyleadem!
Chromatic
https://4649-maintenance-banner-announcement--65a6e2ed2314f7b8f98609d8.chromatic.com
Description
This pull request refactors the
va-maintenance-bannerweb component to improve its logic for rendering and accessibility. The main focus is on determining when and how the maintenance banner should be displayed, ensuring unused content is removed for screen readers, and simplifying conditional rendering. Additionally, related test cases are updated to match the new logic.Component logic and rendering improvements:
componentWillLoadlifecycle method to centralize logic for:isWarning) to distinguish between warning and error states.warn-contentormaintenance-content) from the DOM for accessibility. [1] [2]rendermethod to:preventRenderflag to decide whether to render the banner.isWarning. [1] [2] [3]Accessibility improvements:
warn-contentormaintenance-content) is present in the DOM, preventing confusion for screen readers and assistive technologies.Test updates:
Code cleanup:
@Elementdecorator for DOM manipulation.Related tickets and links
Closes department-of-veterans-affairs/vets-design-system-documentation#4649
Screenshots
Before
After
Testing and review
Approvals
See the QA Checklists section below for suggested approvals. Use your best judgment if additional reviews are needed. When in doubt, request a review.
Approval groups
Add approval groups to the PR as needed:
QA checklists
Use the QA checklists below as guides, not rules. Not all checklists will apply to every PR but there could be some overlap.
In all scenarios, changes should be fully tested by the author and verified by the reviewer(s); functionality, responsiveness, etc.
✨ New Component Added
minorlabel🌱 New Component Variation Added
minorlabel🐞 Component Fix
patchlabel♿️ Component Fix - Accessibility
patchlabel🚨 Component Fix - Breaking API Change
majorlabel🔧 Component Update - Non-Breaking API Change
minorlabel📖 Storybook Update
ignore-for-releaselabel🎨 CSS-Library Update
css-librarylabel