Fleet UI: Align toast icon with first line of message - #50449
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #50449 +/- ##
=======================================
Coverage 68.12% 68.13%
=======================================
Files 3947 3947
Lines 251480 251501 +21
Branches 13299 13307 +8
=======================================
+ Hits 171320 171349 +29
+ Misses 64843 64834 -9
- Partials 15317 15318 +1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
| ariaLabel="Dismiss notification" | ||
| onClick={handleClose} | ||
| > | ||
| <Icon name="close" color="ui-fleet-black-75" /> |
There was a problem hiding this comment.
Waiting for #50195 to merge to use icon prop. Will rebase and update before this is ready for merge!
| onClick={toggle} | ||
| > | ||
| <span | ||
| <Icon |
There was a problem hiding this comment.
Waiting for #50195 to merge to use icon prop. Will rebase and update before this is ready for merge!
|
Ready, except 2 instances of waiting for #50195 to merge to use icon prop. Will rebase and update before this is ready for merge! |
There was a problem hiding this comment.
Warning
- Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.
Pull request overview
This PR updates the ToastNotification UI so the status icon aligns with the first line of a wrapping message (matching the intended design), and refactors toast action buttons to use the shared Button component while preserving a11y wiring.
Changes:
- Adjust toast layout/styles to keep the icon visually pinned to the first line on multi-line messages.
- Refactor toast action controls (chevron + dismiss) to use
Button variant="subdued"and preservearia-controlsvia a newariaControlsprop onButton. - Add Storybook stories for multi-line success/error to validate wrapping alignment.
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/components/ToastNotification/ToastNotification.stories.tsx | Adds multi-line toast stories to verify icon alignment on wrapped messages. |
| frontend/components/ToastNotification/ToastCard.tsx | Switches action elements to shared Button and wires ariaControls for expand/collapse. |
| frontend/components/ToastNotification/_styles.scss | Updates header/action/icon alignment and chevron rotation styling. |
| frontend/components/buttons/Button/Button.tsx | Adds support for aria-controls via new ariaControls prop. |
| changes/49752-toast-icon-alignment | Change note (content excluded from review). |
Files excluded by content exclusion policy (1)
- changes/49752-toast-icon-alignment
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ariaExpanded?: boolean; | ||
| ariaControls?: string; |
| // Match __icon's one-line-tall box so the action buttons visually | ||
| // track the first line of text when the message wraps. | ||
| height: calc(#{$x-small} * #{$line-height}); |
There was a problem hiding this comment.
Going to pass on this suggested fix. Reasoning is that we don't want to make the click area smaller by using a smaller button.
Screen.Recording.2026-08-03.at.11.23.10.AM.mov
Drop align-items: center on __header so the icon-row and action-row — now both one text-line tall — stack against the top of the flex line and track the first line when the message wraps. Also switch the chevron / close buttons to <Button variant="subdued"> for the icon-only styling and add the multi-line success + error Storybook stories.
Migrate the toast's chevron and close buttons off the child <Icon> pattern to the new icon prop from #50195. Drop the explicit ui-fleet-black-75 color on close (subdued's default matches) and move the chevron rotation className from the Icon wrapper up to the Button so the existing __chevron / __chevron--open selectors still resolve. Also add two tests covering aria-controls on Button — attribute present when ariaControls is provided, omitted when undefined.
ea0770e to
c70006b
Compare
There was a problem hiding this comment.
Warning
- Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.
Pull request overview
Copilot reviewed 5 out of 6 changed files in this pull request and generated no new comments.
Files excluded by content exclusion policy (1)
- changes/49752-toast-icon-alignment
- Note in the __actions SCSS comment that subdued Buttons intentionally overflow the ~21px "first-line" band — the mismatch is what centers each button on the first text line. - Drop the redundant transform: rotate(0deg) on the chevron resting state; keep the transition on the base selector, rotation only on --open. - Move the two new aria-controls tests adjacent to the ariaLabel warning tests so all aria assertions cluster together.
WalkthroughUpdated the shared Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/components/ToastNotification/ToastCard.tsx`:
- Around line 127-140: Update handleKeyDown in ToastCard so Enter activation
triggers toggle only once: either prevent the native button activation after the
manual onClick call with evt.preventDefault(), or remove the manual Enter
activation and rely on the native behavior. Add or update a test asserting that
pressing Enter performs exactly one toggle of the error details panel.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: a51a9622-8095-4e66-9427-87555df4af3e
📒 Files selected for processing (6)
changes/49752-toast-icon-alignmentfrontend/components/ToastNotification/ToastCard.tsxfrontend/components/ToastNotification/ToastNotification.stories.tsxfrontend/components/ToastNotification/_styles.scssfrontend/components/buttons/Button/Button.tests.tsxfrontend/components/buttons/Button/Button.tsx
| <Button | ||
| className={classnames(`${baseClass}__chevron`, { | ||
| [`${baseClass}__chevron--open`]: isOpen, | ||
| })} | ||
| variant="subdued" | ||
| icon="chevron-down" | ||
| ariaExpanded={isOpen} | ||
| ariaControls={panelId} | ||
| ariaLabel={ | ||
| isOpen ? "Collapse error details" : "Expand error details" | ||
| } | ||
| onClick={toggle} | ||
| > | ||
| <span | ||
| className={classnames(`${baseClass}__chevron`, { | ||
| [`${baseClass}__chevron--open`]: isOpen, | ||
| })} | ||
| > | ||
| <Icon name="chevron-down" color="ui-fleet-black-75" /> | ||
| </span> | ||
| </button> | ||
| /> | ||
| )} |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Find relevant files:"
fd -a 'ToastCard\.tsx|Button\.tsx' . | sed 's#^\./##'
echo
echo "ToastCard outline:"
ast-grep outline frontend/components/ToastNotification/ToastCard.tsx --view compact || true
echo
echo "Button outline:"
ast-grep outline frontend/components/buttons/Button/Button.tsx --view compact || true
echo
echo "Relevant ToastCard section:"
sed -n '1,220p' frontend/components/ToastNotification/ToastCard.tsx | nl -ba
echo
echo "Relevant Button section:"
sed -n '1,260p' frontend/components/buttons/Button/Button.tsx | nl -baRepository: fleetdm/fleet
Length of output: 1436
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Search keyDown onClick definitions/usages:"
rg -n "handleKeyDown|onKeyDown|onClick|type=\"(submit|button|reset)\"|ariaExpanded|ariaControls" frontend/components/buttons/Button/Button.tsx frontend/components/ToastNotification/ToastCard.tsx frontend -g '*.tsx' -g '*.ts' | head -300Repository: fleetdm/fleet
Length of output: 39381
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Button.tsx relevant lines:"
awk '{printf "%7d\t%s\n", NR, $0}' frontend/components/buttons/Button/Button.tsx | sed -n '1,260p'
echo
echo "ToastCard.tsx relevant lines:"
awk '{printf "%7d\t%s\n", NR, $0}' frontend/components/ToastNotification/ToastCard.tsx | sed -n '1,220p'
echo
echo "Button tests relevant lines:"
awk '{printf "%7d\t%s\n", NR, $0}' frontend/components/buttons/Button/Button.tests.tsx | sed -n '1,230p'
echo
echo "Imports/state/toggle in ToastCard:"
python3 - <<'PY'
from pathlib import Path
p = Path('frontend/components/ToastNotification/ToastCard.tsx')
s = p.read_text()
needles = ['useState', 'toggleOpen', 'isOpen', 'panelId', 'toggle', 'onClick=']
for needle in needles:
print(f'{needle}:', [i+1 for i, line in enumerate(s.splitlines()) if needle in line])
PY
echo
echo "Call sites with Enter manual keydown and toggle-like handler:"
rg -n "handleKeyDown|onKeyDown|onClick={toggle|onClick.*toggle|ariaExpanded|aria-controls" frontend/components frontend/pages -g '*.tsx' -g '*.ts' | head -250Repository: fleetdm/fleet
Length of output: 27393
🌐 Web query:
MDN button element Enter key Enter activates button focus click handler onClick default click keyboard mouse
💡 Result:
The native HTML element is designed as an interactive, focusable element that supports both mouse and keyboard activation [1][2]. When a has focus, pressing the Enter or Space key triggers the button's activation behavior, which is functionally equivalent to a mouse click [3][4][5]. Key technical details regarding this behavior include: Keyboard Activation: For a native element, the click event fires when the user presses Enter or Space while the button is focused [3][4]. This is a built-in feature of the browser's implementation of the element [4]. Click Handler (onClick): Because the browser triggers the click event for both mouse clicks and keyboard activation (Enter/Space), a single JavaScript click event handler (e.g., element.onclick or element.addEventListener('click',...)) will reliably handle both input methods [3][4]. You do not need to add separate keydown or keyup listeners to detect Enter or Space for a native [3]. Default Behavior and Forms: The element's default type is submit [1][6]. If the button is within a , pressing Enter while focused on the button—or even while focused on certain input fields within that form—will trigger the button's activation, which typically submits the form [1][7][8]. If you do not want this behavior, you should explicitly set the type attribute to type="button" [1][2]. Important Distinction (Custom Elements): This native behavior is specific to the element [3]. If you use a non-interactive element (like a
Citations:
- 1: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/button
- 2: https://github.com/mdn/content/blob/main/files/en-us/web/html/reference/elements/button/index.md
- 3: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/button_role
- 4: https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event
- 5: https://quirksmode.org/dom/events/click.html
- 6: https://developer.mozilla.org/en-US/docs/Web/API/HTMLButtonElement
- 7: https://html.spec.whatwg.org/dev/form-elements.html
- 8: https://stackoverflow.com/questions/69788886/what-is-the-default-behavior-of-the-return-key-in-an-html-form
Call toggle only once when Enter activates the chip.
handleKeyDown invokes onClick for Enter before the native <button> keyboard activation fires handleClick. Since toggle flips state, pressing Enter can open and immediately close the error detail panel. Prevent the native activation from handleKeyDown by calling evt.preventDefault(), or remove the manual Enter activation. Add a test/assertion that Enter performs one toggle.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/components/ToastNotification/ToastCard.tsx` around lines 127 - 140,
Update handleKeyDown in ToastCard so Enter activation triggers toggle only once:
either prevent the native button activation after the manual onClick call with
evt.preventDefault(), or remove the manual Enter activation and rely on the
native behavior. Add or update a test asserting that pressing Enter performs
exactly one toggle of the error details panel.
There was a problem hiding this comment.
Will followup if not keyboard accessible
There was a problem hiding this comment.
Warning
- Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.
Pull request overview
Copilot reviewed 5 out of 6 changed files in this pull request and generated no new comments.
Files excluded by content exclusion policy (1)
- changes/49752-toast-icon-alignment
Issue
Resolves #49752
Description
The toast notification icon sat centered against the whole
__headerflex line, so on wrapping messages it drifted off the first line — the design intent was for it to pin to the first line the way it does onIconStatusMessage.align-items: centeron__header.__iconand__actionsare now each one text-line tall (calc(#{$x-small} * #{$line-height})), so the two flex children top-align and the icon stays glued to the first line as the message grows.<button>elements for<Button variant="subdued">(isIconOnly auto-detects the lone<Icon>child). Chevron keeps its rotate-on-open animation via the same__chevron/__chevron--openclassName patternActionsDropdownuses.ariaControlstoButtonso the chevron →__panela11y wiring survives the refactor.__action-button/__chevronSCSS and the$toast-notification-action-sizevariable that came with them.MultiLineSuccessandMultiLineErrorStorybook stories per the ticket's ask.Screenshots
Testing
Storybook
Components/ToastNotification/MultiLineSuccessandMultiLineError: icon sits on the first line, message wraps beneath.Storybook
Success/Error: single-line icon reads as centered with the text.Storybook
ExpandableError: chevron rotates 180° on expand/collapse with the ActionsDropdown-style 0.25s ease.ExpandableErrorkeyboard flow: Tab focuses the chevron<Button>;aria-expandedandaria-controlsstill wired to the panel.Close (
×) button dismisses.QA'd all new/changed functionality manually
Summary by CodeRabbit
Bug Fixes
Accessibility
aria-controls.Documentation