Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
7a147a0
feat(notifications): type filters, date grouping, compact rows
tsahimatsliah Jun 17, 2026
ea7f9bf
refactor(notifications): social-style compact rows, drop date headers
tsahimatsliah Jun 17, 2026
9397648
fix(notifications): hard-clamp row text to keep rows compact
tsahimatsliah Jun 17, 2026
4cc5456
chore: re-trigger CI/preview build
tsahimatsliah Jun 17, 2026
50181c5
fix(notifications): align rows, date back on the right, new filter set
tsahimatsliah Jun 17, 2026
1b6ac1d
refactor(notifications): flat settings-style rows, sticky filters
tsahimatsliah Jun 17, 2026
3aa4a18
feat(notifications): always-visible filters, flat V2, article card
tsahimatsliah Jun 17, 2026
9dfe17a
fix(notifications): always show the full filter set
tsahimatsliah Jun 18, 2026
0c12327
feat(notifications): move filter tabs into the page header
tsahimatsliah Jun 18, 2026
586377a
feat(notifications): move type filters into the sidebar rail panel
tsahimatsliah Jun 18, 2026
3ccb43a
refactor(notifications): cleaner two-level row, fix article gap
tsahimatsliah Jun 18, 2026
269375b
fix(notifications): only highlight the active filter in the rail panel
tsahimatsliah Jun 18, 2026
c4364e5
refactor(notifications): research-backed row — uniform height, readab…
tsahimatsliah Jun 18, 2026
e38500c
feat(notifications): time-grouped feed + consistent bold titles
tsahimatsliah Jun 19, 2026
fffd0f3
refactor(notifications): fixed right edge, image below text, calmer h…
tsahimatsliah Jun 19, 2026
aab518d
feat(notifications): colored type badge + small thumbnail, inline time
tsahimatsliah Jun 19, 2026
32cb482
fix(notifications): stop the snippet duplicating the title
tsahimatsliah Jun 20, 2026
fb36fec
refactor(notifications): date pinned right, badges only for personal …
tsahimatsliah Jun 20, 2026
bc482d1
feat(notifications): add settings gear to mobile header
tsahimatsliah Jun 20, 2026
c1774ea
fix(notifications): hide snippet when it duplicates the title
tsahimatsliah Jun 20, 2026
977defc
fix(notifications): lift type badge above avatar, fill the glyph
tsahimatsliah Jun 20, 2026
9a81209
fix(notifications): allow snippet up to 3 lines
tsahimatsliah Jun 20, 2026
95f014f
docs(storybook): add NotificationItem stories for all cases
tsahimatsliah Jun 21, 2026
7fe7809
fix(notifications): single primary avatar for consistent alignment
tsahimatsliah Jun 21, 2026
418a503
feat(notifications): overlapping avatar stack for multiple actors
tsahimatsliah Jun 21, 2026
c779514
docs(storybook): comprehensive NotificationItem showcase with alignme…
tsahimatsliah Jun 21, 2026
eb39a41
feat(notifications): compact relative date + full date on hover
tsahimatsliah Jun 21, 2026
c312c32
refactor(notifications): top-aligned row, left-aligned lead, rect thu…
tsahimatsliah Jun 21, 2026
0a0f452
docs(storybook): cover all 49 notification types + desktop/mobile views
tsahimatsliah Jun 21, 2026
3232c36
refactor(notifications): rect avatar stack w/ tooltip, square cover, …
tsahimatsliah Jun 21, 2026
8dbd75e
feat(notifications): show referenced post title as subtitle
tsahimatsliah Jun 21, 2026
10e1dbd
feat(notifications): surface post context alongside the comment
tsahimatsliah Jun 21, 2026
032d9ec
refactor(notifications): inline time, three-dots top-right, trim bott…
tsahimatsliah Jun 21, 2026
1bfe33e
refactor(notifications): 2x2 grid for multi-actor lead, center avatars
tsahimatsliah Jun 21, 2026
6798b8b
refactor(notifications): smaller rotated kebab in title row, cover bo…
tsahimatsliah Jun 21, 2026
27c1c11
refactor(notifications): separate-box avatar grid, flat kebab, center…
tsahimatsliah Jun 21, 2026
35187da
fix(notifications): top-align lead + cover with the title
tsahimatsliah Jun 21, 2026
55873be
docs(storybook): add real mobile-viewport story for the notification …
tsahimatsliah Jun 21, 2026
31dab53
style(notifications): nudge lead/cover down a touch for 2-line centering
tsahimatsliah Jun 21, 2026
99eb9c2
fix(notifications): kebab no longer overlaps the cover image
tsahimatsliah Jun 21, 2026
bf1c9c1
fix(notifications): grid only for more than three actors
tsahimatsliah Jun 21, 2026
35dd3f0
fix(notifications): flat (tertiary) kebab, confirmed mobile-default v…
tsahimatsliah Jun 21, 2026
5d3938b
fix(notifications): reserved menu slot so cover images stay aligned
tsahimatsliah Jun 21, 2026
b6c4780
refactor(notifications): reuse category badge icons in rail panel
tsahimatsliah Jun 21, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -91,50 +91,79 @@ describe('notification attachment', () => {
expect(img).toHaveAttribute('src', attachment.image);
});

it('should have a title', async () => {
it('should show the post title as the subtitle (and cover image) when there is no comment', async () => {
const [attachment] = sampleNotificationAttachments;
renderComponent(<NotificationItem {...sampleNotification} />);
renderComponent(
<NotificationItem {...sampleNotification} description={undefined} />,
);
await screen.findByText(attachment.title);
await screen.findByAltText(`Cover preview of: ${attachment.title}`);
});

it('should not repeat the post title when it matches the notification title', async () => {
renderComponent(
<NotificationItem
{...sampleNotification}
description={undefined}
title="<p>Same post title</p>"
attachments={[
{ ...sampleNotificationAttachments[0], title: 'Same post title' },
]}
/>,
);
const matches = await screen.findAllByText('Same post title');
expect(matches).toHaveLength(1);
});
});

describe('notification avatars', () => {
it('should display the avatar of the source', async () => {
const [source] = sampleNotificationAvatars;
renderComponent(<NotificationItem {...sampleNotification} />);
// A single avatar renders the rich source/user avatar (multiple actors
// render as an overlapping stack instead).
renderComponent(
<NotificationItem {...sampleNotification} avatars={[source]} />,
);
const img = await screen.findByAltText(`${source.referenceId}'s profile`);
expect(img).toHaveAttribute('src', source.image);
});

it('should display the avatar of the user', async () => {
const [, user] = sampleNotificationAvatars;
renderComponent(<NotificationItem {...sampleNotification} />);
// Only the primary (first) avatar is shown, so render the user as the
// single avatar to verify user avatars render.
renderComponent(
<NotificationItem {...sampleNotification} avatars={[user]} />,
);
const img = await screen.findByAltText(`${user.referenceId}'s profile`);
expect(img).toHaveAttribute('src', user.image);
});

it('should not display anything if the type is unknown', async () => {
const [source] = sampleNotificationAvatars;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const notif = { ...sampleNotification } as any;
notif.avatars[1].type = 'Test';
const [, user] = sampleNotificationAvatars;
renderComponent(<NotificationItem {...notif} />);
const img = screen.queryByAltText(`${user.referenceId}'s profile`);
const unknownAvatar = { ...source, type: 'Test' } as any;
renderComponent(
<NotificationItem {...sampleNotification} avatars={[unknownAvatar]} />,
);
const img = screen.queryByAltText(`${source.referenceId}'s profile`);
expect(img).not.toBeInTheDocument();
});
});

describe('notification item', () => {
it('should display the icon of the notification', async () => {
renderComponent(<NotificationItem {...sampleNotification} />);
renderComponent(
<NotificationItem {...sampleNotification} avatars={undefined} />,
);
const testid = `notification-${sampleNotification.icon}`;
const img = await screen.findByTestId(testid);
expect(img).toBeInTheDocument();
});

it('should display the default icon if the passed icon is unknown', async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const notification = { ...sampleNotification } as any; // using any to validate unknown icon
const notification = { ...sampleNotification, avatars: undefined } as any; // using any to validate unknown icon
notification.icon = 'new notif';
renderComponent(<NotificationItem {...notification} />);
const testid = `notification-${notification.icon}`;
Expand All @@ -154,6 +183,18 @@ describe('notification item', () => {
renderComponent(<NotificationItem {...sampleNotification} />);
await screen.findByText(sampleNotificationDescription);
});

it('should not render the description when it duplicates the title', async () => {
renderComponent(
<NotificationItem
{...sampleNotification}
title="<p>Exactly the same</p>"
description="<p>Exactly the same</p>"
/>,
);
const matches = await screen.findAllByText('Exactly the same');
expect(matches).toHaveLength(1);
});
});

describe('notification click if onClick prop is provided', () => {
Expand Down
Loading
Loading