@synerise/ds-alert 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,18 @@
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.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
+
8
+
9
+ ### Features
10
+
11
+ * **alert:** add Notification component, stories, test todos ([eeb09d8](https://github.com/Synerise/synerise-design/commit/eeb09d857938100058e6c3112082014acdb67d6c))
12
+ * **alert:** add the remaining placements, code cleanup, storybook fixes ([c469574](https://github.com/Synerise/synerise-design/commit/c469574c2fa5135d956127a5bc35601808285569))
13
+
14
+
15
+
16
+
17
+
6
18
  ## [0.5.2](https://github.com/Synerise/synerise-design/compare/@synerise/ds-alert@0.5.1...@synerise/ds-alert@0.5.2) (2022-03-21)
7
19
 
8
20
  **Note:** Version bump only for package @synerise/ds-alert
package/README.md CHANGED
@@ -13,7 +13,7 @@ yarn add @synerise/ds-alert
13
13
  ```
14
14
 
15
15
  ## Usage
16
- ```
16
+ ```jsx
17
17
  import Alert from '@synerise/ds-alert'
18
18
 
19
19
  <Alert
@@ -28,6 +28,38 @@ import Alert from '@synerise/ds-alert'
28
28
 
29
29
  ```
30
30
 
31
+ ### notifications api usage
32
+
33
+ #### Simple (`ant`-based)
34
+
35
+ ```jsx
36
+ import notification from '@ant/notification'
37
+
38
+ notification.open({message: 'Message content', duration 4.5})
39
+ ```
40
+
41
+ #### A `synerise-design`-styled notification
42
+ ```jsx
43
+ import Notification from '@ds-alert/Notification'
44
+ import notification from '@ant/notification'
45
+
46
+ notification.open({message: <Notification>'Message content'</Notification>, duration: 4.5})
47
+ ```
48
+
49
+ #### Differently positioned notification
50
+
51
+ `antd-notification` is mounted in `docuemnt.body` by default. In order to style using scoped css -
52
+ `getContainer` has to be a `styled-components`-scoped element, this is done in `mountInstance`, see source code for more.
53
+
54
+ ```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))
59
+
60
+ console.log('@synerise/ds-alert notifications get mounted in this container', document.querySelector('.popup-container'))
61
+ ```
62
+
31
63
  ## Demo
32
64
 
33
65
  <iframe src="/storybook-static/iframe.html?id=components-alert--default"></iframe>
@@ -131,4 +163,17 @@ import Alert from '@synerise/ds-alert'
131
163
  | withClose | prop to set closeIcon | `React.ReactNode` | - |
132
164
  | button | prop to set button | `boolean` | - |
133
165
  | textButton | string of button | `string` | - |
134
- | text | string of withEmphasis or withLink | `string` | - |
166
+ | text | string of withEmphasis or withLink | `string` | - |
167
+
168
+
169
+ ### Alert.Notification
170
+ | Property | Description | Type | Default |
171
+ |---------------------|---------------------------------------------------------------------------------------------------|---------------------------------------------------------------|-----------------|
172
+ | children | Content of the notification | string\/React.ReactNode\/JSX.Element | JSX.Element |
173
+ | type | type of the notification, `"info"/"success"`, see `antd-notification`, `info` by default | keyof NotificationInstance | "info" |
174
+ | placement | where to position the notification | 'bottomLeft' 'bottomRight' 'topLeft' 'topRight' 'bottom' | "bottom" |
175
+ | onClose? | Handler for clicking on the close button (close button is rendered only if this prop is provided) | () => void | - |
176
+ | buttonText? | Text on the action button | string | - |
177
+ | onButtonClick? | Handler for `onClick` on the action button | () => void | - |
178
+ | icon? | Icon on the action button | DSIcon | - |
179
+ | closeIconClassName? | Class of the close icon name | string | "ds-close-icon" |
@@ -0,0 +1,36 @@
1
+ import * as React from 'react';
2
+ import { NotificationInstance } from 'antd/lib/notification';
3
+ import type { ArgsProps, NotificationApi } from 'antd/es/notification';
4
+ import 'antd/lib/notification/style/index.less';
5
+ export declare type NotificationProps = {
6
+ /** content of the notification */
7
+ children?: JSX.Element | React.ReactNode | React.ReactNode[];
8
+ /** text displayed on the button */
9
+ buttonText?: string;
10
+ /** type of the notification, `"info" | "success"`, see `antd/notification`, `info` by default */
11
+ type?: keyof NotificationInstance;
12
+ /** handler for clicking on the button, note button is rendered only if `buttonText` is provided */
13
+ onButtonClick?: (ev: React.MouseEvent<HTMLElement, MouseEvent>) => void;
14
+ /** close icon class */
15
+ closeIconClassName?: string;
16
+ /** where to position notification, `"{top,bottom}{Left,Right}" | "bottom"` */
17
+ placement: ArgsProps['placement'] | 'bottom';
18
+ } & Partial<Omit<ArgsProps, 'placement'>>;
19
+ declare type NotificationApiHook = ReturnType<NotificationApi['useNotification']>;
20
+ declare type ApiHook = NotificationApiHook[0];
21
+ declare type ContextHolder = NotificationApiHook[1];
22
+ export declare function Notification({ buttonText, children, onButtonClick, onClose, icon, closeIconClassName, }: NotificationProps): JSX.Element;
23
+ /**
24
+ * creates a div, mounts it in getContainer and returns reference to it.
25
+ * This is a helper function for creating a getContainer-compatible element,
26
+ * which later should be passed to getContainer option in `antd-notification`'s api
27
+ *
28
+ * @param @contextHolder notification's hook api context
29
+ * @param @getContainer where to mount styled wrapper; can be a ref=React.useRef, <div ref={ref}/>
30
+ **/
31
+ export declare function mountInstance(contextHolder?: ContextHolder, { getContainer, className }?: {
32
+ getContainer?: (() => HTMLElement) | undefined;
33
+ 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;
36
+ export default Notification;
@@ -0,0 +1,134 @@
1
+ var _excluded = ["type", "className", "message", "icon", "onClick", "onClose", "closeIconClassName", "placement"];
2
+
3
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
4
+
5
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
6
+
7
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
8
+
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
+
11
+ import * as React from 'react';
12
+ import * as ReactDOM from 'react-dom';
13
+ import { notification } from 'antd';
14
+ import "antd/lib/notification/style/index.css";
15
+ import Button from '@synerise/ds-button';
16
+ import Icon, { UserAddM, CloseM } from '@synerise/ds-icon';
17
+ import * as S from './Notification.styles';
18
+ export function Notification(_ref) {
19
+ var buttonText = _ref.buttonText,
20
+ _ref$children = _ref.children,
21
+ children = _ref$children === void 0 ? undefined : _ref$children,
22
+ onButtonClick = _ref.onButtonClick,
23
+ onClose = _ref.onClose,
24
+ icon = _ref.icon,
25
+ _ref$closeIconClassNa = _ref.closeIconClassName,
26
+ closeIconClassName = _ref$closeIconClassNa === void 0 ? 'ds-close-icon' : _ref$closeIconClassNa;
27
+ return /*#__PURE__*/React.createElement(S.NotificationsContainer, null, /*#__PURE__*/React.createElement(S.TextLabel, null, children), (buttonText || onClose) && /*#__PURE__*/React.createElement(S.Shrink, null, buttonText && /*#__PURE__*/React.createElement(Button, {
28
+ type: "primary",
29
+ mode: "icon-label",
30
+ color: "blue",
31
+ onClick: onButtonClick
32
+ }, icon || icon !== null && /*#__PURE__*/React.createElement(Icon, {
33
+ component: /*#__PURE__*/React.createElement(UserAddM, null)
34
+ }), buttonText), onClose && /*#__PURE__*/React.createElement(Button, {
35
+ className: closeIconClassName,
36
+ type: "ghost",
37
+ onClick: onClose
38
+ }, /*#__PURE__*/React.createElement(Icon, {
39
+ component: /*#__PURE__*/React.createElement(CloseM, null)
40
+ }))));
41
+ }
42
+ /**
43
+ * creates a div, mounts it in getContainer and returns reference to it.
44
+ * This is a helper function for creating a getContainer-compatible element,
45
+ * which later should be passed to getContainer option in `antd-notification`'s api
46
+ *
47
+ * @param @contextHolder notification's hook api context
48
+ * @param @getContainer where to mount styled wrapper; can be a ref=React.useRef, <div ref={ref}/>
49
+ **/
50
+
51
+ export function mountInstance(contextHolder, _temp) {
52
+ var _ref2 = _temp === void 0 ? {} : _temp,
53
+ _ref2$getContainer = _ref2.getContainer,
54
+ getContainer = _ref2$getContainer === void 0 ? function () {
55
+ return document.body;
56
+ } : _ref2$getContainer,
57
+ _ref2$className = _ref2.className,
58
+ className = _ref2$className === void 0 ? 'popup-container' : _ref2$className;
59
+
60
+ var element = document.createElement('div');
61
+ element.setAttribute('class', className);
62
+ var cont = getContainer();
63
+ cont.appendChild(element);
64
+ var jsxEl = /*#__PURE__*/React.createElement(S.NotificationsWrapper, null, contextHolder);
65
+ ReactDOM.render(jsxEl, element);
66
+
67
+ var cleanUpFunction = function cleanUpFunction() {
68
+ cont.removeChild(element);
69
+ };
70
+
71
+ return [element, cleanUpFunction];
72
+ }
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
+ }
120
+
121
+ return onClick && onClick();
122
+ };
123
+
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));
133
+ }
134
+ export default Notification;
@@ -0,0 +1,11 @@
1
+ export declare const NotificationsContainer: import("styled-components").StyledComponent<"div", any, {}, never>;
2
+ export declare const Shrink: import("styled-components").StyledComponent<"div", any, {}, never>;
3
+ export declare const TextLabel: import("styled-components").StyledComponent<"div", any, {}, never>;
4
+ export declare const NotificationsWrapper: import("styled-components").StyledComponent<"div", any, {}, never>;
5
+ declare const _default: {
6
+ NotificationsContainer: import("styled-components").StyledComponent<"div", any, {}, never>;
7
+ NotificationsWrapper: import("styled-components").StyledComponent<"div", any, {}, never>;
8
+ TextLabel: import("styled-components").StyledComponent<"div", any, {}, never>;
9
+ Shrink: import("styled-components").StyledComponent<"div", any, {}, never>;
10
+ };
11
+ export default _default;
@@ -0,0 +1,36 @@
1
+ import styled from 'styled-components';
2
+ import theme from '@synerise/ds-core/dist/js/DSProvider/ThemeProvider/theme';
3
+ export var NotificationsContainer = styled.div.withConfig({
4
+ displayName: "Notificationstyles__NotificationsContainer",
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) {
7
+ var _palette;
8
+
9
+ return (_palette = ((props == null ? void 0 : props.theme) || theme).palette) == null ? void 0 : _palette['grey-800'];
10
+ });
11
+ export var Shrink = styled.div.withConfig({
12
+ displayName: "Notificationstyles__Shrink",
13
+ componentId: "sc-1ke52b0-1"
14
+ })(["flex-grow:0;"]);
15
+ export var TextLabel = styled.div.withConfig({
16
+ displayName: "Notificationstyles__TextLabel",
17
+ componentId: "sc-1ke52b0-2"
18
+ })(["flex-grow:1;font-size:13px;"]);
19
+ export var NotificationsWrapper = styled.div.withConfig({
20
+ displayName: "Notificationstyles__NotificationsWrapper",
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) {
23
+ var _palette2;
24
+
25
+ return (_palette2 = ((props == null ? void 0 : props.theme) || theme).palette) == null ? void 0 : _palette2['grey-900'];
26
+ }, function (props) {
27
+ var _palette3;
28
+
29
+ return (_palette3 = ((props == null ? void 0 : props.theme) || theme).palette) == null ? void 0 : _palette3['grey-900'];
30
+ });
31
+ export default {
32
+ NotificationsContainer: NotificationsContainer,
33
+ NotificationsWrapper: NotificationsWrapper,
34
+ TextLabel: TextLabel,
35
+ Shrink: Shrink
36
+ };
@@ -0,0 +1,27 @@
1
+ describe('notification', function () {
2
+ it.todo('notification message should allow passing jsx component');
3
+ it.todo('notification should have close-button');
4
+ it.todo('notification holder should have no padding nor margin');
5
+ it.todo('by default there should be no icon');
6
+ it.todo('notifications should be closable with a close icon-click');
7
+ it.todo('notifications should dissapear after given time');
8
+ it.todo('pass duration and other props to the notification.open api');
9
+ it.todo('should mount in a styled-components NotificationWrapper wrapper (not next to as a e.g. sibling)');
10
+ it.todo('notification message can handle multiline and/or long messages');
11
+ it.todo('notifications appear correctly even after destroying notifications instance');
12
+ it.todo('notification container has no background (so border-radius on the content can be applied)');
13
+ it.todo('container has background-color of gray-900 and 3px border-radius, padding-left is 16px');
14
+ it.todo('shadow has gray-800');
15
+ it.todo('default text font-size is 13px');
16
+ it.todo('button label font-size is 13px');
17
+ it.todo('notification has min-height of 50px (NotificationsContainer)');
18
+ it.todo('ant-notification-hook-holder should have 0px height if empty (including margins)');
19
+ it.todo('first ant-notification-hook-handler has margin-top 0px, the other ones have 8px');
20
+ it.todo('.ant-notification > div should have transparent background');
21
+ it.todo('close button and action button can be shown separately (even exclusively)');
22
+ it.todo("Notification's message can accept special characters \"&'");
23
+ it.todo('placement bottom applies .ant-notification-bottom class to the container');
24
+ it.todo('sending notification with another placement handles it correctly');
25
+ it.todo('getContainer should always point at NotificationsWrapper element');
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
+ });
package/dist/index.d.ts CHANGED
@@ -1 +1,5 @@
1
1
  export { default } from './Alert';
2
+ export * as S from './Notification/Notification.styles';
3
+ export { default as Notification } from './Notification/Notification';
4
+ export { NotificationProps } from './Notification/Notification';
5
+ export { notificationOpen } from './Notification/Notification';
package/dist/index.js CHANGED
@@ -1 +1,6 @@
1
- export { default } from './Alert';
1
+ export { default } from './Alert';
2
+ import * as _S from './Notification/Notification.styles';
3
+ export { _S as S };
4
+ export { default as Notification } from './Notification/Notification';
5
+ export { NotificationProps } from './Notification/Notification';
6
+ export { notificationOpen } from './Notification/Notification';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synerise/ds-alert",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Alert UI Component for the Synerise Design System",
5
5
  "license": "ISC",
6
6
  "repository": "Synerise/synerise-design",
@@ -32,10 +32,10 @@
32
32
  ],
33
33
  "types": "dist/index.d.ts",
34
34
  "dependencies": {
35
- "@synerise/ds-button": "^0.17.1",
36
- "@synerise/ds-icon": "^0.48.0",
35
+ "@synerise/ds-button": "^0.17.2",
36
+ "@synerise/ds-icon": "^0.49.0",
37
37
  "@synerise/ds-typography": "^0.12.2",
38
- "@synerise/ds-unordered-list": "^0.2.9",
38
+ "@synerise/ds-unordered-list": "^0.2.10",
39
39
  "animate.css": "^4.1.1",
40
40
  "react-animate-height": "^2.0.23",
41
41
  "react-mount-animation": "0.0.9"
@@ -47,5 +47,5 @@
47
47
  "devDependencies": {
48
48
  "@synerise/ds-utils": "^0.19.0"
49
49
  },
50
- "gitHead": "233a9fede96c39eb8c5feeb5f58c635053ba875a"
50
+ "gitHead": "93d7c60a7fe73188d715fd52b9701f81fcb4e60a"
51
51
  }