@servicetitan/notifications 31.2.0 → 31.3.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.
Files changed (75) hide show
  1. package/dist/api/notifications.api.js +4 -3
  2. package/dist/api/notifications.api.js.map +1 -1
  3. package/dist/common.js +4 -3
  4. package/dist/common.js.map +1 -1
  5. package/dist/components/notifications.js +42 -22
  6. package/dist/components/notifications.js.map +1 -1
  7. package/dist/demo/action-button-preview.js +41 -23
  8. package/dist/demo/action-button-preview.js.map +1 -1
  9. package/dist/demo/action-button.js +4 -1
  10. package/dist/demo/action-button.js.map +1 -1
  11. package/dist/demo/basic-preview.js +9 -5
  12. package/dist/demo/basic-preview.js.map +1 -1
  13. package/dist/demo/basic.js +4 -1
  14. package/dist/demo/basic.js.map +1 -1
  15. package/dist/demo/container.js +44 -45
  16. package/dist/demo/container.js.map +1 -1
  17. package/dist/demo/duration-preview.js +23 -9
  18. package/dist/demo/duration-preview.js.map +1 -1
  19. package/dist/demo/duration.js +4 -1
  20. package/dist/demo/duration.js.map +1 -1
  21. package/dist/demo/index.js +1 -0
  22. package/dist/demo/index.js.map +1 -1
  23. package/dist/demo/multiline-message-preview.js +9 -5
  24. package/dist/demo/multiline-message-preview.js.map +1 -1
  25. package/dist/demo/multiline-message.js +4 -1
  26. package/dist/demo/multiline-message.js.map +1 -1
  27. package/dist/demo/prevent-duplicates-preview.js +9 -5
  28. package/dist/demo/prevent-duplicates-preview.js.map +1 -1
  29. package/dist/demo/prevent-duplicates.js +4 -1
  30. package/dist/demo/prevent-duplicates.js.map +1 -1
  31. package/dist/demo/progress-preview.js +9 -5
  32. package/dist/demo/progress-preview.js.map +1 -1
  33. package/dist/demo/progress.js +4 -1
  34. package/dist/demo/progress.js.map +1 -1
  35. package/dist/demo/server-custom-preview.js +5 -4
  36. package/dist/demo/server-custom-preview.js.map +1 -1
  37. package/dist/demo/server-custom.js +13 -7
  38. package/dist/demo/server-custom.js.map +1 -1
  39. package/dist/demo/server-default.js +66 -61
  40. package/dist/demo/server-default.js.map +1 -1
  41. package/dist/demo/status-variations-preview.js +35 -13
  42. package/dist/demo/status-variations-preview.js.map +1 -1
  43. package/dist/demo/status-variations.js +4 -1
  44. package/dist/demo/status-variations.js.map +1 -1
  45. package/dist/index.js +7 -6
  46. package/dist/index.js.map +1 -1
  47. package/dist/intercept.js +1 -0
  48. package/dist/intercept.js.map +1 -1
  49. package/dist/notifications-channel.js +1 -0
  50. package/dist/notifications-channel.js.map +1 -1
  51. package/dist/notifications-service.js +71 -69
  52. package/dist/notifications-service.js.map +1 -1
  53. package/dist/stores/notifications.store.js +210 -218
  54. package/dist/stores/notifications.store.js.map +1 -1
  55. package/dist/utils/date-from-string.js +2 -1
  56. package/dist/utils/date-from-string.js.map +1 -1
  57. package/dist/utils/use-compatible-navigate.js +4 -3
  58. package/dist/utils/use-compatible-navigate.js.map +1 -1
  59. package/package.json +8 -8
  60. package/dist/__tests__/intercept.test.js +0 -13
  61. package/dist/__tests__/intercept.test.js.map +0 -1
  62. package/dist/__tests__/notifications-service.test.js +0 -42
  63. package/dist/__tests__/notifications-service.test.js.map +0 -1
  64. package/dist/components/__tests__/notifications.test.js +0 -93
  65. package/dist/components/__tests__/notifications.test.js.map +0 -1
  66. package/dist/notifications.stories.js +0 -20
  67. package/dist/notifications.stories.js.map +0 -1
  68. package/dist/stores/__mocks__/mock-notifications-channel.js +0 -36
  69. package/dist/stores/__mocks__/mock-notifications-channel.js.map +0 -1
  70. package/dist/stores/__tests__/notifications.store.test.js +0 -367
  71. package/dist/stores/__tests__/notifications.store.test.js.map +0 -1
  72. package/dist/utils/__tests__/date-from-string.test.js +0 -45
  73. package/dist/utils/__tests__/date-from-string.test.js.map +0 -1
  74. package/dist/utils/__tests__/use-compatible-navigate.test.js +0 -27
  75. package/dist/utils/__tests__/use-compatible-navigate.test.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@servicetitan/notifications",
3
- "version": "31.2.0",
3
+ "version": "31.3.0",
4
4
  "description": "",
5
5
  "homepage": "https://docs.st.dev/docs/frontend/notifications-center",
6
6
  "repository": {
@@ -21,10 +21,10 @@
21
21
  "devDependencies": {
22
22
  "@servicetitan/anvil2": "^1.22.0",
23
23
  "@servicetitan/design-system": "~14.5.1",
24
- "@servicetitan/log-service": "^30.0.0",
25
- "@servicetitan/react-ioc": "^30.0.0",
26
- "@servicetitan/testing-library": "^3.1.0",
27
- "@servicetitan/web-components": "^30.0.0",
24
+ "@servicetitan/log-service": "^31.3.2",
25
+ "@servicetitan/react-ioc": "^31.3.2",
26
+ "@servicetitan/testing-library": "^3.2.0",
27
+ "@servicetitan/web-components": "^31.3.2",
28
28
  "@testing-library/jest-dom": "^6.4.2",
29
29
  "@testing-library/react": "^14.2.1",
30
30
  "@types/react": "~18.2.55",
@@ -33,8 +33,8 @@
33
33
  "@types/react-shadow-dom-retarget-events": "~1.0.0",
34
34
  "axios": "~0.30.0",
35
35
  "mobx": "~6.10.2",
36
- "mobx-react": "~9.0.2",
37
- "react": "~18.2.0",
36
+ "mobx-react": "^9.2.0",
37
+ "react": "^18.2.0",
38
38
  "react-dom": "~18.2.0",
39
39
  "react-router-dom": "~5.3.0"
40
40
  },
@@ -56,5 +56,5 @@
56
56
  "cli": {
57
57
  "webpack": false
58
58
  },
59
- "gitHead": "03cfa4dc1e3cb374c8593f568ae47d0996ac7bae"
59
+ "gitHead": "7fc7b9cbf766fe23879ae81831f28bdd96559bce"
60
60
  }
@@ -1,13 +0,0 @@
1
- import { NotificationsStore } from '../stores/notifications.store';
2
- import { intercept } from '../intercept';
3
- describe(intercept.name, () => {
4
- const fn = jest.fn();
5
- const type = 'NotificationType';
6
- const subject = () => intercept(type, fn);
7
- test('registers interceptor', () => {
8
- const interceptSpy = jest.spyOn(NotificationsStore, 'intercept');
9
- subject();
10
- expect(interceptSpy).toHaveBeenCalledWith(type, fn);
11
- });
12
- });
13
- //# sourceMappingURL=intercept.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"intercept.test.js","sourceRoot":"","sources":["../../src/__tests__/intercept.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAEnE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE;IAC1B,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;IACrB,MAAM,IAAI,GAAG,kBAAkB,CAAC;IAEhC,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAE1C,IAAI,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,EAAE,WAAW,CAAC,CAAC;QAEjE,OAAO,EAAE,CAAC;QAEV,MAAM,CAAC,YAAY,CAAC,CAAC,oBAAoB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
@@ -1,42 +0,0 @@
1
- import { Status } from '../common';
2
- import { NotificationsService } from '../notifications-service';
3
- describe(NotificationsService.name, () => {
4
- const store = { add: jest.fn(), initialize: jest.fn() };
5
- let options;
6
- beforeEach(() => {
7
- jest.clearAllMocks();
8
- options = { title: 'Foo', message: 'Bar!' };
9
- });
10
- const subject = new NotificationsService(store);
11
- describe('initialize', () => {
12
- test('initializes store', () => {
13
- const userId = 123;
14
- subject.initialize(userId);
15
- expect(store.initialize).toHaveBeenCalledWith(userId);
16
- });
17
- });
18
- const testCases = [
19
- { method: 'info', status: Status.Info },
20
- { method: 'success', status: Status.Success },
21
- { method: 'warning', status: Status.Warning },
22
- { method: 'error', status: Status.Error },
23
- ];
24
- describe.each(testCases)('$method', ({ method, status }) => {
25
- test(`adds "${status}" notification`, () => {
26
- subject[method](options);
27
- expect(store.add).toHaveBeenCalledWith({ ...options, status }, undefined);
28
- });
29
- test(`passes through preventDuplicates`, () => {
30
- const preventDuplicates = true;
31
- subject[method](options, preventDuplicates);
32
- expect(store.add).toHaveBeenCalledWith(expect.anything(), preventDuplicates);
33
- });
34
- });
35
- describe('show', () => {
36
- test('adds notification', () => {
37
- subject.show(options);
38
- expect(store.add).toHaveBeenCalledWith(options, undefined);
39
- });
40
- });
41
- });
42
- //# sourceMappingURL=notifications-service.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"notifications-service.test.js","sourceRoot":"","sources":["../../src/__tests__/notifications-service.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,MAAM,EAAE,MAAM,WAAW,CAAC;AAG/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAEhE,QAAQ,CAAC,oBAAoB,CAAC,IAAI,EAAE,GAAG,EAAE;IACrC,MAAM,KAAK,GAAgC,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC;IACrF,IAAI,OAAmC,CAAC;IAExC,UAAU,CAAC,GAAG,EAAE;QACZ,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,OAAO,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,IAAI,oBAAoB,CAAC,KAAY,CAAC,CAAC;IAEvD,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;QACxB,IAAI,CAAC,mBAAmB,EAAE,GAAG,EAAE;YAC3B,MAAM,MAAM,GAAG,GAAG,CAAC;YAEnB,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YAE3B,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC;QAC1D,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAOH,MAAM,SAAS,GAAe;QAC1B,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE;QACvC,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE;QAC7C,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE;QAC7C,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE;KAC5C,CAAC;IAEF,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE;QACvD,IAAI,CAAC,SAAS,MAAM,gBAAgB,EAAE,GAAG,EAAE;YACvC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC;YAEzB,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,oBAAoB,CAAC,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,EAAE,SAAS,CAAC,CAAC;QAC9E,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,kCAAkC,EAAE,GAAG,EAAE;YAC1C,MAAM,iBAAiB,GAAG,IAAI,CAAC;YAE/B,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;YAE5C,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,oBAAoB,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,iBAAiB,CAAC,CAAC;QACjF,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;QAClB,IAAI,CAAC,mBAAmB,EAAE,GAAG,EAAE;YAC3B,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAEtB,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,oBAAoB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAC/D,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
@@ -1,93 +0,0 @@
1
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
- return c > 3 && r && Object.defineProperty(target, key, r), r;
6
- };
7
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
- import { injectable, useOptionalDependencies } from '@servicetitan/react-ioc';
9
- import { mockComponent } from '@servicetitan/testing-library';
10
- import { render, screen } from '@testing-library/react';
11
- import { Fragment } from 'react';
12
- import { NotificationsApi } from '../../api/notifications.api';
13
- import { NotificationsStore } from '../../stores/notifications.store';
14
- import { NotificationsService } from '../../notifications-service';
15
- import { useCompatibleNavigate } from '../../utils/use-compatible-navigate';
16
- import { Notifications } from '../notifications';
17
- jest.mock('@servicetitan/anvil2', () => mockComponent('Toaster', { renderProps: false }));
18
- jest.mock('../../utils/use-compatible-navigate', () => ({
19
- useCompatibleNavigate: jest.fn(),
20
- }));
21
- let MockNotificationsApi = class MockNotificationsApi {
22
- constructor() {
23
- Object.defineProperty(this, "getNotification", {
24
- enumerable: true,
25
- configurable: true,
26
- writable: true,
27
- value: jest.fn()
28
- });
29
- Object.defineProperty(this, "getNotifications", {
30
- enumerable: true,
31
- configurable: true,
32
- writable: true,
33
- value: jest.fn()
34
- });
35
- Object.defineProperty(this, "changeIsReadProperty", {
36
- enumerable: true,
37
- configurable: true,
38
- writable: true,
39
- value: jest.fn()
40
- });
41
- }
42
- };
43
- MockNotificationsApi = __decorate([
44
- injectable()
45
- ], MockNotificationsApi);
46
- describe(Notifications.name, () => {
47
- let providedApi;
48
- let providedService;
49
- let providedStore;
50
- let props;
51
- const Component = () => {
52
- [providedApi, providedService, providedStore] = useOptionalDependencies(NotificationsApi, NotificationsService, NotificationsStore);
53
- return null;
54
- };
55
- beforeEach(() => (props = {}));
56
- const subject = () => render(_jsx(Notifications, { ...props, children: _jsx(Component, {}) }));
57
- test('renders Toaster', () => {
58
- subject();
59
- expect(screen).toContainComponent('Toaster');
60
- });
61
- test('provides NotificationService', () => {
62
- subject();
63
- expect(providedService).toBeInstanceOf(NotificationsService);
64
- });
65
- test('provides NotificationStore', () => {
66
- subject();
67
- expect(providedStore).toBeInstanceOf(NotificationsStore);
68
- });
69
- test('sets store.navigate to useCompatibleNavigate result', () => {
70
- const navigate = jest.fn();
71
- jest.mocked(useCompatibleNavigate).mockReturnValue(navigate);
72
- subject();
73
- expect(providedStore.navigate).toBe(navigate);
74
- });
75
- test('disposes store when component is unmounted', () => {
76
- const { unmount } = subject();
77
- const disposeSpy = jest.spyOn(providedStore, 'dispose');
78
- unmount();
79
- expect(disposeSpy).toHaveBeenCalled();
80
- });
81
- test('renders one Toaster when there are are multiple notifications', () => {
82
- render(_jsxs(Fragment, { children: [_jsx(Notifications, { children: _jsx(Notifications, {}) }), _jsx(Notifications, {})] }));
83
- expect(screen.getAllByText('<Toaster />')).toHaveLength(1);
84
- });
85
- describe('when given an apiService', () => {
86
- beforeEach(() => (props.apiService = MockNotificationsApi));
87
- test('provides NotificationsApi', () => {
88
- subject();
89
- expect(providedApi).toBeInstanceOf(MockNotificationsApi);
90
- });
91
- });
92
- });
93
- //# sourceMappingURL=notifications.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"notifications.test.js","sourceRoot":"","sources":["../../../src/components/__tests__/notifications.test.tsx"],"names":[],"mappings":"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"}
@@ -1,20 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { BrowserRouter } from 'react-router-dom';
3
- import { Text, Frame, Page } from '@servicetitan/design-system';
4
- import { BasicExample, StatusVariationsExample, DurationExample, ActionButtonExample, ProgressExample, PreventDuplicatesExample, MultilineMessageExample, ServerDefaultExample, ServerCustomExample, } from './demo';
5
- export default {
6
- title: 'Notifications/Demos',
7
- };
8
- export const Basic = wrap('Basic usage', BasicExample);
9
- export const StatusVariations = wrap('Status Variations', StatusVariationsExample);
10
- export const Duration = wrap('Duration', DurationExample);
11
- export const ActionButton = wrap('Action Button', ActionButtonExample);
12
- export const Progress = wrap('Progress', ProgressExample);
13
- export const PreventDuplicates = wrap('Prevent Duplicates', PreventDuplicatesExample);
14
- export const MultilineMessage = wrap('Multiline Message', MultilineMessageExample);
15
- export const ServerDefault = wrap('Server Default', ServerDefaultExample);
16
- export const ServerCustom = wrap('Server Custom', ServerCustomExample);
17
- function wrap(name, Component) {
18
- return () => (_jsx(BrowserRouter, { children: _jsx(Frame, { children: _jsxs(Page, { children: [_jsx(Text, { size: 4, className: "m-b-half", children: name }), _jsx(Component, {})] }) }) }));
19
- }
20
- //# sourceMappingURL=notifications.stories.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"notifications.stories.js","sourceRoot":"","sources":["../src/notifications.stories.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAC;AAEhE,OAAO,EACH,YAAY,EACZ,uBAAuB,EACvB,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,wBAAwB,EACxB,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,GACtB,MAAM,QAAQ,CAAC;AAEhB,eAAe;IACX,KAAK,EAAE,qBAAqB;CAC/B,CAAC;AAEF,MAAM,CAAC,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;AACvD,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC,mBAAmB,EAAE,uBAAuB,CAAC,CAAC;AACnF,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;AAC1D,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,EAAE,mBAAmB,CAAC,CAAC;AACvE,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;AAC1D,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,CAAC,oBAAoB,EAAE,wBAAwB,CAAC,CAAC;AACtF,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC,mBAAmB,EAAE,uBAAuB,CAAC,CAAC;AACnF,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAC,gBAAgB,EAAE,oBAAoB,CAAC,CAAC;AAC1E,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,EAAE,mBAAmB,CAAC,CAAC;AAEvE,SAAS,IAAI,CAAC,IAAY,EAAE,SAAa;IACrC,OAAO,GAAG,EAAE,CAAC,CACT,KAAC,aAAa,cACV,KAAC,KAAK,cACF,MAAC,IAAI,eACD,KAAC,IAAI,IAAC,IAAI,EAAE,CAAC,EAAE,SAAS,EAAC,UAAU,YAC9B,IAAI,GACF,EAEP,KAAC,SAAS,KAAG,IACV,GACH,GACI,CACnB,CAAC;AACN,CAAC"}
@@ -1,36 +0,0 @@
1
- export class MockNotificationsChannel {
2
- constructor() {
3
- Object.defineProperty(this, "bindings", {
4
- enumerable: true,
5
- configurable: true,
6
- writable: true,
7
- value: void 0
8
- });
9
- Object.defineProperty(this, "bindEvent", {
10
- enumerable: true,
11
- configurable: true,
12
- writable: true,
13
- value: (event, fn, _context) => {
14
- this.bindings.set(event, fn);
15
- return this;
16
- }
17
- });
18
- this.bindings = new Map();
19
- Object.assign(this, { bind: this.bindEvent });
20
- }
21
- // eslint-disable-next-line @typescript-eslint/naming-convention
22
- get global_emitter() {
23
- return this;
24
- }
25
- unbind(event, fn, _context) {
26
- if (this.bindings.get(event) === fn) {
27
- this.bindings.delete(event);
28
- }
29
- return this;
30
- }
31
- emit(event, data) {
32
- var _a;
33
- (_a = this.bindings.get(event)) === null || _a === void 0 ? void 0 : _a(data, undefined);
34
- }
35
- }
36
- //# sourceMappingURL=mock-notifications-channel.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mock-notifications-channel.js","sourceRoot":"","sources":["../../../src/stores/__mocks__/mock-notifications-channel.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,wBAAwB;IAGjC;QAFiB;;;;;WAAqC;QAuBrC;;;;mBAAY,CAAC,KAAa,EAAE,EAAiB,EAAE,QAAc,EAAE,EAAE;gBAC9E,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBAC7B,OAAO,IAAI,CAAC;YAChB,CAAC;WAAC;QAvBE,IAAI,CAAC,QAAQ,GAAG,IAAI,GAAG,EAAE,CAAC;QAC1B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,gEAAgE;IAChE,IAAI,cAAc;QACd,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,MAAM,CAAC,KAAa,EAAE,EAAiB,EAAE,QAAc;QACnD,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;YAClC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;QACD,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,IAAI,CAAC,KAAa,EAAE,IAAS;;QACzB,MAAA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,0CAAG,IAAI,EAAE,SAAS,CAAC,CAAC;IAChD,CAAC;CAMJ"}
@@ -1,367 +0,0 @@
1
- import { toast } from '@servicetitan/anvil2';
2
- import { NotificationProcessStatus, } from '../../api/notifications.api';
3
- import { Status } from '../../common';
4
- import { MockNotificationsChannel } from '../__mocks__/mock-notifications-channel';
5
- import { NotificationsStore } from '../notifications.store';
6
- jest.mock('@servicetitan/anvil2', () => ({
7
- toast: {
8
- info: jest.fn(makeId),
9
- success: jest.fn(),
10
- warning: jest.fn(),
11
- danger: jest.fn(),
12
- update: jest.fn(),
13
- dismiss: jest.fn(),
14
- },
15
- }));
16
- class MockNotificationsApi {
17
- constructor() {
18
- Object.defineProperty(this, "getNotifications", {
19
- enumerable: true,
20
- configurable: true,
21
- writable: true,
22
- value: jest.fn().mockResolvedValue({ data: [] })
23
- });
24
- Object.defineProperty(this, "getNotification", {
25
- enumerable: true,
26
- configurable: true,
27
- writable: true,
28
- value: jest.fn()
29
- });
30
- Object.defineProperty(this, "changeIsReadProperty", {
31
- enumerable: true,
32
- configurable: true,
33
- writable: true,
34
- value: jest.fn()
35
- });
36
- }
37
- }
38
- function makeId() {
39
- return Math.round(1e6 * Math.random()).toString(36);
40
- }
41
- function getServerStatusName(status) {
42
- return {
43
- [NotificationProcessStatus.Info]: 'Info',
44
- [NotificationProcessStatus.InProgress]: 'InProgress',
45
- [NotificationProcessStatus.Failure]: 'Failure',
46
- [NotificationProcessStatus.Success]: 'Success',
47
- }[status];
48
- }
49
- function getToastMethod(status) {
50
- return {
51
- [Status.Info]: 'info',
52
- [Status.Success]: 'success',
53
- [Status.Warning]: 'warning',
54
- [Status.Error]: 'danger',
55
- [NotificationProcessStatus.Info]: 'info',
56
- [NotificationProcessStatus.InProgress]: 'info',
57
- [NotificationProcessStatus.Failure]: 'danger',
58
- [NotificationProcessStatus.Success]: 'success',
59
- }[status];
60
- }
61
- describe(NotificationsStore.name, () => {
62
- let store;
63
- beforeEach(() => {
64
- store = undefined;
65
- jest.clearAllMocks();
66
- jest.useFakeTimers();
67
- });
68
- afterEach(() => {
69
- store === null || store === void 0 ? void 0 : store.dispose();
70
- jest.useRealTimers();
71
- });
72
- describe('when notification is added', () => {
73
- let options;
74
- let preventDuplicates;
75
- beforeEach(() => {
76
- options = { title: 'Foo', message: 'bar', duration: 0, progress: 0 };
77
- preventDuplicates = undefined;
78
- });
79
- const subject = () => {
80
- if (!store) {
81
- store = new NotificationsStore();
82
- store.initialize(0);
83
- }
84
- store.add(options, preventDuplicates);
85
- };
86
- function itAddsToast({ method = 'info' } = {}) {
87
- test(`adds ${method} toast`, () => {
88
- subject();
89
- const { status, ...toastOptions } = options;
90
- expect(toast[method]).toHaveBeenCalledWith(expect.objectContaining(toastOptions));
91
- });
92
- }
93
- itAddsToast({ method: 'info' });
94
- describe('when duration is not specified', () => {
95
- beforeEach(() => delete options.duration);
96
- test('sets toast duration to 8 seconds', () => {
97
- subject();
98
- expect(toast.info).toHaveBeenCalledWith(expect.objectContaining({ duration: 8000 }));
99
- });
100
- });
101
- describe('when preventDuplicates is true', () => {
102
- beforeEach(() => (preventDuplicates = true));
103
- describe('with existing notification', () => {
104
- beforeEach(() => {
105
- subject();
106
- jest.clearAllMocks();
107
- });
108
- test('ignores duplicate notification', () => {
109
- subject();
110
- expect(toast.info).not.toHaveBeenCalled();
111
- });
112
- describe('with different notification', () => {
113
- beforeEach(() => (options.title = `!${options.title}`));
114
- itAddsToast();
115
- });
116
- });
117
- });
118
- describe.each([Status.Success, Status.Warning, Status.Error, Status.Info])('when status is "%s"', status => {
119
- beforeEach(() => (options.status = status));
120
- itAddsToast({ method: getToastMethod(status) });
121
- });
122
- describe('when notification has action', () => {
123
- let navigate;
124
- beforeEach(() => {
125
- options.action = { label: 'foo', link: 'bar' };
126
- navigate = jest.fn();
127
- });
128
- test('sets duration to false', () => {
129
- subject();
130
- expect(toast.info).toHaveBeenCalledWith(expect.objectContaining({ duration: false }));
131
- });
132
- describe('when action is clicked', () => {
133
- const setup = () => {
134
- var _a, _b, _c;
135
- subject();
136
- store.navigate = navigate;
137
- (_c = (_a = jest.mocked(toast.info).mock.calls[0][0].actions) === null || _a === void 0 ? void 0 : (_b = _a.primary).onClick) === null || _c === void 0 ? void 0 : _c.call(_b, {});
138
- };
139
- test('dismisses toast', () => {
140
- setup();
141
- expect(toast.dismiss).toHaveBeenCalled();
142
- });
143
- test('navigates to link', () => {
144
- setup();
145
- expect(navigate).toHaveBeenCalledWith(options.action.link);
146
- });
147
- describe('when "navigate" is not defined', () => {
148
- beforeEach(() => (navigate = undefined));
149
- test('does nothing', () => {
150
- expect(() => setup()).not.toThrow();
151
- });
152
- });
153
- describe('when link is external', () => {
154
- beforeEach(() => (options.action.external = true));
155
- test('opens link in new window', () => {
156
- const openSpy = jest.spyOn(window, 'open').mockImplementation(jest.fn());
157
- setup();
158
- expect(openSpy).toHaveBeenCalledWith(options.action.link);
159
- });
160
- });
161
- describe('when action is callback', () => {
162
- beforeEach(() => {
163
- delete options.action.link;
164
- options.action.onClick = jest.fn();
165
- });
166
- test('invokes callback', () => {
167
- setup();
168
- expect(options.action.onClick).toHaveBeenCalled();
169
- });
170
- });
171
- });
172
- });
173
- });
174
- describe('when notification is published', () => {
175
- const userId = 42;
176
- const api = new MockNotificationsApi();
177
- const publisher = new MockNotificationsChannel();
178
- const options = { title: 'Foo' };
179
- let notification;
180
- beforeEach(() => {
181
- notification = {
182
- id: 1,
183
- userId,
184
- type: 'ServerNotification',
185
- status: NotificationProcessStatus.Info,
186
- isRead: false,
187
- createdOn: new Date(),
188
- modifiedOn: new Date(),
189
- version: 1,
190
- payload: JSON.stringify(options),
191
- };
192
- });
193
- afterEach(() => store === null || store === void 0 ? void 0 : store.dispose());
194
- const subject = () => {
195
- if (!store) {
196
- store = new NotificationsStore(api, () => publisher);
197
- store.initialize(userId);
198
- }
199
- publisher.emit('NotificationEvent', notification);
200
- };
201
- function itAddsToast(method = 'info') {
202
- test(`adds ${method} toast`, () => {
203
- subject();
204
- expect(toast[method]).toHaveBeenCalledWith(expect.objectContaining(options));
205
- });
206
- }
207
- itAddsToast();
208
- describe.each([
209
- { status: NotificationProcessStatus.Success, method: 'success' },
210
- { status: NotificationProcessStatus.Failure, method: 'danger' },
211
- { status: NotificationProcessStatus.InProgress, method: 'info' },
212
- ])('when status is "$status"', ({ method, status }) => {
213
- beforeEach(() => (notification.status = status));
214
- itAddsToast(method);
215
- });
216
- describe('when notification is for different user', () => {
217
- beforeEach(() => ++notification.userId);
218
- test('ignores notification', () => {
219
- subject();
220
- expect(toast.info).not.toHaveBeenCalled();
221
- });
222
- });
223
- describe('when notification is closed', () => {
224
- const setup = () => {
225
- var _a, _b;
226
- subject();
227
- (_b = (_a = jest.mocked(toast.info).mock.calls[0][0]).onClose) === null || _b === void 0 ? void 0 : _b.call(_a, {});
228
- };
229
- test('notifies api', () => {
230
- setup();
231
- expect(api.changeIsReadProperty).toHaveBeenCalledWith(notification.id, true, notification.version);
232
- });
233
- });
234
- describe('when notification has interceptor', () => {
235
- const newOptions = { title: `!${options.title}` };
236
- const interceptor = jest.fn();
237
- beforeEach(() => {
238
- notification.type = makeId();
239
- interceptor.mockImplementation(original => ({ ...original, payload: newOptions }));
240
- NotificationsStore.intercept(notification.type, interceptor);
241
- });
242
- test('adds toast with interceptor return value', () => {
243
- subject();
244
- expect(toast.info).toHaveBeenCalledWith(expect.objectContaining(newOptions));
245
- });
246
- describe('dismiss callback', () => {
247
- let dismissCallback;
248
- let onClose;
249
- let toastId;
250
- beforeEach(() => {
251
- subject();
252
- dismissCallback = interceptor.mock.calls[0][1];
253
- onClose = jest.mocked(toast.info).mock.calls[0][0].onClose;
254
- toastId = jest.mocked(toast.info).mock.results[0].value;
255
- jest.clearAllMocks();
256
- });
257
- test('dismisses toast', () => {
258
- dismissCallback();
259
- expect(toast.dismiss).toHaveBeenCalledWith(toastId);
260
- });
261
- describe('when toast is already closed', () => {
262
- beforeEach(() => onClose({}));
263
- test('does nothing', () => {
264
- dismissCallback();
265
- expect(toast.dismiss).not.toHaveBeenCalled();
266
- });
267
- });
268
- });
269
- describe('when interceptor returns undefined', () => {
270
- beforeEach(() => interceptor.mockReturnValue(undefined));
271
- test('ignores notification', () => {
272
- subject();
273
- expect(toast.info).not.toHaveBeenCalled();
274
- });
275
- });
276
- describe('when interceptor is removed', () => {
277
- beforeEach(() => NotificationsStore.intercept(notification.type));
278
- itAddsToast();
279
- });
280
- });
281
- [NotificationProcessStatus.Info, NotificationProcessStatus.InProgress].forEach(status => {
282
- const name = getServerStatusName(status);
283
- describe(`when "${name}" notification already exists`, () => {
284
- beforeEach(() => {
285
- notification.status = status;
286
- subject();
287
- });
288
- function itUpdatesToast(expected = options) {
289
- test('updates toast', () => {
290
- subject();
291
- expect(toast.update).toHaveBeenCalledWith(expect.any(String), expect.objectContaining(expected));
292
- });
293
- }
294
- function itIgnoresNotification() {
295
- test('ignores notification', () => {
296
- subject();
297
- expect(toast.update).not.toHaveBeenCalled();
298
- });
299
- }
300
- function itReplacesToast({ method }) {
301
- test('replaces toast', () => {
302
- subject();
303
- expect(toast.dismiss).toHaveBeenCalled();
304
- expect(toast[method]).toHaveBeenCalledWith(expect.objectContaining(options));
305
- });
306
- }
307
- itIgnoresNotification();
308
- describe('when duplicate is newer', () => {
309
- beforeEach(() => (notification.modifiedOn = new Date(Date.now() + 1)));
310
- itUpdatesToast();
311
- describe('when duplicate has different status', () => {
312
- beforeEach(() => (notification.status = NotificationProcessStatus.Success));
313
- itReplacesToast({ method: 'success' });
314
- describe('when notification has no payload', () => {
315
- beforeEach(() => delete notification.payload);
316
- test('replaces with empty toast', () => {
317
- subject();
318
- expect(toast.dismiss).toHaveBeenCalled();
319
- expect(toast.success).toHaveBeenCalledWith(expect.objectContaining({ title: undefined }));
320
- });
321
- });
322
- if (status !== NotificationProcessStatus.InProgress) {
323
- describe('when new status is "InProgress"', () => {
324
- beforeEach(() => {
325
- notification.status = NotificationProcessStatus.InProgress;
326
- });
327
- itUpdatesToast();
328
- });
329
- }
330
- });
331
- if (status === NotificationProcessStatus.InProgress) {
332
- describe('when existing notification was closed', () => {
333
- beforeEach(() => {
334
- var _a, _b;
335
- (_b = (_a = jest.mocked(toast.info).mock.calls[0][0]).onClose) === null || _b === void 0 ? void 0 : _b.call(_a, {});
336
- });
337
- itIgnoresNotification();
338
- describe('when duplicate has different status', () => {
339
- beforeEach(() => {
340
- notification.status = NotificationProcessStatus.Success;
341
- });
342
- itAddsToast();
343
- });
344
- describe('when notification was close over a day ago', () => {
345
- const DAY_INTERVAL = 24 * 60 * 60 * 1000;
346
- beforeEach(() => {
347
- jest.clearAllMocks();
348
- jest.setSystemTime(Date.now() + DAY_INTERVAL + 1);
349
- jest.advanceTimersByTime(DAY_INTERVAL + 1);
350
- });
351
- itAddsToast();
352
- });
353
- });
354
- }
355
- });
356
- });
357
- });
358
- describe('when api has pending notifications', () => {
359
- beforeEach(() => api.getNotifications.mockResolvedValue({ data: [notification] }));
360
- test('adds toast', () => {
361
- subject();
362
- expect(toast.info).toHaveBeenCalledWith(expect.objectContaining(options));
363
- });
364
- });
365
- });
366
- });
367
- //# sourceMappingURL=notifications.store.test.js.map