feat(ui): notification manager#1689
Conversation
Signed-off-by: Vladislav Schur <u.shchur@sap.com>
|
|
There was a problem hiding this comment.
Pull request overview
Adds a new NotificationManager component to ui-components that wraps the Sonner toast system and exposes a Juno-flavored toast API, alongside initial Storybook stories and Vitest coverage.
Changes:
- Introduces
NotificationManager(Sonner<Toaster />wrapper) and an exportedtoastAPI with semantic helpers (info/success/warning/error/danger). - Adds Storybook stories demonstrating basic usage, modal overlay behavior, and ShadowRoot caveats.
- Adds tests covering basic rendering, semantic toast rendering, dismissal by id, and multi-manager scenarios; updates dependencies/lockfile to include
sonner.
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Adds the sonner dependency resolution to the workspace lockfile. |
| packages/ui-components/package.json | Adds sonner@2.0.7 to ui-components dependencies. |
| packages/ui-components/src/components/NotificationManager/NotificationManager.types.tsx | Defines the public toast/option types and a customToast helper. |
| packages/ui-components/src/components/NotificationManager/NotificationManager.component.tsx | Implements NotificationManager and the semantic toast wrapper over Sonner. |
| packages/ui-components/src/components/NotificationManager/NotificationManager.test.tsx | Adds Vitest tests for toaster rendering, semantic toast content, dismissal, and scoping. |
| packages/ui-components/src/components/NotificationManager/NotificationManager.stories.tsx | Adds Storybook stories for common integration patterns and known Sonner limitations. |
| packages/ui-components/src/components/NotificationManager/index.ts | Exports NotificationManager and toast from the component module. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (1)
packages/ui-components/src/components/NotificationManager/NotificationManager.component.tsx:173
- The
dismissibleprop (and per-toastcloseButtonoption) isn’t actually enforced for semantic toasts because the renderedToastcomponent always shows a close icon and can hide itself on click. To honor the API contract, the close control should be hidden/disabled when dismissal is not allowed (and ideally dismissal should always go through Sonner’sdismiss).
return customToast(
({ dismiss }) => (
<Toast variant={variant} onDismiss={dismiss}>
<div className="jn:flex jn:flex-col jn:gap-1">
<div>{title}</div>
{description ? <div className="jn:text-theme-medium">{description}</div> : null}
</div>
</Toast>
| */ | ||
|
|
||
| import React from "react" | ||
| import { Meta, StoryObj } from "@storybook/react-vite" |
| type ToastAction = { | ||
| label: React.ReactNode | ||
| onClick: () => void | ||
| actionButtonStyle?: CSSProperties | ||
| } |
| // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access | ||
| export type SonnerCustomToast = ( | ||
| _content: (_t: { dismiss: () => void; id: ToastId }) => React.ReactNode, | ||
| _options?: NotificationOptions | ||
| ) => ToastId | ||
| // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access | ||
| export const customToast = (sonnerToast as any).custom as SonnerCustomToast |
| export const NotificationManager = ({ | ||
| dismissible = true, | ||
| duration = 4000, | ||
| visibleToasts = 3, | ||
| position = "bottom-right", | ||
| ...props | ||
| }: NotificationManagerProps) => ( | ||
| <Toaster | ||
| expand | ||
| className="juno-notification-manager" | ||
| closeButton={dismissible} | ||
| duration={duration} | ||
| visibleToasts={visibleToasts} | ||
| position={position} | ||
| toastOptions={{ | ||
| classNames: { toast: "juno-toast" }, | ||
| }} | ||
| {...props} | ||
| /> |
| const title = typeof message === "function" ? message() : message | ||
| const description = typeof data?.description === "function" ? data.description() : data?.description | ||
|
|
||
| // const { description: _description, ...customOptions } = data || {} |
| /* | ||
| * SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and Juno contributors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
| import type { CSSProperties, ReactNode } from "react" | ||
| import { toast as sonnerToast } from "sonner" |
Summary
Changes Made
Related Issues
Screenshots (if applicable)
Testing Instructions
pnpm ipnpm TASKChecklist
PR Manifesto
Review the PR Manifesto for best practises.