Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/notification-severity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@chimely/client": minor
"@chimely/react": minor
---

First-class notification severity (frontend slice). `WellKnownPayload` gains an optional `severity: 'high' | 'medium' | 'low'`, and the default item renders a left accent tinted by the new `colorSeverityHigh` / `colorSeverityMedium` / `colorSeverityLow` appearance variables. Any other value renders no accent. Tabs can already filter on `severity` via their `filter` predicate.
6 changes: 6 additions & 0 deletions packages/client/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ export interface WellKnownPayload {
action_url?: string;
/** Leading icon/avatar in the default rendering. */
icon_url?: string;
/**
* Relative importance. The default item shows a matching severity accent
* tinted by the `colorSeverity*` appearance variables. Any other value
* renders no accent.
*/
severity?: 'high' | 'medium' | 'low';
[custom: string]: unknown;
}

Expand Down
6 changes: 6 additions & 0 deletions packages/react/src/appearance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ export interface InboxAppearance {
colorBadgeForeground?: string;
/** Popover and pill box-shadow. Default 0 8px 24px rgba(0, 0, 0, 0.12). */
shadow?: string;
/** Accent on `severity: 'high'` items. Default #dc2626. */
colorSeverityHigh?: string;
/** Accent on `severity: 'medium'` items. Default #d97706. */
colorSeverityMedium?: string;
/** Accent on `severity: 'low'` items. Default #2563eb. */
colorSeverityLow?: string;
/** Extension point: forwarded as `--chimely-<key>` verbatim. */
[customProperty: string]: string | undefined;
};
Expand Down
11 changes: 11 additions & 0 deletions packages/react/src/components/DefaultItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,19 @@ export function DefaultItem<TPayload>(
): ReactNode {
const { item, className, style, formatTimestamp, onClick } = props;
const payload = item.payload as Partial<WellKnownPayload>;
// Payloads pass through verbatim, so guard against values outside the union.
const severity =
payload.severity === 'high' || payload.severity === 'medium' || payload.severity === 'low'
? payload.severity
: undefined;
return (
<button type="button" className={className} style={style} onClick={onClick}>
{severity && (
<span
className={`chimely-item-severity chimely-item-severity-${severity}`}
aria-hidden="true"
/>
)}
{props.renderAvatar
? props.renderAvatar({ item })
: typeof payload.icon_url === 'string' &&
Expand Down
33 changes: 33 additions & 0 deletions packages/react/src/inbox.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -848,3 +848,36 @@ describe('standalone mode', () => {
expect(stub.stream().closed).toBe(true);
});
});

describe('severity accent', () => {
test('renders a tinted accent for a known severity', async () => {
const stub = createStubServer();
stub.addNotification({ payload: { title: 'urgent', severity: 'high' } });
await renderInbox(stub);
fireEvent.click(bell());

const accent = document.querySelector('.chimely-item-severity');
expect(accent).not.toBeNull();
expect(accent?.classList.contains('chimely-item-severity-high')).toBe(true);
// Decorative only: the count already rides the item text.
expect(accent?.getAttribute('aria-hidden')).toBe('true');
});

test('renders no accent without a severity', async () => {
const stub = createStubServer();
stub.addNotification({ payload: { title: 'routine' } });
await renderInbox(stub);
fireEvent.click(bell());

expect(document.querySelector('.chimely-item-severity')).toBeNull();
});

test('ignores a severity outside the known set', async () => {
const stub = createStubServer();
stub.addNotification({ payload: { title: 'weird', severity: 'critical' } });
await renderInbox(stub);
fireEvent.click(bell());

expect(document.querySelector('.chimely-item-severity')).toBeNull();
});
});
17 changes: 17 additions & 0 deletions packages/react/src/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ export const INBOX_CSS = `
background: var(--chimely-colorMuted, #f3f4f6);
}
.chimely-item {
position: relative;
display: flex;
align-items: flex-start;
gap: 10px;
Expand Down Expand Up @@ -304,6 +305,22 @@ export const INBOX_CSS = `
background: var(--chimely-colorPrimary, #1264FF);
flex: none;
}
.chimely-item-severity {
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 3px;
}
.chimely-item-severity-high {
background: var(--chimely-colorSeverityHigh, #dc2626);
}
.chimely-item-severity-medium {
background: var(--chimely-colorSeverityMedium, #d97706);
}
.chimely-item-severity-low {
background: var(--chimely-colorSeverityLow, #2563eb);
}
.chimely-empty {
padding: 32px 16px;
text-align: center;
Expand Down