Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Core: Add duration and onClick support to Notification API/UI #26696

Merged
merged 13 commits into from
Apr 3, 2024
Merged
35 changes: 17 additions & 18 deletions code/lib/manager-api/src/modules/notifications.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { API_Notification } from '@storybook/types';
import partition from 'lodash/partition.js';
import type { ModuleFn } from '../lib/types';

export interface SubState {
Expand All @@ -25,26 +26,24 @@ export interface SubAPI {

export const init: ModuleFn = ({ store }) => {
const api: SubAPI = {
addNotification: (notification) => {
// Get rid of it if already exists
api.clearNotification(notification.id);

const { notifications } = store.getState();

store.setState({ notifications: [...notifications, notification] });
addNotification: (newNotification) => {
store.setState(({ notifications }) => {
const [existing, others] = partition(notifications, (n) => n.id === newNotification.id);
existing.forEach((notification) => {
if (notification.onClear) notification.onClear({ dismissed: false, timeout: false });
});
return { notifications: [...others, newNotification] };
});
},

clearNotification: (id) => {
const { notifications } = store.getState();

const notification = notifications.find((n) => n.id === id);

if (notification) {
store.setState({ notifications: notifications.filter((n) => n.id !== id) });
if (notification.onClear) {
notification.onClear({ dismissed: false });
}
}
clearNotification: (notificationId) => {
store.setState(({ notifications }) => {
const [matching, others] = partition(notifications, (n) => n.id === notificationId);
matching.forEach((notification) => {
if (notification.onClear) notification.onClear({ dismissed: false, timeout: false });
});
return { notifications: others };
});
},
};

Expand Down
31 changes: 15 additions & 16 deletions code/lib/manager-api/src/tests/notifications.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,33 @@ import { describe, it, expect, vi } from 'vitest';
import { init as initNotifications } from '../modules/notifications';

describe('notifications API', () => {
it('allows adding notifications', () => {
const store = {
getState: () => ({
notifications: [],
}),
setState: vi.fn(),
};
const store = {
state: { notifications: [] },
getState: () => store.state,
setState: (update) => {
if (typeof update === 'function') {
store.state = update(store.state);
} else {
store.state = update;
}
},
};

it('allows adding notifications', () => {
const { api } = initNotifications({ store });

api.addNotification({ id: '1' });
expect(store.setState).toHaveBeenCalledWith({
expect(store.getState()).toEqual({
notifications: [{ id: '1' }],
});
});

it('allows removing notifications', () => {
const store = {
getState: () => ({
notifications: [{ id: '1' }, { id: '2' }, { id: '3' }],
}),
setState: vi.fn(),
};

store.setState({ notifications: [{ id: '1' }, { id: '2' }, { id: '3' }] });
const { api } = initNotifications({ store });

api.clearNotification('2');
expect(store.setState).toHaveBeenCalledWith({
expect(store.getState()).toEqual({
notifications: [{ id: '1' }, { id: '3' }],
});
});
Expand Down
17 changes: 15 additions & 2 deletions code/lib/types/src/modules/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,20 @@ export interface API_SidebarOptions {

interface OnClearOptions {
/**
* True when the user dismissed the notification.
* True when the user manually dismissed the notification.
*/
dismissed: boolean;
/**
* True when the notification timed out after the set duration.
*/
timeout: boolean;
ghengeveld marked this conversation as resolved.
Show resolved Hide resolved
}

interface OnClickOptions {
/**
* Function to dismiss the notification.
*/
onDismiss: () => void;
}

/**
Expand All @@ -130,14 +141,16 @@ interface DeprecatedIconType {
}
export interface API_Notification {
id: string;
link: string;
content: {
headline: string;
subHeadline?: string | any;
};
duration?: number;
link?: string;
// TODO: Remove DeprecatedIconType in 9.0
icon?: React.ReactNode | DeprecatedIconType;
onClear?: (options: OnClearOptions) => void;
onClick?: (options: OnClickOptions) => void;
}

type API_Versions = Record<string, string>;
Expand Down
Loading
Loading