{
  "name": "NotificationsPanel",
  "source": "src/components/NotificationsPanel.tsx",
  "propsType": "NotificationsPanelProps",
  "description": "NotificationsPanel — what the bell opens: notifications grouped by day,\nunread ones marked, each one openable and markable, and a way to clear\nthem all.\n\nCONTENT, NOT A CONTAINER. It is the panel's contents; put it in a Popover\nunder the bell on a desktop — whose `title` names it — or a Drawer on a\nphone. It owns no data: the caller passes `notifications` and updates them\nfrom `onReadChange` and `onMarkAllRead`.\n\nUNREAD IS SAID, NOT ONLY SHOWN. An unread notification has a dot and a\nheavier title; to a screen reader its title begins \"Unread:\". The bar\ncounts them in words. The bell that opens it should say the count too —\n\"Notifications, 3 unread\" — for the same reason.\n\nNOTHING THAT HAS FOCUS DISAPPEARS. Each notification's toggle stays where\nit is and swaps \"Mark as read\" for \"Mark as unread\", rather than vanishing\nonce pressed. \"Mark all as read\" does go — there is nothing left to mark —\nso focus moves to the first notification first, not to the top of the page.\nEach change is announced in a status region that is always mounted.\n\nGROUPED BY DAY, NEWEST FIRST. Today, Yesterday, Earlier — each a heading,\nso a screen reader can jump between them — counted from the local calendar\nday, not from 24-hour windows.",
  "import": "import { NotificationsPanel } from 'ionbase-ui';",
  "status": "stable",
  "summary": "What the bell opens: notifications grouped by day — Today, Yesterday, Earlier — with unread ones marked in words as well as a dot, a read toggle on each that never disappears, Mark all as read, an empty state and a loading state. Content for a Popover or a Drawer; the caller owns the data.",
  "useWhen": [
    "the product tells a user about things that happened while they were elsewhere — an approval waiting, a run that failed, a mention",
    "each item can be opened, and whether it has been seen matters"
  ],
  "useInstead": [
    {
      "when": "the result of what the user just did — saved, sent, deleted",
      "use": "Toast",
      "why": "it comes and goes by itself; a notification waits in the list"
    },
    {
      "when": "a notice about the whole product — maintenance, a trial ending",
      "use": "Banner",
      "why": "it is seen on every page without opening anything"
    },
    {
      "when": "the history of one record — who changed it and when",
      "use": "List",
      "why": "an activity log has nothing to mark as read; Timeline, when it ships, is the record's history"
    }
  ],
  "composition": {
    "order": [
      "Button",
      "Popover",
      "NotificationsPanel"
    ],
    "note": "The bell is a Button whose name says the count — \"Notifications, 3 unread\" — and opens a Popover titled Notifications, lg, whose content is the panel. On a phone use a Drawer instead. The caller keeps the list and updates it from `onReadChange` and `onMarkAllRead`; `href` makes a title a link, `onOpen` a button. With `notifications` empty it shows \"You’re all caught up\"; with `isLoading`, rows of Skeleton and \"Loading notifications\" announced.",
    "example": "<Popover title=\"Notifications\" size=\"lg\" content={<NotificationsPanel notifications={items} onReadChange={setRead} onMarkAllRead={readAll} />}><Button aria-label=\"Notifications, 3 unread\" … /></Popover>"
  },
  "a11y": {
    "requires": [
      "the container's name — the Popover's or Drawer's `title`, \"Notifications\"",
      "the bell's name includes the unread count, since its badge is not read",
      "`labels` — translated, with `unread` as a function of the count"
    ],
    "guarantees": [
      "an unread notification's title begins \"Unread:\" for a screen reader; the dot and weight are not the only signal",
      "the unread count is in words in the bar",
      "each group is a section named by its heading, so a screen reader can jump between days",
      "each toggle stays where it is and swaps Mark as read for Mark as unread — focus is never on something that vanishes",
      "Mark all as read moves focus to the first notification before it goes",
      "every change is announced in a status region that is always mounted",
      "groups are counted from the local calendar day, not 24-hour windows; each has a machine-readable <time>"
    ],
    "notes": [
      "opening an unread notification marks it read, through `onReadChange`",
      "the list scrolls inside the panel past 28rem, so the popover never runs off the screen"
    ]
  },
  "antiPatterns": [
    {
      "dont": "a blue dot as the only sign a notification is unread",
      "do": "NotificationsPanel, which also says \"Unread:\" and counts them",
      "why": "a dot is silent to a screen reader and missing in forced colours"
    },
    {
      "dont": "<Button aria-label=\"Notifications\"><Badge>3</Badge></Button>",
      "do": "<Button aria-label=\"Notifications, 3 unread\" />",
      "why": "the badge's number is not part of the name that is read"
    },
    {
      "dont": "a Mark as read button that is removed once pressed",
      "do": "the panel's toggle, which becomes Mark as unread",
      "why": "removing the focused button sends a keyboard user to the top of the page"
    }
  ],
  "stylesheet": "src/styles/notifications-panel.css",
  "tokens": [
    "--border-subtle",
    "--border-width-default",
    "--border-width-thick",
    "--font-family-sans",
    "--font-weight-semibold",
    "--radius-full",
    "--radius-xs",
    "--ring-focus",
    "--spacing-12",
    "--spacing-16",
    "--spacing-2",
    "--spacing-32",
    "--spacing-4",
    "--spacing-6",
    "--spacing-8",
    "--surface-primary",
    "--text-default",
    "--text-secondary",
    "--type-body-sm",
    "--type-body-sm-line-height",
    "--type-caption",
    "--type-caption-line-height"
  ],
  "props": {
    "notifications": {
      "type": "NotificationItem[]",
      "required": true,
      "origin": "own"
    },
    "onReadChange": {
      "type": "((id: string, isRead: boolean) => void) | undefined",
      "required": false,
      "origin": "own",
      "description": "A notification's read state changed — its toggle was pressed, or it was\nopened while unread. The caller owns the data and updates it."
    },
    "onMarkAllRead": {
      "type": "(() => void) | undefined",
      "required": false,
      "origin": "own",
      "description": "Shows \"Mark all as read\" while anything is unread."
    },
    "onOpen": {
      "type": "((id: string) => void) | undefined",
      "required": false,
      "origin": "own",
      "description": "A notification was opened — its title pressed. With `href` it is also a\nlink. An unread one is marked read as well."
    },
    "isLoading": {
      "type": "boolean | undefined",
      "required": false,
      "origin": "own",
      "description": "Rows of Skeleton in place of the list, while the first fetch runs."
    },
    "now": {
      "type": "Date | undefined",
      "required": false,
      "origin": "own",
      "description": "\"Today\" and \"Yesterday\" are counted from here. Defaults to now."
    },
    "locale": {
      "type": "string | undefined",
      "required": false,
      "origin": "own",
      "description": "For the times shown; defaults to the browser's."
    },
    "headingLevel": {
      "type": "EmptyStateHeadingLevel | undefined",
      "required": false,
      "origin": "own",
      "description": "The group headings' level. Defaults to 3, under the popover's title."
    },
    "labels": {
      "type": "Partial<NotificationsPanelLabels> | undefined",
      "required": false,
      "origin": "own",
      "description": "Every string it shows or announces, for translation."
    }
  },
  "propCounts": {
    "own": 9,
    "aria": 0,
    "dom": 277,
    "other": 0
  }
}
