Fleet UI: Device notification toast page for notify-before-patching - #51251
Draft
RachelElysia wants to merge 7 commits into
Draft
Fleet UI: Device notification toast page for notify-before-patching#51251RachelElysia wants to merge 7 commits into
RachelElysia wants to merge 7 commits into
Conversation
RachelElysia
temporarily deployed
to
Docker Hub
August 14, 2026 16:06 — with
GitHub Actions
Inactive
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #51251 +/- ##
==========================================
+ Coverage 68.67% 68.68% +0.01%
==========================================
Files 4000 4004 +4
Lines 258221 258331 +110
Branches 13704 13722 +18
==========================================
+ Hits 177321 177427 +106
- Misses 65153 65157 +4
Partials 15747 15747
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:
|
…:token/notifications/:uuid route Registers the route outside the DeviceUserPage wrapper so the Fleet Desktop toast window does not inherit the My device header, nav, or chrome. Page body is a placeholder — real layout, fetch, and bridge wiring land in a follow-up.
RachelElysia
temporarily deployed
to
Docker Hub
August 14, 2026 16:39 — with
GitHub Actions
Inactive
- Adds a `postBridgeMessage` utility that guards `window.webkit`, so the page no-ops safely when opened in a normal browser during dev. - Fetches the notification via useQuery with retry/refetch disabled — toast is a one-shot, server owns retries via exit codes. - Posts `ready` once after the first successful render (unblocks Fleet Desktop's 30s ToastWindow.loadTimeout) and posts `error` on fetch failure so the script exits non-zero and the server retries. Placeholder render for the fetched shape; real layout, resize posting, and action wiring land in follow-ups.
…ght/dark and resize posting - Card surface on a transparent page background — the native ToastWindow supplies rounded corners and shadow, so a page-level background would square them off. - Logo swaps via <picture> + `(prefers-color-scheme: dark)` so it tracks system appearance directly rather than Fleet's stored theme preference. Card colours follow the same media query. - Item list caps at 4.5 rows before scrolling — the half row is the affordance signaling more content below. - Small inline `**bold**` parser for title and description; keeps react-markdown out of the toast bundle for one construct. - ResizeObserver on the card posts `resize` on every height change so the native window sizes to content. - Colours, spacing, and paddings are placeholders — designer-review pass against the Figma nodes is a follow-up.
…y, subdued secondaries Uses Fleet's <Button> component so styling stays in the design system. Last (primary) action gets the default variant; earlier (secondary) actions render with variant="subdued". Removes the placeholder button CSS from _styles.scss now that <Button> owns those styles.
…twareIcon default preset - Actions: useMutation around postNotificationAction. Primary post → server returns Installing… view → write into the react-query cache so the page re-renders on the same tree. Primary bridge posts on the primary path; dismiss bridge posts on secondaries so the native window closes. - Inline error state (role="alert") when the action POST fails — covers the CoS "must not silently do nothing" clause. Buttons disable while a mutation is in-flight. - SoftwareIcon uses `size="small"` (24px) — matches other Fleet compact-row lists like PoliciesTable. Drops the hand-rolled 32px CSS override. - Styles migrated to Fleet SCSS tokens ($core-fleet-white, $ui-fleet-black-*, $pad-*, $border-radius-*) and mixins (@include flex-column-24px-gap, flex-column-8px-gap). Removes the hand-rolled @media (prefers-color-scheme: dark) overrides — color tokens carry the theme automatically. - Adds three action-path tests: primary posts primary + transitions to Installing, secondary posts dismiss, action failure surfaces an inline error and no outcome bridge.
… always closes the toast The position-last rule marks the sole Hide button in the post-update_now Installing view as the primary, but semantically Hide closes the toast. The server reuses the `dismiss` action id for any close action, so treat the id as the authoritative signal: `dismiss` id always posts the dismiss bridge, regardless of whether it's the positional primary. Adds a test that clicks Hide in a one-action Installing view and confirms `dismiss` (not `primary`) reaches the bridge.
RachelElysia
temporarily deployed
to
Docker Hub
August 14, 2026 20:32 — with
GitHub Actions
Inactive
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue
Contributes to #50916
Note
Draft — a TEMP local-preview mock is still in the working tree so I can eyeball the layout against a live
make servewithout the BE endpoints from #50910. It's clearly marked inDeviceNotificationPage.tsx. That, plus the invalid-token error path check and the manualFleetDesktop notify --urlrun on a real Mac with Fleet Desktop 1.5.0+, are the pre-merge items. Wanted the review to be able to start on the shape without waiting.Description
INotificationView,INotificationItem,INotificationAction, and the WebKit bridge message union infrontend/interfaces/device_notification.ts.getNotificationandpostNotificationActioninfrontend/services/entities/device_notifications.ts. Handler fixtures for default 3-item, 20-item scrolling, post-update_nowInstalling… state, 404, and 500 infrontend/test/handlers/device-notifications-handlers.ts. Three BE contract assumptions posted on #50916 — flag on the ticket if any need to change./device/:token/notifications/:uuid, registered as a sibling to<Route component={DeviceUserPage}>so the Fleet Desktop toast doesn't inherit the My device shell.postBridgeMessage(action, payload?)infleetDesktopBridge.ts. Guardswindow.webkitso the page no-ops safely when opened in a browser during dev. Postsreadyonce on first successful render (unblocksToastWindow.loadTimeout),resizeon every height change viaResizeObserver(initial + list scroll + action swap),erroron fetch failure,primaryon primary-action success,dismisson close-action success.5546:42302,5546:42315): 32×32 org logo top-right, title +**bold**description on the left, bordered rounded list container with 1px dividers between 48px rows,<SoftwareIcon size="small">per Fleet's compact-row convention, 4.5-row scroll (half row is the affordance for "more below"), primary action last, subdued secondaries. All colors and spacing via Fleet SCSS tokens ($core-fleet-white,$ui-fleet-black-*,$pad-*,$border-radius-*) and mixins (@include flex-column-24px-gap) — dark mode flips automatically through the CSS-variable tokens, matching macOS system appearance rather than Fleet's stored theme.useMutation. Primary POSTs{action: "update_now"}→ server returns the Installing… view → cache updated in place →primarybridge fires and the window stays open. Secondaries POST →dismissbridge fires so the native window closes. In-flight mutation disables both buttons. A failed POST surfaces an inlinerole="alert"error, covering the CoS "must not silently do nothing" clause.dismissid is the authoritative close signal (not just position). Fixes an edge case where the sole Hide button in the Installing state would otherwise be marked primary because it's positionally last. Rule:action.id === "dismiss"always closes; otherwise position-last is the primary CTA.**text**splitter (renderBoldMarkup) keepsreact-markdownout of the toast bundle for one construct.Screenrecording
Testing
yarn test frontend/pages/DeviceNotificationPage— 13 passing, 1 TEMP-skipped (the 404-renders-null test can't hold while the TEMP mock fallback is in place). Coverage: fixture render, bold markup, both logo srcs, ready-once, resize, fetch error paths, primary action → primary bridge + Installing transition, secondary → dismiss bridge, Hide/dismiss id → dismiss (Installing state), action failure → inline error and no wrong bridge, no-throw whenwindow.webkitis absent. Dedicated bridge utility tests infleetDesktopBridge.tests.ts.