Skip to content

Fleet UI: Align toast icon with first line of message - #50449

Merged
RachelElysia merged 3 commits into
mainfrom
49752-fix-toast-icon-alignment
Aug 3, 2026
Merged

Fleet UI: Align toast icon with first line of message#50449
RachelElysia merged 3 commits into
mainfrom
49752-fix-toast-icon-alignment

Conversation

@RachelElysia

@RachelElysia RachelElysia commented Aug 3, 2026

Copy link
Copy Markdown
Member

Issue

Resolves #49752

Description

The toast notification icon sat centered against the whole __header flex 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 on IconStatusMessage.

  • Removed align-items: center on __header. __icon and __actions are 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.
  • Swapped the two hand-rolled <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--open className pattern ActionsDropdown uses.
  • Added ariaControls to Button so the chevron → __panel a11y wiring survives the refactor.
  • Dropped the orphaned __action-button / __chevron SCSS and the $toast-notification-action-size variable that came with them.
  • Added MultiLineSuccess and MultiLineError Storybook stories per the ticket's ask.

Screenshots

Screenshot 2026-08-03 at 10 21 35 AM Screenshot 2026-08-03 at 10 20 38 AM

Testing

  • Storybook Components/ToastNotification/MultiLineSuccess and MultiLineError: 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.

  • ExpandableError keyboard flow: Tab focuses the chevron <Button>; aria-expanded and aria-controls still wired to the panel.

  • Close (×) button dismisses.

  • QA'd all new/changed functionality manually

Summary by CodeRabbit

  • Bug Fixes

    • Improved icon alignment in success and error toast notifications, including single- and multi-line messages.
    • Refined toast action controls and chevron behavior for clearer expanding and dismissing interactions.
  • Accessibility

    • Added support for associating buttons with controlled content through aria-controls.
  • Documentation

    • Added examples demonstrating multi-line success and error notifications with rich message formatting.

Copilot AI review requested due to automatic review settings August 3, 2026 17:23
@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 68.13%. Comparing base (756295a) to head (72edb1b).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
...rontend/components/ToastNotification/ToastCard.tsx 0.00% 1 Missing ⚠️
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     
Flag Coverage Δ
frontend 61.35% <0.00%> (+0.04%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

ariaLabel="Dismiss notification"
onClick={handleClose}
>
<Icon name="close" color="ui-fleet-black-75" />

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Waiting for #50195 to merge to use icon prop. Will rebase and update before this is ready for merge!

onClick={toggle}
>
<span
<Icon

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Waiting for #50195 to merge to use icon prop. Will rebase and update before this is ready for merge!

@RachelElysia

Copy link
Copy Markdown
Member Author

Ready, except 2 instances of waiting for #50195 to merge to use icon prop. Will rebase and update before this is ready for merge!

Copilot AI left a comment

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.

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 preserve aria-controls via a new ariaControls prop on Button.
  • 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.

Comment on lines 49 to +50
ariaExpanded?: boolean;
ariaControls?: string;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

done

Comment on lines +131 to +133
// 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});

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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.
Copilot AI review requested due to automatic review settings August 3, 2026 18:37
@RachelElysia
RachelElysia force-pushed the 49752-fix-toast-icon-alignment branch from ea0770e to c70006b Compare August 3, 2026 18:37

Copilot AI left a comment

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.

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.
Copilot AI review requested due to automatic review settings August 3, 2026 21:04
@RachelElysia
RachelElysia marked this pull request as ready for review August 3, 2026 21:05
@RachelElysia
RachelElysia requested a review from a team as a code owner August 3, 2026 21:05
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Updated the shared Button component with optional ariaControls support and tests. Replaced native toast action buttons with shared Button components. Adjusted toast styles for first-line icon alignment and chevron behavior. Added multiline success and error Storybook stories. Documented the toast alignment fix.

Possibly related PRs

  • fleetdm/fleet#50195: Both PRs modify the shared Button component and its icon/action styling.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the primary toast icon alignment fix.
Description check ✅ Passed The description covers the issue, implementation, testing, screenshots, and manual QA; omitted generic checklist items are not relevant.
Linked Issues check ✅ Passed The changes align toast icons with the first message line and add multiline Storybook examples as required by issue #49752.
Out of Scope Changes check ✅ Passed The Button refactor, accessibility support, tests, and style cleanup directly support the toast alignment fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 49752-fix-toast-icon-alignment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

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.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 756295a and 72edb1b.

📒 Files selected for processing (6)
  • changes/49752-toast-icon-alignment
  • frontend/components/ToastNotification/ToastCard.tsx
  • frontend/components/ToastNotification/ToastNotification.stories.tsx
  • frontend/components/ToastNotification/_styles.scss
  • frontend/components/buttons/Button/Button.tests.tsx
  • frontend/components/buttons/Button/Button.tsx

Comment on lines +127 to 140
<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>
/>
)}

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.

🎯 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 -ba

Repository: 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 -300

Repository: 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 -250

Repository: 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

or ) and assign it role="button" via ARIA, the browser will not automatically treat it as a button [3]. In that case, you must manually implement keyboard support, including adding event listeners for the Enter and Space keys to trigger your action, to ensure accessibility [3]. In summary, native HTML
elements handle the Enter key activation automatically, firing the standard click event without requiring extra keyboard-specific code [3][4].

Citations:


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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Will followup if not keyboard accessible

Copilot AI left a comment

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.

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

@RachelElysia
RachelElysia merged commit e7a9456 into main Aug 3, 2026
27 checks passed
@RachelElysia
RachelElysia deleted the 49752-fix-toast-icon-alignment branch August 3, 2026 21:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Misaligned icon in new toast component

3 participants