diff --git a/packages/core/admin/admin/src/components/Notifications/Notification/index.js b/packages/core/admin/admin/src/components/Notifications/Notification/index.js
index 3dfffc2728..75e97d3f2d 100644
--- a/packages/core/admin/admin/src/components/Notifications/Notification/index.js
+++ b/packages/core/admin/admin/src/components/Notifications/Notification/index.js
@@ -1,40 +1,12 @@
import React, { useEffect, useCallback } from 'react';
import PropTypes from 'prop-types';
-import { Padded, Text, Flex } from '@buffetjs/core';
import { useIntl } from 'react-intl';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Remove } from '@buffetjs/icons';
-import { NotificationWrapper, IconWrapper, LinkArrow, RemoveWrapper } from './styledComponents';
-
-const types = {
- success: {
- icon: 'check',
- color: 'green',
- },
- warning: {
- icon: 'exclamation',
- color: 'orange',
- },
- info: {
- icon: 'info',
- color: 'blue',
- },
-};
+import { Alert } from '@strapi/parts/Alert';
+import { Link } from '@strapi/parts/Link';
const Notification = ({ dispatch, notification }) => {
const { formatMessage } = useIntl();
-
- const {
- title,
- message,
- link,
- type,
- id,
- onClose,
- timeout,
- blockTransition,
- centered,
- } = notification;
+ const { message, link, type, id, onClose, timeout, blockTransition } = notification;
const formattedMessage = msg => (typeof msg === 'string' ? msg : formatMessage(msg, msg.values));
const handleClose = useCallback(() => {
@@ -61,62 +33,53 @@ const Notification = ({ dispatch, notification }) => {
return () => clearTimeout(timeoutToClear);
}, [blockTransition, handleClose, timeout]);
+ let variant;
+ let alertTitle;
+
+ if (type === 'info') {
+ variant = 'default';
+ alertTitle = formatMessage({
+ id: 'notification.default.title',
+ defaultMessage: 'Information Alert:',
+ });
+ } else if (type === 'warning') {
+ alertTitle = formatMessage({
+ id: 'notification.warning.title',
+ defaultMessage: 'Warning Alert:',
+ });
+ variant = 'danger';
+ } else {
+ alertTitle = formatMessage({
+ id: 'notification.success.title',
+ defaultMessage: 'Success Alert:',
+ });
+ variant = 'success';
+ }
+
return (
-
-
-
-
-
-
-
- {title && (
-
- {formattedMessage(title)}
-
- )}
-
- {message && (
- {formattedMessage(message)}
- )}
- {link && (
-
-
-
-
- {formattedMessage(link.label)}
-
- {link.target === '_blank' && (
-
-
-
- )}
-
-
-
- )}
-
-
-
-
-
-
-
-
+
+ {formatMessage({
+ id: link.label?.id || link.label,
+ defaultMessage: link.label?.defaultMessage || link.label?.id || link.label,
+ })}
+
+ ) : (
+ undefined
+ )
+ }
+ onClose={handleClose}
+ closeLabel="Close"
+ title={alertTitle}
+ variant={variant}
+ >
+ {formattedMessage({
+ id: message?.id || message,
+ defaultMessage: message?.defaultMessage || message?.id || message,
+ })}
+
);
};
@@ -131,7 +94,6 @@ Notification.defaultProps = {
onClose: () => null,
timeout: 2500,
blockTransition: false,
- centered: false,
},
};
@@ -147,14 +109,6 @@ Notification.propTypes = {
values: PropTypes.object,
}),
]),
- title: PropTypes.oneOfType([
- PropTypes.string,
- PropTypes.shape({
- id: PropTypes.string.isRequired,
- defaultMessage: PropTypes.string,
- values: PropTypes.object,
- }),
- ]),
link: PropTypes.shape({
target: PropTypes.string,
url: PropTypes.string.isRequired,
@@ -171,7 +125,6 @@ Notification.propTypes = {
onClose: PropTypes.func,
timeout: PropTypes.number,
blockTransition: PropTypes.bool,
- centered: PropTypes.bool,
}),
};
diff --git a/packages/core/admin/admin/src/components/Notifications/Notification/styledComponents.js b/packages/core/admin/admin/src/components/Notifications/Notification/styledComponents.js
deleted file mode 100644
index 70d7fada4c..0000000000
--- a/packages/core/admin/admin/src/components/Notifications/Notification/styledComponents.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import styled from 'styled-components';
-import { Arrow } from '@buffetjs/icons';
-
-const NotificationWrapper = styled.div`
- position: relative;
-
- pointer-events: auto;
-
- &:hover {
- box-shadow: 0 1px 5px 0 rgba(0, 0, 0, 0.2);
- }
-`;
-
-// border-top-right-radius: ${({ theme }) => theme.main.sizes.borderRadius};
-// border-bottom-right-radius: ${({ theme }) => theme.main.sizes.borderRadius};
-// margin-bottom: ${({ theme }) => theme.main.sizes.paddings.sm};
-// box-shadow: 0 2px 4px 0 ${({ theme }) => theme.main.colors.darkGrey};
-// background-color: ${props => props.theme.main.colors.white};
-// border-left: 2px solid ${({ theme, color }) => theme.main.colors[color]};
-// overflow: hidden;
-// z-index: 10;
-// color: ${({ color, theme }) => theme.main.colors[color]};
-// transition: all 0.15s ease;
-// width: 400px;
-// min-height: 60px;
-// margin-left: ${({ centered }) => (centered ? '0px' : '240px')};
-
-const IconWrapper = styled.div`
- border: 1px solid;
- padding: 5px;
- border-radius: 50%;
- width: 20px;
- height: 20px;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 10px;
-`;
-
-const LinkArrow = styled(Arrow)`
- transform: rotate(45deg);
- margin-top: 4px;
- color: ${({ theme }) => theme.main.colors.blue};
-`;
-
-const RemoveWrapper = styled.div`
- position: relative;
- display: flex;
- width: 20px;
- cursor: pointer;
- opacity: 0.6;
- font-size: 1.4rem;
- color: #bbc2bf;
- transition: opacity 0.1s ease;
- -webkit-font-smoothing: antialiased;
-
- &:hover {
- opacity: 1;
- }
-
- svg {
- margin: auto;
- font-size: 1.3rem;
- font-weight: 100 !important;
- }
-`;
-
-export { NotificationWrapper, IconWrapper, LinkArrow, RemoveWrapper };
diff --git a/packages/core/admin/admin/src/components/Notifications/Wrapper.js b/packages/core/admin/admin/src/components/Notifications/Wrapper.js
index 8ec390d002..63a7f4ccf2 100644
--- a/packages/core/admin/admin/src/components/Notifications/Wrapper.js
+++ b/packages/core/admin/admin/src/components/Notifications/Wrapper.js
@@ -1,19 +1,12 @@
import styled from 'styled-components';
-import { TransitionGroup } from 'react-transition-group';
+import { Row } from '@strapi/parts';
-const Wrapper = styled(TransitionGroup)`
- display: flex;
- align-items: center;
- flex-direction: column;
+const Wrapper = styled(Row)`
position: fixed;
- top: 72px;
- left: 0;
+ top: 46px;
right: 0;
+ left: 0;
z-index: 1100;
- list-style: none;
- width: 100%;
- overflow-y: hidden;
- pointer-events: none;
`;
export default Wrapper;
diff --git a/packages/core/admin/admin/src/components/Notifications/index.js b/packages/core/admin/admin/src/components/Notifications/index.js
index 1d1b0fb4d3..7a99668b51 100644
--- a/packages/core/admin/admin/src/components/Notifications/index.js
+++ b/packages/core/admin/admin/src/components/Notifications/index.js
@@ -1,15 +1,14 @@
import { NotificationsProvider } from '@strapi/helper-plugin';
import React, { useReducer } from 'react';
import PropTypes from 'prop-types';
-// import { CSSTransition } from 'react-transition-group';
-
-// import Notification from './Notification';
+import { Box } from '@strapi/parts/Box';
+import { Stack } from '@strapi/parts/Stack';
+import Notification from './Notification';
import reducer, { initialState } from './reducer';
-// import NotificationsWrapper from './Wrapper';
+import NotificationsWrapper from './Wrapper';
const Notifications = ({ children }) => {
- // const [{ notifications }, dispatch] = useReducer(reducer, initialState);
- const [, dispatch] = useReducer(reducer, initialState);
+ const [{ notifications }, dispatch] = useReducer(reducer, initialState);
const displayNotification = config => {
dispatch({
@@ -20,30 +19,20 @@ const Notifications = ({ children }) => {
return (
+
+
+ {notifications.map(notification => {
+ return (
+
+
+
+ );
+ })}
+
+
{children}
);
-
- // FIXME
- // return (
- //
- //
- // {notifications.map(notification => (
- //
- //
- //
- // ))}
- //
- // {children}
- //
- // );
};
Notifications.propTypes = {
diff --git a/packages/core/admin/admin/src/components/Notifications/reducer.js b/packages/core/admin/admin/src/components/Notifications/reducer.js
index 60d45ef757..1d67d1696f 100644
--- a/packages/core/admin/admin/src/components/Notifications/reducer.js
+++ b/packages/core/admin/admin/src/components/Notifications/reducer.js
@@ -19,13 +19,10 @@ const notificationReducer = (state = initialState, action) =>
id: 'notification.success.saved',
defaultMessage: 'Saved',
}),
- title: get(action, ['config', 'title'], null),
link: get(action, ['config', 'link'], null),
timeout: get(action, ['config', 'timeout'], 2500),
blockTransition: get(action, ['config', 'blockTransition'], false),
- uid: get(action, ['config', 'uid'], null),
onClose: get(action, ['config', 'onClose'], null),
- centered: get(action, ['config', 'centered'], false),
});
draftState.notifId = state.notifId + 1;
break;
diff --git a/packages/core/admin/admin/src/components/Notifications/tests/index.test.js b/packages/core/admin/admin/src/components/Notifications/tests/index.test.js
new file mode 100644
index 0000000000..078d10bd95
--- /dev/null
+++ b/packages/core/admin/admin/src/components/Notifications/tests/index.test.js
@@ -0,0 +1,176 @@
+/**
+ *
+ * Tests for Notifications
+ *
+ */
+
+import React from 'react';
+import { render, fireEvent, screen } from '@testing-library/react';
+import { IntlProvider } from 'react-intl';
+import { useNotification } from '@strapi/helper-plugin';
+import { act } from 'react-dom/test-utils';
+import Theme from '../../Theme';
+import Notifications from '../index';
+
+const messages = {
+ en: {},
+};
+
+describe('', () => {
+ it('renders and matches the snapshot', () => {
+ const {
+ container: { firstChild },
+ } = render(
+
+
+
+
+
+
+
+ );
+
+ expect(firstChild).toMatchInlineSnapshot(`
+ .c0 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-flex-direction: row;
+ -ms-flex-direction: row;
+ flex-direction: row;
+ -webkit-box-pack: space-around;
+ -webkit-justify-content: space-around;
+ -ms-flex-pack: space-around;
+ justify-content: space-around;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ }
+
+ .c2 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-flex-direction: column;
+ -ms-flex-direction: column;
+ flex-direction: column;
+ }
+
+ .c2 > * {
+ margin-top: 0;
+ margin-bottom: 0;
+ }
+
+ .c2 > * + * {
+ margin-top: 0px;
+ }
+
+ .c1 {
+ position: fixed;
+ top: 46px;
+ right: 0;
+ left: 0;
+ z-index: 1100;
+ }
+
+
+ `);
+ });
+
+ it('should display a notification correctly', async () => {
+ const Button = () => {
+ const toggleNotification = useNotification();
+
+ const handleClick = () => {
+ toggleNotification({ type: 'success', message: 'simple notif' });
+ };
+
+ return (
+
+ );
+ };
+
+ render(
+
+
+
+
+
+
+
+ );
+
+ // Click button
+ fireEvent.click(screen.getByText('display notif'));
+
+ const items = await screen.findAllByText(/simple notif/);
+
+ expect(items).toHaveLength(1);
+
+ await act(async () => {
+ await new Promise(resolve => setTimeout(resolve, 2500));
+ });
+
+ const foundItems = screen.queryAllByText(/simple notif/);
+
+ expect(foundItems).toHaveLength(0);
+ });
+
+ it('should display a notification correctly and not toggle it', async () => {
+ const Button = () => {
+ const toggleNotification = useNotification();
+
+ const handleClick = () => {
+ toggleNotification({ type: 'success', message: 'simple notif', blockTransition: true });
+ };
+
+ return (
+
+ );
+ };
+
+ render(
+
+
+
+
+
+
+
+ );
+
+ // Click button
+ fireEvent.click(screen.getByText('display notif'));
+
+ const items = await screen.findAllByText(/simple notif/);
+
+ expect(items).toHaveLength(1);
+
+ await act(async () => {
+ await new Promise(resolve => setTimeout(resolve, 2500));
+ });
+
+ const foundItems = screen.queryAllByText(/simple notif/);
+
+ expect(foundItems).toHaveLength(1);
+
+ fireEvent.click(screen.getByLabelText('Close'));
+
+ const displayedItems = screen.queryAllByText(/simple notif/);
+
+ expect(displayedItems).toHaveLength(0);
+ });
+});
diff --git a/packages/core/admin/admin/src/components/Notifications/tests/reducer.test.js b/packages/core/admin/admin/src/components/Notifications/tests/reducer.test.js
index 620ff48c70..bf3a348814 100644
--- a/packages/core/admin/admin/src/components/Notifications/tests/reducer.test.js
+++ b/packages/core/admin/admin/src/components/Notifications/tests/reducer.test.js
@@ -32,12 +32,9 @@ describe('ADMIN | COMPONENTS | NOTIFICATIONS | reducer', () => {
id: 0,
type: 'success',
message: { id: 'notification.message' },
- title: null,
link: null,
timeout: 2500,
blockTransition: false,
- centered: false,
- uid: null,
onClose: null,
},
],
diff --git a/packages/core/admin/admin/src/tests/StrapiApp.test.js b/packages/core/admin/admin/src/tests/StrapiApp.test.js
index 9104f99de2..afcb75e9c7 100644
--- a/packages/core/admin/admin/src/tests/StrapiApp.test.js
+++ b/packages/core/admin/admin/src/tests/StrapiApp.test.js
@@ -14,23 +14,6 @@ describe('ADMIN | StrapiApp', () => {
const { container } = render(app.render());
expect(container.firstChild).toMatchInlineSnapshot(`
- .c1 {
- border: 0;
- -webkit-clip: rect(0 0 0 0);
- clip: rect(0 0 0 0);
- height: 1px;
- margin: -1px;
- overflow: hidden;
- padding: 0;
- position: absolute;
- width: 1px;
- }
-
- .c2 {
- -webkit-animation: gzYjWD 1s infinite linear;
- animation: gzYjWD 1s infinite linear;
- }
-
.c0 {
display: -webkit-box;
display: -webkit-flex;
@@ -49,25 +32,39 @@ describe('ADMIN | StrapiApp', () => {
align-items: center;
}
+ .c2 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-flex-direction: column;
+ -ms-flex-direction: column;
+ flex-direction: column;
+ }
+
+ .c2 > * {
+ margin-top: 0;
+ margin-bottom: 0;
+ }
+
+ .c2 > * + * {
+ margin-top: 0px;
+ }
+
+ .c1 {
+ position: fixed;
+ top: 46px;
+ right: 0;
+ left: 0;
+ z-index: 1100;
+ }
+
-
- Loading content.
-
-

-
+ class="c2"
+ />
`);
});
diff --git a/packages/core/admin/admin/src/translations/en.json b/packages/core/admin/admin/src/translations/en.json
index 434d12f519..1d457c08d3 100644
--- a/packages/core/admin/admin/src/translations/en.json
+++ b/packages/core/admin/admin/src/translations/en.json
@@ -576,6 +576,9 @@
"notification.form.success.fields": "Changes saved",
"notification.link-copied": "Link copied into the clipboard",
"notification.permission.not-allowed-read": "You are not allowed to see this document",
+ "notification.default.title": "Information Alert:",
+ "notification.success.title": "Success Alert:",
+ "notification.warning.title": "Warning Alert:",
"notification.success.delete": "The item has been deleted",
"notification.success.saved": "Saved",
"notification.version.update.link": "See more",
diff --git a/packages/core/admin/ee/admin/hooks/useAuthProviders/index.js b/packages/core/admin/ee/admin/hooks/useAuthProviders/index.js
index 6fad3a5974..f31b0ad9a6 100644
--- a/packages/core/admin/ee/admin/hooks/useAuthProviders/index.js
+++ b/packages/core/admin/ee/admin/hooks/useAuthProviders/index.js
@@ -41,7 +41,6 @@ const useAuthProviders = ({ ssoEnabled }) => {
toggleNotification({
type: 'warning',
message: { id: 'notification.error' },
- centered: true,
});
}
};
diff --git a/packages/core/helper-plugin/lib/src/hooks/useNotification/useNotification.stories.mdx b/packages/core/helper-plugin/lib/src/hooks/useNotification/useNotification.stories.mdx
new file mode 100644
index 0000000000..2359b7fc37
--- /dev/null
+++ b/packages/core/helper-plugin/lib/src/hooks/useNotification/useNotification.stories.mdx
@@ -0,0 +1,48 @@
+
+
+import { Meta } from '@storybook/addon-docs';
+
+
+
+# useNotification
+
+This hook is used in order to display a notification in the admin panel.
+
+## Usage
+
+```
+import { useNotification } from '@strapi/helper-plugin';
+import { Button, Main } from '@strapi/parts';
+
+const HomePage = () => {
+ const toggleNotification = useNotification();
+
+ const handleClick = () => {
+ toggleNotification({
+ // required
+ type: 'info|success|warning',
+ // required
+ message: { id: 'notification.version.update.message', defaultMessage: 'A new version is available' },
+ // optional
+ link: {
+ url: 'https://github.com/strapi/strapi/releases/tag/v4',
+ label: {
+ id: 'notification.version.update.link',
+ defaultMessage: 'See more'
+ },
+ },
+ // optional: default = false
+ blockTransition: true,
+ // optional
+ onClose: () => localStorage.setItem('STRAPI_UPDATE_NOTIF', true),
+ });
+ }
+
+ return (
+
+ This is the homepage
+
+
+ );
+};
+```