Skip to content

Fleet UI: Device notification toast page for notify-before-patching - #51251

Draft
RachelElysia wants to merge 7 commits into
mainfrom
50916-device-notification-page
Draft

Fleet UI: Device notification toast page for notify-before-patching#51251
RachelElysia wants to merge 7 commits into
mainfrom
50916-device-notification-page

Conversation

@RachelElysia

@RachelElysia RachelElysia commented Aug 14, 2026

Copy link
Copy Markdown
Member

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 serve without the BE endpoints from #50910. It's clearly marked in DeviceNotificationPage.tsx. That, plus the invalid-token error path check and the manual FleetDesktop notify --url run 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

  • Interface + service + MSW. INotificationView, INotificationItem, INotificationAction, and the WebKit bridge message union in frontend/interfaces/device_notification.ts. getNotification and postNotificationAction in frontend/services/entities/device_notifications.ts. Handler fixtures for default 3-item, 20-item scrolling, post-update_now Installing… state, 404, and 500 in frontend/test/handlers/device-notifications-handlers.ts. Three BE contract assumptions posted on #50916 — flag on the ticket if any need to change.
  • Standalone route at /device/:token/notifications/:uuid, registered as a sibling to <Route component={DeviceUserPage}> so the Fleet Desktop toast doesn't inherit the My device shell.
  • Fleet Desktop bridge utilitypostBridgeMessage(action, payload?) in fleetDesktopBridge.ts. Guards window.webkit so the page no-ops safely when opened in a browser during dev. Posts ready once on first successful render (unblocks ToastWindow.loadTimeout), resize on every height change via ResizeObserver (initial + list scroll + action swap), error on fetch failure, primary on primary-action success, dismiss on close-action success.
  • Layout matches Figma (nodes 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.
  • Action wiring via useMutation. Primary POSTs {action: "update_now"} → server returns the Installing… view → cache updated in place → primary bridge fires and the window stays open. Secondaries POST → dismiss bridge fires so the native window closes. In-flight mutation disables both buttons. A failed POST surfaces an inline role="alert" error, covering the CoS "must not silently do nothing" clause.
  • dismiss id 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.
  • Bold markup: small inline **text** splitter (renderBoldMarkup) keeps react-markdown out of the toast bundle for one construct.

Screenrecording

  • Toast light and dark modes, primary click → Installing transition, secondary click → close.

Testing

  • Added/updated automated tests
  • QA'd all new/changed functionality manually

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 when window.webkit is absent. Dedicated bridge utility tests in fleetDesktopBridge.tests.ts.

…s for notify-before-patching toast

Scaffolds the FE contract for #50916 so page work can proceed against
mocks while the BE endpoints in #50910 land. Assumptions on the server
contract are captured in the interface file and on the sub-issue.
@codecov

codecov Bot commented Aug 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.74074% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.68%. Comparing base (ea79ecf) to head (39c9e96).
⚠️ Report is 17 commits behind head on main.

Files with missing lines Patch % Lines
...end/test/handlers/device-notifications-handlers.ts 82.85% 6 Missing ⚠️
.../DeviceNotificationPage/DeviceNotificationPage.tsx 96.36% 2 Missing ⚠️
frontend/router/paths.ts 0.00% 2 Missing ⚠️
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              
Flag Coverage Δ
frontend 62.95% <90.74%> (+0.09%) ⬆️

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.

…: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 RachelElysia changed the title Fleet UI: Scaffold device notification interface, service, and MSW handlers for notify-before-patching toast Fleet UI: Scaffold interface, service, MSW handlers, route, and page for notify-before-patching toast Aug 14, 2026
- 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 RachelElysia changed the title Fleet UI: Scaffold interface, service, MSW handlers, route, and page for notify-before-patching toast Fleet UI: Device notification toast page for notify-before-patching Aug 14, 2026
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.

1 participant