@synerise/ds-alert 0.6.0 → 0.7.1

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/CHANGELOG.md CHANGED
@@ -3,6 +3,44 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [0.7.1](https://github.com/Synerise/synerise-design/compare/@synerise/ds-alert@0.7.0...@synerise/ds-alert@0.7.1) (2022-04-05)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **alert:** Notification's notice border-radius ([1017519](https://github.com/Synerise/synerise-design/commit/1017519504de355667618737075adaf5b5bcb426))
12
+
13
+
14
+
15
+
16
+
17
+ # [0.7.0](https://github.com/Synerise/synerise-design/compare/@synerise/ds-alert@0.6.1...@synerise/ds-alert@0.7.0) (2022-04-01)
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * **alert:** linter Promise, async function returns Promise<void> ([5c1e54d](https://github.com/Synerise/synerise-design/commit/5c1e54d52a0897345cd88609adcbdcdcf5fd85bf))
23
+
24
+
25
+ ### Features
26
+
27
+ * **alert:** notificationOpen to use Promise interface ([531b813](https://github.com/Synerise/synerise-design/commit/531b81318370f97d29c13d14510e602b861e20f4))
28
+
29
+
30
+
31
+
32
+
33
+ ## [0.6.1](https://github.com/Synerise/synerise-design/compare/@synerise/ds-alert@0.6.0...@synerise/ds-alert@0.6.1) (2022-03-31)
34
+
35
+
36
+ ### Bug Fixes
37
+
38
+ * **alert:** exporting NotificationProps typing ([985c994](https://github.com/Synerise/synerise-design/commit/985c994a516f0323e2903afddf9ca4bea33a594c))
39
+
40
+
41
+
42
+
43
+
6
44
  # [0.6.0](https://github.com/Synerise/synerise-design/compare/@synerise/ds-alert@0.5.2...@synerise/ds-alert@0.6.0) (2022-03-30)
7
45
 
8
46
 
package/README.md CHANGED
@@ -28,22 +28,31 @@ import Alert from '@synerise/ds-alert'
28
28
 
29
29
  ```
30
30
 
31
- ### notifications api usage
31
+ ### Notifications
32
32
 
33
- #### Simple (`ant`-based)
33
+ Notifications API offer three things:
34
+
35
+ * `<Notification/>` component for styled content,
36
+ * `notificationApi.useNotification()` for building a tunnel for the the right ContextApi (notifications use both `React.createPortal` plus they are mounted in container mounted in `document.body` for making it possible to position them correctly),
37
+ * *Pro-users*: change `getContainer` for sending notifications in other scrollable sections (`@synerise/ds-modal`, `@synerise/ds-section`).
38
+ * and `notificationOpen` for scheduling showing notificaitons.
39
+
40
+ #### The simplest notification call
34
41
 
35
42
  ```jsx
36
- import notification from '@ant/notification'
43
+ import notificationApi from '@synerise/ds-alert'
37
44
 
38
- notification.open({message: 'Message content', duration 4.5})
45
+ notification.open({message: 'Message content'});
39
46
  ```
40
47
 
41
- #### A `synerise-design`-styled notification
48
+ #### Styled notification
42
49
  ```jsx
43
- import Notification from '@ds-alert/Notification'
44
- import notification from '@ant/notification'
50
+ import { Notification, notificationApi} from '@synerise/ds-alert'
45
51
 
46
- notification.open({message: <Notification>'Message content'</Notification>, duration: 4.5})
52
+ notificationApi.open({
53
+ duration: 4.5,
54
+ message: <Notification>Message content</Notification>
55
+ });
47
56
  ```
48
57
 
49
58
  #### Differently positioned notification
@@ -52,14 +61,21 @@ notification.open({message: <Notification>'Message content'</Notification>, dura
52
61
  `getContainer` has to be a `styled-components`-scoped element, this is done in `mountInstance`, see source code for more.
53
62
 
54
63
  ```jsx
55
- import open from '@ds-alert/Notification'
56
-
57
- const [api, contextHolder] = notification.useNotification();
58
- open({message: Message content, duration: 4.5}, api, contextHolder))
64
+ import { Notification, notificationApi, notificationOpen } from '@synerise/ds-alert';
59
65
 
60
- console.log('@synerise/ds-alert notifications get mounted in this container', document.querySelector('.popup-container'))
66
+ const [api, contextHolder] = notificationApi.useNotification();
67
+ notificationOpen({
68
+ message: <Notification>You have new message.</Notification>,
69
+ placement: 'topLeft'
70
+ }, api, contextHolder);
61
71
  ```
62
72
 
73
+ ### Usage recommendations
74
+
75
+ It is recommended to call `notificationOpen` from `React.useEffect`.
76
+ Of course you can mount styled `<Notification/>` component by yourself,
77
+ but then you need to manage its rendering lifecycle.
78
+
63
79
  ## Demo
64
80
 
65
81
  <iframe src="/storybook-static/iframe.html?id=components-alert--default"></iframe>
@@ -2,6 +2,9 @@ import * as React from 'react';
2
2
  import { NotificationInstance } from 'antd/lib/notification';
3
3
  import type { ArgsProps, NotificationApi } from 'antd/es/notification';
4
4
  import 'antd/lib/notification/style/index.less';
5
+ /**
6
+ * Typings for using better autocompletion for defining an argument for `notificationOpen`'s message property.
7
+ */
5
8
  export declare type NotificationProps = {
6
9
  /** content of the notification */
7
10
  children?: JSX.Element | React.ReactNode | React.ReactNode[];
@@ -14,11 +17,15 @@ export declare type NotificationProps = {
14
17
  /** close icon class */
15
18
  closeIconClassName?: string;
16
19
  /** where to position notification, `"{top,bottom}{Left,Right}" | "bottom"` */
17
- placement: ArgsProps['placement'] | 'bottom';
20
+ placement?: ArgsProps['placement'] | 'bottom';
18
21
  } & Partial<Omit<ArgsProps, 'placement'>>;
19
22
  declare type NotificationApiHook = ReturnType<NotificationApi['useNotification']>;
20
23
  declare type ApiHook = NotificationApiHook[0];
21
24
  declare type ContextHolder = NotificationApiHook[1];
25
+ /**
26
+ * Component with the content of the notification.
27
+ * Note that in order to show notification you need to use `notificationOpen`
28
+ */
22
29
  export declare function Notification({ buttonText, children, onButtonClick, onClose, icon, closeIconClassName, }: NotificationProps): JSX.Element;
23
30
  /**
24
31
  * creates a div, mounts it in getContainer and returns reference to it.
@@ -31,6 +38,28 @@ export declare function Notification({ buttonText, children, onButtonClick, onCl
31
38
  export declare function mountInstance(contextHolder?: ContextHolder, { getContainer, className }?: {
32
39
  getContainer?: (() => HTMLElement) | undefined;
33
40
  className?: string | undefined;
34
- }): [HTMLElement | null, Function];
35
- export declare function notificationOpen({ type, className, message, icon, onClick, onClose, closeIconClassName, placement, ...props }: NotificationProps, notificationApi?: ApiHook, contextHolder?: ContextHolder): void;
41
+ }): [Promise<HTMLElement>, HTMLElement, () => void];
42
+ /**
43
+ * Function for showing new notifications.
44
+ * It requires proper context to be injected (see `notificationApi.useNotification`)
45
+ * and `message` prop in its first argument.
46
+ * Below you will find an example usage.
47
+ * Please remember that it is on you to provide contextHolder in the right place.
48
+ * ```
49
+ * import { notificationApi, Notification, notificationOpen } from '@synerise/ds-alert';
50
+ * const [api, contextHolder] = notificationApi.useNotification();
51
+ *
52
+ * function App() {
53
+ * return (<div id="app">
54
+ * {contextHolder}
55
+ * <button onClick={() => notificationOpen({
56
+ * message: <Notification>You have been notified.</Notification>
57
+ * })}>
58
+ * Show notification
59
+ * </button>);
60
+ *
61
+ * ReactDOM.render(<App/>, document.querySelector('#app'));
62
+ * ```
63
+ */
64
+ export declare function notificationOpen({ type, className, message, icon, onClick, onClose, closeIconClassName, placement, ...props }: NotificationProps, notificationApi?: ApiHook, contextHolder?: ContextHolder): Promise<void>;
36
65
  export default Notification;
@@ -8,6 +8,10 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
8
8
 
9
9
  function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
10
10
 
11
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
12
+
13
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
14
+
11
15
  import * as React from 'react';
12
16
  import * as ReactDOM from 'react-dom';
13
17
  import { notification } from 'antd';
@@ -15,6 +19,14 @@ import "antd/lib/notification/style/index.css";
15
19
  import Button from '@synerise/ds-button';
16
20
  import Icon, { UserAddM, CloseM } from '@synerise/ds-icon';
17
21
  import * as S from './Notification.styles';
22
+ /**
23
+ * Typings for using better autocompletion for defining an argument for `notificationOpen`'s message property.
24
+ */
25
+
26
+ /**
27
+ * Component with the content of the notification.
28
+ * Note that in order to show notification you need to use `notificationOpen`
29
+ */
18
30
  export function Notification(_ref) {
19
31
  var buttonText = _ref.buttonText,
20
32
  _ref$children = _ref.children,
@@ -62,73 +74,112 @@ export function mountInstance(contextHolder, _temp) {
62
74
  var cont = getContainer();
63
75
  cont.appendChild(element);
64
76
  var jsxEl = /*#__PURE__*/React.createElement(S.NotificationsWrapper, null, contextHolder);
65
- ReactDOM.render(jsxEl, element);
77
+ var renderPromsie = new Promise(function (resolve) {
78
+ ReactDOM.render(jsxEl, element, function () {
79
+ return resolve(element);
80
+ });
81
+ });
66
82
 
67
83
  var cleanUpFunction = function cleanUpFunction() {
68
84
  cont.removeChild(element);
69
85
  };
70
86
 
71
- return [element, cleanUpFunction];
87
+ return [renderPromsie, element, cleanUpFunction];
72
88
  }
73
- export function notificationOpen(_ref3, notificationApi, contextHolder) {
74
- var _ref3$type = _ref3.type,
75
- type = _ref3$type === void 0 ? 'info' : _ref3$type,
76
- _ref3$className = _ref3.className,
77
- className = _ref3$className === void 0 ? 'popup-container' : _ref3$className,
78
- message = _ref3.message,
79
- icon = _ref3.icon,
80
- onClick = _ref3.onClick,
81
- onClose = _ref3.onClose,
82
- _ref3$closeIconClassN = _ref3.closeIconClassName,
83
- closeIconClassName = _ref3$closeIconClassN === void 0 ? 'ds-close-icon' : _ref3$closeIconClassN,
84
- _ref3$placement = _ref3.placement,
85
- placement = _ref3$placement === void 0 ? 'bottom' : _ref3$placement,
86
- props = _objectWithoutPropertiesLoose(_ref3, _excluded);
87
-
88
- var api = notificationApi || notification; // TODO: check if context is actually available
89
-
90
- var el = document.body.querySelector("." + className);
91
-
92
- if (!el) {
93
- var _mountInstance = mountInstance(contextHolder, {
94
- className: className
95
- });
96
-
97
- el = _mountInstance[0];
98
- }
99
-
100
- var getContainer = function getContainer() {
101
- var _el;
102
-
103
- return (_el = el) == null ? void 0 : _el.querySelector('div>div,.NotificationsBottomPlacementWrapper>.NotificationsWrapper');
104
- };
105
- /** a workaround for handling close clicks,
106
- * since there's no way for injecting other element triggering onClose listener.
107
- * It is set as a listener for all the clicks
108
- * and fires onClose when close-icon was clicked */
109
-
110
-
111
- var maybeCloseClick = function maybeCloseClick(ev) {
112
- var _ev$currentTarget;
113
-
114
- var isClickedElementChildOfCloseIcon = ev.target.closest("." + closeIconClassName);
115
- var isThisCloseIcon = ev == null ? void 0 : (_ev$currentTarget = ev.currentTarget) == null ? void 0 : _ev$currentTarget.classList.contains("" + closeIconClassName);
116
-
117
- if (isClickedElementChildOfCloseIcon || isThisCloseIcon) {
118
- return onClose && onClose();
119
- }
89
+ /**
90
+ * Function for showing new notifications.
91
+ * It requires proper context to be injected (see `notificationApi.useNotification`)
92
+ * and `message` prop in its first argument.
93
+ * Below you will find an example usage.
94
+ * Please remember that it is on you to provide contextHolder in the right place.
95
+ * ```
96
+ * import { notificationApi, Notification, notificationOpen } from '@synerise/ds-alert';
97
+ * const [api, contextHolder] = notificationApi.useNotification();
98
+ *
99
+ * function App() {
100
+ * return (<div id="app">
101
+ * {contextHolder}
102
+ * <button onClick={() => notificationOpen({
103
+ * message: <Notification>You have been notified.</Notification>
104
+ * })}>
105
+ * Show notification
106
+ * </button>);
107
+ *
108
+ * ReactDOM.render(<App/>, document.querySelector('#app'));
109
+ * ```
110
+ */
120
111
 
121
- return onClick && onClick();
122
- };
112
+ export function notificationOpen(_x, _x2, _x3) {
113
+ return _notificationOpen.apply(this, arguments);
114
+ }
123
115
 
124
- return api.open(_objectSpread({
125
- message: message,
126
- type: type,
127
- placement: placement,
128
- getContainer: getContainer,
129
- icon: icon,
130
- onClick: maybeCloseClick,
131
- bottom: 16
132
- }, props));
116
+ function _notificationOpen() {
117
+ _notificationOpen = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(_ref3, notificationApi, contextHolder) {
118
+ var _ref3$type, type, _ref3$className, className, message, icon, onClick, onClose, _ref3$closeIconClassN, closeIconClassName, _ref3$placement, placement, props, api, el, containerPromise, _mountInstance, getContainer, maybeCloseClick;
119
+
120
+ return regeneratorRuntime.wrap(function _callee$(_context) {
121
+ while (1) {
122
+ switch (_context.prev = _context.next) {
123
+ case 0:
124
+ _ref3$type = _ref3.type, type = _ref3$type === void 0 ? 'info' : _ref3$type, _ref3$className = _ref3.className, className = _ref3$className === void 0 ? 'popup-container' : _ref3$className, message = _ref3.message, icon = _ref3.icon, onClick = _ref3.onClick, onClose = _ref3.onClose, _ref3$closeIconClassN = _ref3.closeIconClassName, closeIconClassName = _ref3$closeIconClassN === void 0 ? 'ds-close-icon' : _ref3$closeIconClassN, _ref3$placement = _ref3.placement, placement = _ref3$placement === void 0 ? 'bottom' : _ref3$placement, props = _objectWithoutPropertiesLoose(_ref3, _excluded);
125
+ api = notificationApi || notification; // TODO: check if context is actually available
126
+
127
+ el = document.body.querySelector("." + className);
128
+
129
+ if (!el) {
130
+ _mountInstance = mountInstance(contextHolder, {
131
+ className: className
132
+ });
133
+ containerPromise = _mountInstance[0];
134
+ el = _mountInstance[1];
135
+ }
136
+
137
+ _context.next = 6;
138
+ return containerPromise;
139
+
140
+ case 6:
141
+ getContainer = function getContainer() {
142
+ var _el;
143
+
144
+ return (_el = el) == null ? void 0 : _el.querySelector('div>div,.NotificationsBottomPlacementWrapper>.NotificationsWrapper');
145
+ };
146
+ /** a workaround for handling close clicks,
147
+ * since there's no way for injecting other element triggering onClose listener.
148
+ * It is set as a listener for all the clicks
149
+ * and fires onClose when close-icon was clicked */
150
+
151
+
152
+ maybeCloseClick = function maybeCloseClick(ev) {
153
+ var _ev$currentTarget;
154
+
155
+ var isClickedElementChildOfCloseIcon = ev.target.closest("." + closeIconClassName);
156
+ var isThisCloseIcon = ev == null ? void 0 : (_ev$currentTarget = ev.currentTarget) == null ? void 0 : _ev$currentTarget.classList.contains("" + closeIconClassName);
157
+
158
+ if (isClickedElementChildOfCloseIcon || isThisCloseIcon) {
159
+ return onClose && onClose();
160
+ }
161
+
162
+ return onClick && onClick();
163
+ };
164
+
165
+ return _context.abrupt("return", api.open(_objectSpread({
166
+ message: message,
167
+ type: type,
168
+ placement: placement,
169
+ getContainer: getContainer,
170
+ icon: icon,
171
+ onClick: maybeCloseClick,
172
+ bottom: 16
173
+ }, props)));
174
+
175
+ case 9:
176
+ case "end":
177
+ return _context.stop();
178
+ }
179
+ }
180
+ }, _callee);
181
+ }));
182
+ return _notificationOpen.apply(this, arguments);
133
183
  }
184
+
134
185
  export default Notification;
@@ -3,7 +3,7 @@ import theme from '@synerise/ds-core/dist/js/DSProvider/ThemeProvider/theme';
3
3
  export var NotificationsContainer = styled.div.withConfig({
4
4
  displayName: "Notificationstyles__NotificationsContainer",
5
5
  componentId: "sc-1ke52b0-0"
6
- })(["background-color:", ";color:white;min-width:588px;padding:8px;padding-left:16px;padding-right:8px;display:flex;align-items:center;width:100px;min-height:50px;"], function (props) {
6
+ })(["background-color:", ";color:white;min-width:588px;padding:8px 8px 8px 16px;display:flex;align-items:center;width:100px;min-height:50px;"], function (props) {
7
7
  var _palette;
8
8
 
9
9
  return (_palette = ((props == null ? void 0 : props.theme) || theme).palette) == null ? void 0 : _palette['grey-800'];
@@ -19,7 +19,7 @@ export var TextLabel = styled.div.withConfig({
19
19
  export var NotificationsWrapper = styled.div.withConfig({
20
20
  displayName: "Notificationstyles__NotificationsWrapper",
21
21
  componentId: "sc-1ke52b0-3"
22
- })(["& .ant-notification-bottom .ant-notification-hook-holder{margin:0px;margin-top:0px;&:not(:first-child){margin-top:8px;}}& .ant-notification.ant-notification-bottom{right:0px;left:0px;margin:0 auto;width:588px;bottom:8px;}& .ant-notification-hook-holder{background-color:transparent;box-shadow:none;width:588px;}& .ant-notification-notice{padding:0px;background-color:transparent;margin:unset;}& .ant-notification-notice{background-color:transparent;width:588px;border-radius:3px;box-shadow:0 16px 32px 0 ", "1a,0 8px 16px 0 ", "1a;}.ant-notification-notice-icon{display:none;}.ant-notification-notice-with-icon{background-color:transparent;}.ant-notification-notice-close{display:none;}& .ant-notification-notice-message,& .ant-notification-notice-closable .ant-notification-notice-message{padding-right:0;}& .ant-notification-notice-message,& .ant-notification-notice-with-icon .ant-notification-notice-message{margin-left:0;margin-bottom:0;}}"], function (props) {
22
+ })(["& .ant-notification-bottom .ant-notification-hook-holder{margin:0;&:not(:first-child){margin-top:8px;}}& .ant-notification.ant-notification-bottom{right:0;left:0;margin:0 auto;width:588px;bottom:8px;}& .ant-notification-hook-holder{background-color:transparent;box-shadow:none;width:588px;}& .ant-notification-notice{padding:0;background-color:transparent;margin:unset;}& .ant-notification-notice{background-color:transparent;width:588px;border-radius:6px;box-shadow:0 16px 32px 0 ", "1a,0 8px 16px 0 ", "1a;}.ant-notification-notice-icon{display:none;}.ant-notification-notice-with-icon{background-color:transparent;}.ant-notification-notice-close{display:none;}& .ant-notification-notice-message,& .ant-notification-notice-closable .ant-notification-notice-message{padding-right:0;}& .ant-notification-notice-message,& .ant-notification-notice-with-icon .ant-notification-notice-message{margin-left:0;margin-bottom:0;}}"], function (props) {
23
23
  var _palette2;
24
24
 
25
25
  return (_palette2 = ((props == null ? void 0 : props.theme) || theme).palette) == null ? void 0 : _palette2['grey-900'];
@@ -24,4 +24,6 @@ describe('notification', function () {
24
24
  it.todo('sending notification with another placement handles it correctly');
25
25
  it.todo('getContainer should always point at NotificationsWrapper element');
26
26
  it.todo('.ant-notification-bottom should be properly styled even if e.g. .ant-notification-topLeft was added first (`.ant-notification{, }.ant-notification-bottom`)');
27
+ it.todo('notificationOpen works with useEffect');
28
+ it.todo('if running from useEffect handle it so works, do not force used to rely on useLayoutEffect');
27
29
  });
package/dist/index.d.ts CHANGED
@@ -1,5 +1,10 @@
1
1
  export { default } from './Alert';
2
2
  export * as S from './Notification/Notification.styles';
3
+ /**
4
+ * notificationApi is required for properly handling injecting ContextApi for styling and locales.
5
+ * It's a proxy to `antd`'s `notification` module.
6
+ */
7
+ export declare const notificationsApi: import("antd/lib/notification").NotificationApi;
8
+ export type { NotificationProps } from './Notification/Notification';
3
9
  export { default as Notification } from './Notification/Notification';
4
- export { NotificationProps } from './Notification/Notification';
5
10
  export { notificationOpen } from './Notification/Notification';
package/dist/index.js CHANGED
@@ -1,6 +1,12 @@
1
+ import { notification } from 'antd';
1
2
  export { default } from './Alert';
2
3
  import * as _S from './Notification/Notification.styles';
3
4
  export { _S as S };
5
+ /**
6
+ * notificationApi is required for properly handling injecting ContextApi for styling and locales.
7
+ * It's a proxy to `antd`'s `notification` module.
8
+ */
9
+
10
+ export var notificationsApi = notification;
4
11
  export { default as Notification } from './Notification/Notification';
5
- export { NotificationProps } from './Notification/Notification';
6
12
  export { notificationOpen } from './Notification/Notification';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synerise/ds-alert",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "description": "Alert UI Component for the Synerise Design System",
5
5
  "license": "ISC",
6
6
  "repository": "Synerise/synerise-design",
@@ -47,5 +47,5 @@
47
47
  "devDependencies": {
48
48
  "@synerise/ds-utils": "^0.19.0"
49
49
  },
50
- "gitHead": "93d7c60a7fe73188d715fd52b9701f81fcb4e60a"
50
+ "gitHead": "5793ce09feb88b36cc8ca1923ddfcb272b889cb0"
51
51
  }