@servicetitan/notifications 28.5.0 → 29.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/intercept.test.d.ts +2 -0
- package/dist/__tests__/intercept.test.d.ts.map +1 -0
- package/dist/__tests__/intercept.test.js +13 -0
- package/dist/__tests__/intercept.test.js.map +1 -0
- package/dist/__tests__/notifications-service.test.d.ts +2 -0
- package/dist/__tests__/notifications-service.test.d.ts.map +1 -0
- package/dist/__tests__/notifications-service.test.js +42 -0
- package/dist/__tests__/notifications-service.test.js.map +1 -0
- package/dist/api/notifications.api.d.ts +2 -2
- package/dist/api/notifications.api.d.ts.map +1 -1
- package/dist/common.d.ts +4 -22
- package/dist/common.d.ts.map +1 -1
- package/dist/common.js +4 -11
- package/dist/common.js.map +1 -1
- package/dist/components/__tests__/notifications.test.d.ts +2 -0
- package/dist/components/__tests__/notifications.test.d.ts.map +1 -0
- package/dist/components/__tests__/notifications.test.js +93 -0
- package/dist/components/__tests__/notifications.test.js.map +1 -0
- package/dist/components/notifications.d.ts +8 -1
- package/dist/components/notifications.d.ts.map +1 -1
- package/dist/components/notifications.js +43 -23
- package/dist/components/notifications.js.map +1 -1
- package/dist/demo/action-button-preview.d.ts.map +1 -1
- package/dist/demo/action-button-preview.js +2 -2
- package/dist/demo/action-button-preview.js.map +1 -1
- package/dist/demo/basic-preview.d.ts.map +1 -1
- package/dist/demo/basic-preview.js +1 -1
- package/dist/demo/basic-preview.js.map +1 -1
- package/dist/demo/container.d.ts +1 -3
- package/dist/demo/container.d.ts.map +1 -1
- package/dist/demo/container.js.map +1 -1
- package/dist/demo/duration-preview.d.ts.map +1 -1
- package/dist/demo/duration-preview.js +2 -2
- package/dist/demo/duration-preview.js.map +1 -1
- package/dist/demo/multiline-message-preview.d.ts.map +1 -1
- package/dist/demo/multiline-message-preview.js +2 -2
- package/dist/demo/multiline-message-preview.js.map +1 -1
- package/dist/demo/prevent-duplicates-preview.d.ts.map +1 -1
- package/dist/demo/prevent-duplicates-preview.js +2 -2
- package/dist/demo/prevent-duplicates-preview.js.map +1 -1
- package/dist/demo/progress-preview.d.ts.map +1 -1
- package/dist/demo/progress-preview.js +2 -2
- package/dist/demo/progress-preview.js.map +1 -1
- package/dist/demo/server-custom-preview.d.ts +1 -1
- package/dist/demo/server-custom-preview.d.ts.map +1 -1
- package/dist/demo/server-custom-preview.js +21 -7
- package/dist/demo/server-custom-preview.js.map +1 -1
- package/dist/demo/server-custom.d.ts +0 -1
- package/dist/demo/server-custom.d.ts.map +1 -1
- package/dist/demo/server-custom.js +25 -21
- package/dist/demo/server-custom.js.map +1 -1
- package/dist/demo/server-default.d.ts.map +1 -1
- package/dist/demo/server-default.js +1 -1
- package/dist/demo/server-default.js.map +1 -1
- package/dist/demo/status-variations-preview.d.ts.map +1 -1
- package/dist/demo/status-variations-preview.js +2 -2
- package/dist/demo/status-variations-preview.js.map +1 -1
- package/dist/demo/status-variations.d.ts.map +1 -1
- package/dist/demo/status-variations.js.map +1 -1
- package/dist/index.d.ts +3 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -4
- package/dist/index.js.map +1 -1
- package/dist/intercept.d.ts +4 -0
- package/dist/intercept.d.ts.map +1 -0
- package/dist/intercept.js +5 -0
- package/dist/intercept.js.map +1 -0
- package/dist/notifications-channel.d.ts +2 -6
- package/dist/notifications-channel.d.ts.map +1 -1
- package/dist/notifications-channel.js.map +1 -1
- package/dist/notifications-service.d.ts +10 -8
- package/dist/notifications-service.d.ts.map +1 -1
- package/dist/notifications-service.js.map +1 -1
- package/dist/notifications.stories.d.ts +14 -0
- package/dist/notifications.stories.d.ts.map +1 -0
- package/dist/notifications.stories.js +20 -0
- package/dist/notifications.stories.js.map +1 -0
- package/dist/stores/__mocks__/mock-notifications-channel.d.ts +11 -0
- package/dist/stores/__mocks__/mock-notifications-channel.d.ts.map +1 -0
- package/dist/stores/__mocks__/mock-notifications-channel.js +36 -0
- package/dist/stores/__mocks__/mock-notifications-channel.js.map +1 -0
- package/dist/stores/__tests__/notifications.store.test.d.ts +2 -0
- package/dist/stores/__tests__/notifications.store.test.d.ts.map +1 -0
- package/dist/stores/__tests__/notifications.store.test.js +360 -0
- package/dist/stores/__tests__/notifications.store.test.js.map +1 -0
- package/dist/stores/notifications.store.d.ts +20 -8
- package/dist/stores/notifications.store.d.ts.map +1 -1
- package/dist/stores/notifications.store.js +156 -45
- package/dist/stores/notifications.store.js.map +1 -1
- package/dist/utils/__tests__/date-from-string.test.d.ts +2 -0
- package/dist/utils/__tests__/date-from-string.test.d.ts.map +1 -0
- package/dist/utils/__tests__/date-from-string.test.js +39 -0
- package/dist/utils/__tests__/date-from-string.test.js.map +1 -0
- package/dist/utils/__tests__/use-compatible-navigate.test.d.ts +2 -0
- package/dist/utils/__tests__/use-compatible-navigate.test.d.ts.map +1 -0
- package/dist/utils/__tests__/use-compatible-navigate.test.js +27 -0
- package/dist/utils/__tests__/use-compatible-navigate.test.js.map +1 -0
- package/dist/utils/date-from-string.d.ts.map +1 -0
- package/dist/utils/date-from-string.js +19 -0
- package/dist/utils/date-from-string.js.map +1 -0
- package/package.json +6 -6
- package/src/__tests__/intercept.test.ts +18 -0
- package/src/__tests__/notifications-service.test.ts +62 -0
- package/src/api/notifications.api.ts +2 -2
- package/src/common.ts +12 -27
- package/src/components/__tests__/notifications.test.tsx +107 -0
- package/src/components/notifications.tsx +59 -36
- package/src/demo/action-button-preview.tsx +4 -6
- package/src/demo/basic-preview.tsx +2 -4
- package/src/demo/container.tsx +1 -4
- package/src/demo/duration-preview.tsx +4 -6
- package/src/demo/multiline-message-preview.tsx +3 -9
- package/src/demo/prevent-duplicates-preview.tsx +3 -9
- package/src/demo/progress-preview.tsx +3 -9
- package/src/demo/server-custom-preview.tsx +17 -14
- package/src/demo/server-custom.tsx +30 -29
- package/src/demo/server-default.tsx +1 -3
- package/src/demo/status-variations-preview.tsx +4 -6
- package/src/demo/status-variations.tsx +0 -1
- package/src/index.ts +13 -4
- package/src/intercept.ts +14 -0
- package/src/notifications-channel.ts +2 -6
- package/src/notifications-service.ts +14 -42
- package/src/stores/__mocks__/mock-notifications-channel.ts +31 -0
- package/src/stores/__tests__/notifications.store.test.ts +458 -0
- package/src/stores/notifications.store.ts +178 -53
- package/src/utils/__tests__/date-from-string.test.ts +53 -0
- package/src/utils/__tests__/use-compatible-navigate.test.ts +43 -0
- package/src/utils/date-from-string.ts +22 -0
- package/dist/components/__tests__/container.test.d.ts +0 -2
- package/dist/components/__tests__/container.test.d.ts.map +0 -1
- package/dist/components/__tests__/container.test.js +0 -59
- package/dist/components/__tests__/container.test.js.map +0 -1
- package/dist/components/container.d.ts +0 -3
- package/dist/components/container.d.ts.map +0 -1
- package/dist/components/container.js +0 -26
- package/dist/components/container.js.map +0 -1
- package/dist/components/default-notification.d.ts +0 -7
- package/dist/components/default-notification.d.ts.map +0 -1
- package/dist/components/default-notification.js +0 -26
- package/dist/components/default-notification.js.map +0 -1
- package/dist/components/default-notification.module.css +0 -3
- package/dist/components/no-ssr.d.ts +0 -5
- package/dist/components/no-ssr.d.ts.map +0 -1
- package/dist/components/no-ssr.js +0 -9
- package/dist/components/no-ssr.js.map +0 -1
- package/dist/components/notifications-unwrapped.d.ts +0 -3
- package/dist/components/notifications-unwrapped.d.ts.map +0 -1
- package/dist/components/notifications-unwrapped.js +0 -27
- package/dist/components/notifications-unwrapped.js.map +0 -1
- package/dist/components/shadow-dom.d.ts +0 -3
- package/dist/components/shadow-dom.d.ts.map +0 -1
- package/dist/components/shadow-dom.js +0 -41
- package/dist/components/shadow-dom.js.map +0 -1
- package/dist/components/use-style-sheets.d.ts +0 -2
- package/dist/components/use-style-sheets.d.ts.map +0 -1
- package/dist/components/use-style-sheets.js +0 -15
- package/dist/components/use-style-sheets.js.map +0 -1
- package/dist/create-element.d.ts +0 -2
- package/dist/create-element.d.ts.map +0 -1
- package/dist/create-element.js +0 -8
- package/dist/create-element.js.map +0 -1
- package/dist/date-from-string.d.ts.map +0 -1
- package/dist/date-from-string.js +0 -27
- package/dist/date-from-string.js.map +0 -1
- package/dist/register.d.ts +0 -3
- package/dist/register.d.ts.map +0 -1
- package/dist/register.js +0 -5
- package/dist/register.js.map +0 -1
- package/src/components/__tests__/container.test.tsx +0 -71
- package/src/components/container.tsx +0 -35
- package/src/components/default-notification.module.css +0 -3
- package/src/components/default-notification.module.css.d.ts +0 -4
- package/src/components/default-notification.tsx +0 -59
- package/src/components/no-ssr.tsx +0 -11
- package/src/components/notifications-unwrapped.tsx +0 -51
- package/src/components/shadow-dom.tsx +0 -53
- package/src/components/use-style-sheets.ts +0 -19
- package/src/create-element.ts +0 -12
- package/src/date-from-string.ts +0 -30
- package/src/register.ts +0 -6
- /package/dist/{date-from-string.d.ts → utils/date-from-string.d.ts} +0 -0
@@ -0,0 +1,107 @@
|
|
1
|
+
import { injectable, useOptionalDependencies } from '@servicetitan/react-ioc';
|
2
|
+
import { mockComponent } from '@servicetitan/testing-library';
|
3
|
+
import { render, screen } from '@testing-library/react';
|
4
|
+
import { FC, Fragment } from 'react';
|
5
|
+
|
6
|
+
import { NotificationsApi } from '../../api/notifications.api';
|
7
|
+
import { NotificationsStore } from '../../stores/notifications.store';
|
8
|
+
import { NotificationsService } from '../../notifications-service';
|
9
|
+
import { useCompatibleNavigate } from '../../utils/use-compatible-navigate';
|
10
|
+
|
11
|
+
import { Notifications } from '../notifications';
|
12
|
+
|
13
|
+
jest.mock('@servicetitan/anvil2', () => mockComponent('Toaster', { renderProps: false }));
|
14
|
+
jest.mock('../../utils/use-compatible-navigate', () => ({
|
15
|
+
useCompatibleNavigate: jest.fn(),
|
16
|
+
}));
|
17
|
+
|
18
|
+
@injectable()
|
19
|
+
class MockNotificationsApi implements NotificationsApi {
|
20
|
+
getNotification = jest.fn();
|
21
|
+
getNotifications = jest.fn();
|
22
|
+
changeIsReadProperty = jest.fn();
|
23
|
+
}
|
24
|
+
|
25
|
+
describe(Notifications.name, () => {
|
26
|
+
let providedApi: NotificationsApi | undefined;
|
27
|
+
let providedService: NotificationsService | undefined;
|
28
|
+
let providedStore: NotificationsStore | undefined;
|
29
|
+
let props: Parameters<typeof Notifications>[0];
|
30
|
+
|
31
|
+
const Component: FC = () => {
|
32
|
+
[providedApi, providedService, providedStore] = useOptionalDependencies(
|
33
|
+
NotificationsApi,
|
34
|
+
NotificationsService,
|
35
|
+
NotificationsStore
|
36
|
+
);
|
37
|
+
return null;
|
38
|
+
};
|
39
|
+
|
40
|
+
beforeEach(() => (props = {}));
|
41
|
+
|
42
|
+
const subject = () =>
|
43
|
+
render(
|
44
|
+
<Notifications {...props}>
|
45
|
+
<Component />
|
46
|
+
</Notifications>
|
47
|
+
);
|
48
|
+
|
49
|
+
test('renders Toaster', () => {
|
50
|
+
subject();
|
51
|
+
|
52
|
+
expect(screen).toContainComponent('Toaster');
|
53
|
+
});
|
54
|
+
|
55
|
+
test('provides NotificationService', () => {
|
56
|
+
subject();
|
57
|
+
|
58
|
+
expect(providedService).toBeInstanceOf(NotificationsService);
|
59
|
+
});
|
60
|
+
|
61
|
+
test('provides NotificationStore', () => {
|
62
|
+
subject();
|
63
|
+
|
64
|
+
expect(providedStore).toBeInstanceOf(NotificationsStore);
|
65
|
+
});
|
66
|
+
|
67
|
+
test('sets store.navigate to useCompatibleNavigate result', () => {
|
68
|
+
const navigate = jest.fn();
|
69
|
+
jest.mocked(useCompatibleNavigate).mockReturnValue(navigate);
|
70
|
+
|
71
|
+
subject();
|
72
|
+
|
73
|
+
expect(providedStore!.navigate).toBe(navigate);
|
74
|
+
});
|
75
|
+
|
76
|
+
test('disposes store when component is unmounted', () => {
|
77
|
+
const { unmount } = subject();
|
78
|
+
const disposeSpy = jest.spyOn(providedStore!, 'dispose');
|
79
|
+
|
80
|
+
unmount();
|
81
|
+
|
82
|
+
expect(disposeSpy).toHaveBeenCalled();
|
83
|
+
});
|
84
|
+
|
85
|
+
test('renders one Toaster when there are are multiple notifications', () => {
|
86
|
+
render(
|
87
|
+
<Fragment>
|
88
|
+
<Notifications>
|
89
|
+
<Notifications />
|
90
|
+
</Notifications>
|
91
|
+
<Notifications />
|
92
|
+
</Fragment>
|
93
|
+
);
|
94
|
+
|
95
|
+
expect(screen.getAllByText('<Toaster />')).toHaveLength(1);
|
96
|
+
});
|
97
|
+
|
98
|
+
describe('when given an apiService', () => {
|
99
|
+
beforeEach(() => (props.apiService = MockNotificationsApi));
|
100
|
+
|
101
|
+
test('provides NotificationsApi', () => {
|
102
|
+
subject();
|
103
|
+
|
104
|
+
expect(providedApi).toBeInstanceOf(MockNotificationsApi);
|
105
|
+
});
|
106
|
+
});
|
107
|
+
});
|
@@ -1,25 +1,19 @@
|
|
1
|
-
import {
|
2
|
-
|
3
|
-
import { Provider, interfaces } from '@servicetitan/react-ioc';
|
4
|
-
|
1
|
+
import { Toaster } from '@servicetitan/anvil2';
|
2
|
+
import { Provider, interfaces, useDependencies } from '@servicetitan/react-ioc';
|
5
3
|
import {
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
FC,
|
5
|
+
PropsWithChildren,
|
6
|
+
useCallback,
|
7
|
+
useEffect,
|
8
|
+
useRef,
|
9
|
+
useState,
|
10
|
+
useSyncExternalStore,
|
11
|
+
} from 'react';
|
12
|
+
|
13
|
+
import { NotificationsApi } from '../api/notifications.api';
|
10
14
|
import { NotificationsStore } from '../stores/notifications.store';
|
15
|
+
import { useCompatibleNavigate } from '../utils/use-compatible-navigate';
|
11
16
|
import { NotificationsService } from '../notifications-service';
|
12
|
-
import { register } from '../register';
|
13
|
-
|
14
|
-
import {
|
15
|
-
DEFAULT_CLIENT_NOTIFICATION_TYPE,
|
16
|
-
DEFAULT_SERVER_NOTIFICATION_TYPE,
|
17
|
-
DefaultNotificationOptions,
|
18
|
-
Status,
|
19
|
-
} from '../common';
|
20
|
-
|
21
|
-
import { DefaultNotification } from './default-notification';
|
22
|
-
import { NotificationsUnwrapped } from './notifications-unwrapped';
|
23
17
|
|
24
18
|
export interface NotificationsProps extends PropsWithChildren<{}> {
|
25
19
|
apiService?: interfaces.Newable<NotificationsApi>;
|
@@ -38,26 +32,55 @@ export const Notifications: FC<NotificationsProps> = ({ apiService, children })
|
|
38
32
|
</Provider>
|
39
33
|
);
|
40
34
|
|
41
|
-
|
42
|
-
|
35
|
+
declare global {
|
36
|
+
/**
|
37
|
+
* This variable ensures there is one and only one \<Toaster />.
|
38
|
+
* It contains the unique id of the instance that owns the Toaster.
|
39
|
+
*/
|
40
|
+
// eslint-disable-next-line no-var
|
41
|
+
var STNotificationsActiveId: string | undefined;
|
42
|
+
}
|
43
|
+
|
44
|
+
const NotificationsUnwrapped: FC = () => {
|
45
|
+
// Configure and clean up store
|
43
46
|
|
44
|
-
|
45
|
-
|
47
|
+
const [store] = useDependencies(NotificationsStore);
|
48
|
+
const navigate = useCompatibleNavigate();
|
49
|
+
useEffect(() => {
|
50
|
+
store.navigate = navigate;
|
51
|
+
return () => {
|
52
|
+
store.dispose();
|
53
|
+
};
|
54
|
+
}, [store, navigate]);
|
46
55
|
|
47
|
-
|
48
|
-
const payload = notification.payload as DefaultServerNotificationPayload;
|
56
|
+
// Use useSyncExternalStore to ensure we re-render when the active id changes
|
49
57
|
|
50
|
-
|
51
|
-
|
52
|
-
|
58
|
+
const [thisId] = useState(() => makeId());
|
59
|
+
const syncExternalStoreCallback = useRef<Function>();
|
60
|
+
const subscribe = useCallback((callback: Function) => {
|
61
|
+
syncExternalStoreCallback.current = callback;
|
62
|
+
return () => (syncExternalStoreCallback.current = undefined);
|
63
|
+
}, []);
|
64
|
+
const activeId = useSyncExternalStore(subscribe, () => globalThis.STNotificationsActiveId);
|
53
65
|
|
54
|
-
|
55
|
-
return <DefaultNotification {...payload} duration={0} onClose={onClose} />;
|
66
|
+
// Set and clean up active id
|
56
67
|
|
57
|
-
|
58
|
-
|
68
|
+
useEffect(() => {
|
69
|
+
if (globalThis.STNotificationsActiveId === undefined) {
|
70
|
+
globalThis.STNotificationsActiveId = thisId;
|
71
|
+
syncExternalStoreCallback.current?.();
|
72
|
+
}
|
73
|
+
return () => {
|
74
|
+
if (globalThis.STNotificationsActiveId === thisId) {
|
75
|
+
globalThis.STNotificationsActiveId = undefined;
|
76
|
+
syncExternalStoreCallback.current?.();
|
77
|
+
}
|
78
|
+
};
|
79
|
+
}, [syncExternalStoreCallback, thisId]);
|
59
80
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
81
|
+
return activeId === thisId ? <Toaster id={'st-notifications-' + thisId} /> : null;
|
82
|
+
};
|
83
|
+
|
84
|
+
function makeId() {
|
85
|
+
return Math.round(1e8 * Math.random()).toString(36);
|
86
|
+
}
|
@@ -1,8 +1,6 @@
|
|
1
|
-
import {
|
2
|
-
|
1
|
+
import { Button, Flex } from '@servicetitan/anvil2';
|
3
2
|
import { useDependencies } from '@servicetitan/react-ioc';
|
4
|
-
|
5
|
-
import { ButtonGroup, Button } from '@servicetitan/design-system';
|
3
|
+
import { FC } from 'react';
|
6
4
|
|
7
5
|
import { NotificationsService } from '..';
|
8
6
|
|
@@ -38,10 +36,10 @@ export const ActionButtonPreview: FC = () => {
|
|
38
36
|
});
|
39
37
|
|
40
38
|
return (
|
41
|
-
<
|
39
|
+
<Flex direction="row" gap="2">
|
42
40
|
<Button onClick={handleWithLinkClick}>With Link</Button>
|
43
41
|
<Button onClick={handleWithExternalLinkClick}>With Link (new tab)</Button>
|
44
42
|
<Button onClick={handleWithHandlerClick}>With Handler</Button>
|
45
|
-
</
|
43
|
+
</Flex>
|
46
44
|
);
|
47
45
|
};
|
@@ -1,8 +1,6 @@
|
|
1
|
-
import {
|
2
|
-
|
1
|
+
import { Button } from '@servicetitan/anvil2';
|
3
2
|
import { useDependencies } from '@servicetitan/react-ioc';
|
4
|
-
|
5
|
-
import { Button } from '@servicetitan/design-system';
|
3
|
+
import { FC } from 'react';
|
6
4
|
|
7
5
|
import { NotificationsService } from '..';
|
8
6
|
|
package/src/demo/container.tsx
CHANGED
@@ -3,12 +3,9 @@ import { useEffect, useMemo, FC } from 'react';
|
|
3
3
|
import { Provider, useDependencies } from '@servicetitan/react-ioc';
|
4
4
|
|
5
5
|
import { Notification } from '../api/notifications.api';
|
6
|
-
|
7
6
|
import { NOTIFICATIONS_CHANNEL_TOKEN, Notifications, NotificationsService } from '..';
|
8
7
|
|
9
|
-
|
10
|
-
(notification: Notification): void;
|
11
|
-
}
|
8
|
+
type EventCallback = (notification: Notification) => void;
|
12
9
|
|
13
10
|
class Publisher {
|
14
11
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
@@ -1,8 +1,6 @@
|
|
1
|
-
import {
|
2
|
-
|
1
|
+
import { Button, Flex } from '@servicetitan/anvil2';
|
3
2
|
import { useDependencies } from '@servicetitan/react-ioc';
|
4
|
-
|
5
|
-
import { ButtonGroup, Button } from '@servicetitan/design-system';
|
3
|
+
import { FC } from 'react';
|
6
4
|
|
7
5
|
import { NotificationsService } from '..';
|
8
6
|
|
@@ -22,9 +20,9 @@ export const DurationPreview: FC = () => {
|
|
22
20
|
});
|
23
21
|
|
24
22
|
return (
|
25
|
-
<
|
23
|
+
<Flex direction="row" gap="2">
|
26
24
|
<Button onClick={handleOneSecondClick}>1 second</Button>
|
27
25
|
<Button onClick={handleUnlimitedClick}>Unlimited</Button>
|
28
|
-
</
|
26
|
+
</Flex>
|
29
27
|
);
|
30
28
|
};
|
@@ -1,8 +1,6 @@
|
|
1
|
-
import {
|
2
|
-
|
1
|
+
import { Button } from '@servicetitan/anvil2';
|
3
2
|
import { useDependencies } from '@servicetitan/react-ioc';
|
4
|
-
|
5
|
-
import { ButtonGroup, Button } from '@servicetitan/design-system';
|
3
|
+
import { FC } from 'react';
|
6
4
|
|
7
5
|
import { NotificationsService } from '..';
|
8
6
|
|
@@ -15,9 +13,5 @@ export const MultilineMessagePreview: FC = () => {
|
|
15
13
|
message: '1. First\r\n2. Second\r\n3. Third',
|
16
14
|
});
|
17
15
|
|
18
|
-
return
|
19
|
-
<ButtonGroup>
|
20
|
-
<Button onClick={handleClick}>Show</Button>
|
21
|
-
</ButtonGroup>
|
22
|
-
);
|
16
|
+
return <Button onClick={handleClick}>Show</Button>;
|
23
17
|
};
|
@@ -1,8 +1,6 @@
|
|
1
|
-
import {
|
2
|
-
|
1
|
+
import { Button } from '@servicetitan/anvil2';
|
3
2
|
import { useDependencies } from '@servicetitan/react-ioc';
|
4
|
-
|
5
|
-
import { ButtonGroup, Button } from '@servicetitan/design-system';
|
3
|
+
import { FC } from 'react';
|
6
4
|
|
7
5
|
import { NotificationsService } from '..';
|
8
6
|
|
@@ -17,9 +15,5 @@ export const PreventDuplicatesPreview: FC = () => {
|
|
17
15
|
true
|
18
16
|
);
|
19
17
|
|
20
|
-
return
|
21
|
-
<ButtonGroup>
|
22
|
-
<Button onClick={handleClick}>Show</Button>
|
23
|
-
</ButtonGroup>
|
24
|
-
);
|
18
|
+
return <Button onClick={handleClick}>Show</Button>;
|
25
19
|
};
|
@@ -1,8 +1,6 @@
|
|
1
|
-
import {
|
2
|
-
|
1
|
+
import { Button } from '@servicetitan/anvil2';
|
3
2
|
import { useDependencies } from '@servicetitan/react-ioc';
|
4
|
-
|
5
|
-
import { ButtonGroup, Button } from '@servicetitan/design-system';
|
3
|
+
import { FC } from 'react';
|
6
4
|
|
7
5
|
import { NotificationsService } from '..';
|
8
6
|
|
@@ -15,9 +13,5 @@ export const ProgressPreview: FC = () => {
|
|
15
13
|
progress: 25,
|
16
14
|
});
|
17
15
|
|
18
|
-
return
|
19
|
-
<ButtonGroup>
|
20
|
-
<Button onClick={handleClick}>Show</Button>
|
21
|
-
</ButtonGroup>
|
22
|
-
);
|
16
|
+
return <Button onClick={handleClick}>Show</Button>;
|
23
17
|
};
|
@@ -1,16 +1,19 @@
|
|
1
|
-
import {
|
1
|
+
import { intercept } from '../intercept';
|
2
2
|
|
3
|
-
|
3
|
+
export function setupIntercept() {
|
4
|
+
intercept<{ username: string }>('CustomServerNotification', notification => {
|
5
|
+
const { username, ...payload } = notification.payload;
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
});
|
7
|
+
return {
|
8
|
+
...notification,
|
9
|
+
payload: {
|
10
|
+
...payload,
|
11
|
+
title: `Hi ${username}!`,
|
12
|
+
action: {
|
13
|
+
label: 'Thanks!',
|
14
|
+
onClick: () => window.alert("You're welcome!"),
|
15
|
+
},
|
16
|
+
},
|
17
|
+
};
|
18
|
+
});
|
19
|
+
}
|
@@ -1,36 +1,37 @@
|
|
1
|
-
import {
|
2
|
-
|
3
|
-
import { Button } from '@servicetitan/design-system';
|
1
|
+
import { Button } from '@servicetitan/anvil2';
|
2
|
+
import { FC, useEffect } from 'react';
|
4
3
|
|
5
4
|
import { NotificationProcessStatus } from '..';
|
6
|
-
|
7
5
|
import { Container } from './container';
|
6
|
+
import { setupIntercept } from './server-custom-preview';
|
8
7
|
|
9
|
-
|
8
|
+
export const ServerCustomExample: FC = () => {
|
9
|
+
useEffect(setupIntercept, []);
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
11
|
+
return (
|
12
|
+
<Container>
|
13
|
+
{publisher => {
|
14
|
+
const handleClick = () => {
|
15
|
+
const notification = {
|
16
|
+
id: Date.now(),
|
17
|
+
userId: 0,
|
18
|
+
type: 'CustomServerNotification',
|
19
|
+
status: NotificationProcessStatus.Info,
|
20
|
+
isRead: false,
|
21
|
+
createdOn: new Date(),
|
22
|
+
modifiedOn: new Date(),
|
23
|
+
version: NotificationProcessStatus.Info,
|
24
|
+
payload: JSON.stringify({
|
25
|
+
username: 'Jane',
|
26
|
+
message: 'Your order is completed.',
|
27
|
+
}),
|
28
|
+
};
|
29
29
|
|
30
|
-
|
31
|
-
|
30
|
+
publisher.fire(notification);
|
31
|
+
};
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
);
|
33
|
+
return <Button onClick={handleClick}>Show</Button>;
|
34
|
+
}}
|
35
|
+
</Container>
|
36
|
+
);
|
37
|
+
};
|
@@ -1,9 +1,7 @@
|
|
1
|
+
import { Button } from '@servicetitan/anvil2';
|
1
2
|
import { FC } from 'react';
|
2
3
|
|
3
|
-
import { Button } from '@servicetitan/design-system';
|
4
|
-
|
5
4
|
import { NotificationProcessStatus } from '..';
|
6
|
-
|
7
5
|
import { Container } from './container';
|
8
6
|
|
9
7
|
export const ServerDefaultExample: FC = () => (
|
@@ -1,8 +1,6 @@
|
|
1
|
-
import {
|
2
|
-
|
1
|
+
import { Button, Flex } from '@servicetitan/anvil2';
|
3
2
|
import { useDependencies } from '@servicetitan/react-ioc';
|
4
|
-
|
5
|
-
import { ButtonGroup, Button } from '@servicetitan/design-system';
|
3
|
+
import { FC } from 'react';
|
6
4
|
|
7
5
|
import { NotificationsService } from '..';
|
8
6
|
|
@@ -30,11 +28,11 @@ export const StatusVariationsPreview: FC = () => {
|
|
30
28
|
});
|
31
29
|
|
32
30
|
return (
|
33
|
-
<
|
31
|
+
<Flex direction="row" gap="2">
|
34
32
|
<Button onClick={handleInfoClick}>Info</Button>
|
35
33
|
<Button onClick={handleSuccessClick}>Success</Button>
|
36
34
|
<Button onClick={handleWarningClick}>Warning</Button>
|
37
35
|
<Button onClick={handleErrorClick}>Error</Button>
|
38
|
-
</
|
36
|
+
</Flex>
|
39
37
|
);
|
40
38
|
};
|
package/src/index.ts
CHANGED
@@ -1,7 +1,16 @@
|
|
1
|
-
export {
|
2
|
-
|
1
|
+
export {
|
2
|
+
NotificationsApi,
|
3
|
+
NotificationProcessStatus,
|
4
|
+
DefaultServerNotificationPayload,
|
5
|
+
} from './api/notifications.api';
|
3
6
|
export { Notifications, NotificationsProps } from './components/notifications';
|
4
|
-
export {
|
7
|
+
export {
|
8
|
+
DefaultNotificationOptions,
|
9
|
+
LinkAction,
|
10
|
+
FunctionAction,
|
11
|
+
Notification,
|
12
|
+
Status,
|
13
|
+
} from './common';
|
14
|
+
export { intercept, NotificationInterceptor } from './intercept';
|
5
15
|
export { NotificationsChannel, NOTIFICATIONS_CHANNEL_TOKEN } from './notifications-channel';
|
6
16
|
export { INotificationsService, NotificationsService } from './notifications-service';
|
7
|
-
export { register } from './register';
|
package/src/intercept.ts
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
import { DefaultNotificationOptions, Notification } from './common';
|
2
|
+
import { NotificationsStore } from './stores/notifications.store';
|
3
|
+
|
4
|
+
export type NotificationInterceptor<T extends Record<keyof any, any> = {}> = (
|
5
|
+
notification: Notification<T>,
|
6
|
+
dismiss: () => void
|
7
|
+
) => Notification<DefaultNotificationOptions> | undefined;
|
8
|
+
|
9
|
+
export function intercept<T extends Record<keyof any, any> = {}>(
|
10
|
+
type: string,
|
11
|
+
fn?: NotificationInterceptor<T>
|
12
|
+
) {
|
13
|
+
NotificationsStore.intercept(type, fn);
|
14
|
+
}
|
@@ -1,8 +1,6 @@
|
|
1
1
|
import { symbolToken } from '@servicetitan/react-ioc';
|
2
2
|
|
3
|
-
|
4
|
-
(context: any, data: any): void;
|
5
|
-
}
|
3
|
+
type EventCallback = (data: any, context: any) => void;
|
6
4
|
|
7
5
|
interface EventsDispatcher {
|
8
6
|
bind(eventName: string, callback: EventCallback, context?: any): this;
|
@@ -14,9 +12,7 @@ interface Publisher extends EventsDispatcher {
|
|
14
12
|
global_emitter: EventsDispatcher;
|
15
13
|
}
|
16
14
|
|
17
|
-
export
|
18
|
-
(): Publisher;
|
19
|
-
}
|
15
|
+
export type NotificationsChannel = () => Publisher;
|
20
16
|
|
21
17
|
export const NOTIFICATIONS_CHANNEL_TOKEN = symbolToken<NotificationsChannel>(
|
22
18
|
'NOTIFICATIONS_CHANNEL_TOKEN'
|
@@ -4,13 +4,15 @@ import { DefaultNotificationOptions, Status } from './common';
|
|
4
4
|
|
5
5
|
import { NotificationsStore } from './stores/notifications.store';
|
6
6
|
|
7
|
+
type OptionsWithoutStatus = Omit<DefaultNotificationOptions, 'status'>;
|
8
|
+
|
7
9
|
export interface INotificationsService {
|
8
10
|
initialize(userId?: number): Promise<void>;
|
9
11
|
show(options: DefaultNotificationOptions, preventDuplicates?: boolean): void;
|
10
|
-
info(options:
|
11
|
-
success(options:
|
12
|
-
warning(options:
|
13
|
-
error(options:
|
12
|
+
info(options: OptionsWithoutStatus, preventDuplicates?: boolean): void;
|
13
|
+
success(options: OptionsWithoutStatus, preventDuplicates?: boolean): void;
|
14
|
+
warning(options: OptionsWithoutStatus, preventDuplicates?: boolean): void;
|
15
|
+
error(options: OptionsWithoutStatus, preventDuplicates?: boolean): void;
|
14
16
|
}
|
15
17
|
|
16
18
|
@injectable()
|
@@ -25,49 +27,19 @@ export class NotificationsService implements INotificationsService {
|
|
25
27
|
this.store.add(options, preventDuplicates);
|
26
28
|
};
|
27
29
|
|
28
|
-
info = (options:
|
29
|
-
this.store.add(
|
30
|
-
{
|
31
|
-
...options,
|
32
|
-
status: Status.Info,
|
33
|
-
},
|
34
|
-
preventDuplicates
|
35
|
-
);
|
30
|
+
info = (options: OptionsWithoutStatus, preventDuplicates?: boolean) => {
|
31
|
+
this.store.add({ ...options, status: Status.Info }, preventDuplicates);
|
36
32
|
};
|
37
33
|
|
38
|
-
success = (
|
39
|
-
options:
|
40
|
-
preventDuplicates?: boolean
|
41
|
-
) => {
|
42
|
-
this.store.add(
|
43
|
-
{
|
44
|
-
...options,
|
45
|
-
status: Status.Success,
|
46
|
-
},
|
47
|
-
preventDuplicates
|
48
|
-
);
|
34
|
+
success = (options: OptionsWithoutStatus, preventDuplicates?: boolean) => {
|
35
|
+
this.store.add({ ...options, status: Status.Success }, preventDuplicates);
|
49
36
|
};
|
50
37
|
|
51
|
-
warning = (
|
52
|
-
options:
|
53
|
-
preventDuplicates?: boolean
|
54
|
-
) => {
|
55
|
-
this.store.add(
|
56
|
-
{
|
57
|
-
...options,
|
58
|
-
status: Status.Warning,
|
59
|
-
},
|
60
|
-
preventDuplicates
|
61
|
-
);
|
38
|
+
warning = (options: OptionsWithoutStatus, preventDuplicates?: boolean) => {
|
39
|
+
this.store.add({ ...options, status: Status.Warning }, preventDuplicates);
|
62
40
|
};
|
63
41
|
|
64
|
-
error = (options:
|
65
|
-
this.store.add(
|
66
|
-
{
|
67
|
-
...options,
|
68
|
-
status: Status.Error,
|
69
|
-
},
|
70
|
-
preventDuplicates
|
71
|
-
);
|
42
|
+
error = (options: OptionsWithoutStatus, preventDuplicates?: boolean) => {
|
43
|
+
this.store.add({ ...options, status: Status.Error }, preventDuplicates);
|
72
44
|
};
|
73
45
|
}
|