+ {props.image ? (
+ props.image
+ ) : (
+
-
No notifications
+
{i18n('no-notifications')}
{props.content ? (
{props.content}
) : null}
diff --git a/src/components/Notifications/NotificationsErrorState.tsx b/src/components/Notifications/NotificationsErrorState.tsx
new file mode 100644
index 00000000..7fdd7e7a
--- /dev/null
+++ b/src/components/Notifications/NotificationsErrorState.tsx
@@ -0,0 +1,37 @@
+import React from 'react';
+
+import {Icon, useTheme} from '@gravity-ui/uikit';
+
+import {block} from '../utils/cn';
+
+import i18n from './i18n/index';
+
+import './Notifications.scss';
+
+const b = block('notifications');
+
+const errorSvg = `
`;
+
+const errorDarkSvg = `
`;
+
+type Props = {image?: React.ReactNode; content: React.ReactNode};
+
+export const NotificationsErrorState = React.memo(function NotificationsEmptyState(props: Props) {
+ const theme = useTheme();
+
+ return (
+
+ {props.image ? (
+ props.image
+ ) : (
+
+ )}
+
+
{i18n('notifications-error')}
+ {props.content ? (
+
{props.content}
+ ) : null}
+
+
+ );
+});
diff --git a/src/components/Notifications/NotificationsList.tsx b/src/components/Notifications/NotificationsList.tsx
index 579dba20..1b8ce7c0 100644
--- a/src/components/Notifications/NotificationsList.tsx
+++ b/src/components/Notifications/NotificationsList.tsx
@@ -1,12 +1,10 @@
import React from 'react';
-import {useMobile} from '@gravity-ui/uikit';
-
-import {Notification} from '../Notification';
-import {NotificationWithSwipe} from '../Notification/NotificationWithSwipe';
import {NotificationProps} from '../Notification/definitions';
import {block} from '../utils/cn';
+import {NotificationWrapper} from './NotificationWrapper';
+
import './Notifications.scss';
const b = block('notifications');
@@ -16,30 +14,15 @@ type Props = {
swipeThreshold?: number;
};
-export const NotificationsList = React.memo(function NotificationsList({
- notifications,
- swipeThreshold,
-}: Props) {
- const [mobile] = useMobile();
-
+export const NotificationsList = React.memo(function NotificationsList(props: Props) {
return (
- {notifications.map((notification) => (
-
(
+
- {mobile && notification.swipeActions ? (
-
- ) : (
-
- )}
-
+ />
))}
);
diff --git a/src/components/Notifications/NotificationsLoadingState.tsx b/src/components/Notifications/NotificationsLoadingState.tsx
new file mode 100644
index 00000000..2bd35768
--- /dev/null
+++ b/src/components/Notifications/NotificationsLoadingState.tsx
@@ -0,0 +1,17 @@
+import React from 'react';
+
+import {Loader} from '@gravity-ui/uikit';
+
+import {block} from '../utils/cn';
+
+import './Notifications.scss';
+
+const b = block('notifications');
+
+export const NotificationsLoadingState = React.memo(function NotificationsEmptyState() {
+ return (
+
+
+
+ );
+});
diff --git a/src/components/Notifications/NotificationsPopupWrapper.tsx b/src/components/Notifications/NotificationsPopupWrapper.tsx
new file mode 100644
index 00000000..33d3f3ba
--- /dev/null
+++ b/src/components/Notifications/NotificationsPopupWrapper.tsx
@@ -0,0 +1,25 @@
+import React from 'react';
+
+const WIDTH = '320px';
+const HEIGHT = '470px';
+
+type Props = React.PropsWithChildren<{
+ className?: string;
+ style?: React.CSSProperties;
+ fullHeight?: boolean;
+}>;
+
+export const NotificationsPopupWrapper = (props: Props) => {
+ const {className, style, fullHeight = true, children} = props;
+
+ const finalStyles = React.useMemo((): React.CSSProperties => {
+ const heightStyles = fullHeight ? {height: HEIGHT} : {maxHeight: HEIGHT};
+ return {...heightStyles, width: WIDTH, overflowY: 'auto', ...style};
+ }, [fullHeight, style]);
+
+ return (
+
+ {children}
+
+ );
+};
diff --git a/src/components/Notifications/README.md b/src/components/Notifications/README.md
index 529e34b1..6faa8a5f 100644
--- a/src/components/Notifications/README.md
+++ b/src/components/Notifications/README.md
@@ -1,46 +1,61 @@
## Notifications
-Components for displaying notifications.
+Components for displaying notifications ([storybook](https://preview.gravity-ui.com/components/?path=/story/components-notifications--default)).
Can be used on desktop and touch devices.
### Simple usage example
```typescript
const YourComponent: React.FC = () => {
- const notifications = useMemo(
+ const notifications: NotificationProps = React.useMemo(
() => [
{
id: 'minimum',
content:
Bare minimum,
+ formattedDate: '13 seconds ago',
},
],
[],
);
- const action = useMemo(() => ({icon: Plus, text: 'Add', onClick: () => console.log('ADD')}), []);
+ const action = React.useMemo(
+ () => ({icon: Plus, text: 'Add', onClick: () => console.log('ADD')}),
+ [],
+ );
return (
-
}
- emptyMessage={'Unfortunately, there are no notifications for you, pal'}
- />
+ // If you use Notifications inside a popup, use NotificationsPopupWrapper
+
+ }
+ emptyMessage={'Unfortunately, there are no notifications for you, pal'}
+ />
+
);
};
```
+For more code examples go to [Notifications.stories.tsx](https://github.com/gravity-ui/components/blob/main/src/components/Notifications/__stories__/Notifications.stories.tsx).
+
### Components
**Notifications** — renders notifications and actions on these notifications.
-| Property | Type | Required | Default | Description |
-| :--------------- | :-------------------- | :------: | :---------------- | :--------------------------------------------------------- |
-| `notifications` | `NotificationProps[]` | `true` | `false` | Touch device (mobile) mode |
-| `title` | `ReactNode` | | `"Notifications"` | Notifications' title |
-| `actions` | `ReactNode` | | | Notifications' actions (e.g. create new, mark all as read) |
-| `emptyMessage` | `ReactNode` | | | Message for «No notifications» case |
-| `swipeThreshold` | `number` | | 0.4 | A value from 0 to 1 — the more the harder to swipe |
+| Property | Type | Required | Default | Description |
+| :-------------------------- | :-------------------- | :------: | :---------------- | :---------------------------------------------------------------------------------------- |
+| `notifications` | `NotificationProps[]` | `true` | `false` | Touch device (mobile) mode |
+| `title` | `ReactNode` | | `"Notifications"` | Notifications' title |
+| `actions` | `ReactNode` | | | Notifications' actions (e.g. create new, mark all as read) |
+| `areAllNotificationsLoaded` | `boolean` | | `false` | When `true` renders a Loader instead of the notifications |
+| `onLoadMoreNotifications` | `() => Promise` | | `noop` | Callback is called when the user scrolls to the end (so you can fetch more notifications) |
+| `isLoading` | `boolean` | | `false` | When `true` renders a Loader instead of the notifications |
+| `errorContent` | `ReactNode` | | | Used for the Error state (the message under the «Error») |
+| `errorImage` | `ReactNode` | | | Custom image for the Error state |
+| `emptyContent` | `ReactNode` | | | Same as `errorContent`, but for the Empty state |
+| `emptyImage` | `ReactNode` | | | Custom image for the Empty state |
+| `swipeThreshold` | `number` | | 0.4 | A value from 0 to 1 — the more the harder it is to swipe |
**Notification** — renders a notification with actions (side/bottom/swipe).
@@ -50,21 +65,22 @@ const YourComponent: React.FC = () => {
**NotificationProps** — notification's type:
-| Property | Type | Required | Default | Description |
-| :------------ | :---------------------------------- | :------: | :------ | :---------------------------------------------------- |
-| id | `string` | `true` | | Unique identifier (used in `key` for example) |
-| content | `ReactNode` | `true` | | Notification's content (what it's about) |
-| title | `ReactNode` | | | Notification's title (bold) |
-| formattedDate | `ReactNode` | | | Notification's creation date (already formatted) |
-| unread | `boolean` | | `false` | Is notification unread |
-| source | `NotificationSourceProps` | | | Notification's source (e.g. Cloud/Tracker/Console) |
-| theme | `NotificationTheme` | | | Notification's theme (e.g. warning/danger) |
-| className | `string` | | | Notification's `className` |
-| sideActions | `ReactNode` | | | Notification's actions on the right side |
-| bottomActions | `ReactNode` | | | Notification's bottom actions (as buttons by default) |
-| swipeActions | `NotificationSwipeActionsProps` | | | Notification's action on left/right swipe |
-| onMouseEnter | `MouseEventHandler
` | | | Callback for `onMouseEnter` |
-| onMouseLeave | `MouseEventHandler` | | | Callback for `onMouseLeave` |
-| onClick | `MouseEventHandler` | | | Callback for `onClick` |
+| Property | Type | Required | Default | Description |
+| :------------ | :------------------------------ | :------: | :------ | :--------------------------------------------------------------- |
+| id | `string` | `true` | | Unique identifier (used in `key` for example) |
+| content | `ReactNode` | `true` | | Notification's content (what it's about) |
+| title | `ReactNode` | | | Notification's title (bold) |
+| formattedDate | `ReactNode` | | | Notification's creation date (already formatted) |
+| unread | `boolean` | | `false` | Is notification unread |
+| archived | `boolean` | | `false` | Is notification archived (invisible to the user) |
+| source | `NotificationSourceProps` | | | Notification's source (e.g. Cloud/Tracker/Console) |
+| theme | `NotificationTheme` | | | Notification's theme (e.g. warning/danger) |
+| className | `string` | | | Notification's `className` |
+| sideActions | `ReactNode` | | | Notification's actions on the right side |
+| bottomActions | `ReactNode` | | | Notification's bottom actions (as buttons by default) |
+| swipeActions | `NotificationSwipeActionsProps` | | | Notification's action on left/right swipe (mobile mode required) |
+| onMouseEnter | `MouseEventHandler` | | | Callback for `onMouseEnter` |
+| onMouseLeave | `MouseEventHandler` | | | Callback for `onMouseLeave` |
+| onClick | `MouseEventHandler` | | | Callback for `onClick` |
For a more detailed info on types go to [Notifications' types](https://github.com/gravity-ui/components/blob/main/src/components/Notifications/definitions.ts) and [Notification' types](https://github.com/gravity-ui/components/blob/main/src/components/Notification/definitions.ts).
diff --git a/src/components/Notifications/__stories__/Notifications.stories.tsx b/src/components/Notifications/__stories__/Notifications.stories.tsx
index 73158bc2..15e87e0b 100644
--- a/src/components/Notifications/__stories__/Notifications.stories.tsx
+++ b/src/components/Notifications/__stories__/Notifications.stories.tsx
@@ -1,39 +1,184 @@
+/* eslint-disable no-console */
import React from 'react';
-import {ComponentMeta, ComponentStory} from '@storybook/react';
+import {Bell} from '@gravity-ui/icons';
+import {Button, Icon, Popup} from '@gravity-ui/uikit';
+import {Meta, StoryFn} from '@storybook/react';
+import {delay} from '../../InfiniteScroll/__stories__/utils';
+import {NotificationProps} from '../../Notification/definitions';
import {Notifications} from '../Notifications';
+import {NotificationsPopupWrapper} from '../NotificationsPopupWrapper';
-import {mockNotifications, notificationsMockActions} from './mockData';
+import {
+ generateNotification,
+ mockNotifications,
+ notificationSideActions,
+ notificationsMockActions,
+} from './mockData';
export default {
title: 'Components/Notifications',
component: Notifications,
-} as ComponentMeta;
-
-const Template: ComponentStory = (args) => (
-
-
-
-);
-
-export const Default = Template.bind({});
-Default.args = {
- notifications: mockNotifications,
- actions: notificationsMockActions,
-};
-
-export const Empty = Template.bind({});
-Empty.args = {
- notifications: [],
- emptyMessage: 'You have not received any notifications',
+} as Meta;
+
+const wrapperStyles = {
+ borderRadius: '8px',
+ border: '1px solid var(--g-color-line-generic)',
+ background: 'var(--g-color-base-background)',
+ margin: '4px',
+};
+
+const Wrapper = (props: React.PropsWithChildren) => {
+ return (
+
+ {props.children}
+
+ );
+};
+
+type BooleanMap = Record;
+
+export const Default: StoryFn = () => {
+ const {notifications, actions} = useNotificationsWithActions();
+
+ return (
+
+
+
+ );
};
+
+export const LoadByScrolling: StoryFn = () => {
+ const [notifications, setNotifications] = React.useState([]);
+ const areAllNotificationsLoaded = notifications.length >= 40;
+
+ const onLoadMoreNotifications = async () => {
+ await delay(1500);
+
+ const newNotifications = Array.from({length: 10}).map((_, i) =>
+ generateNotification(1 + notifications.length + i),
+ );
+
+ setNotifications((value) => [...value, ...newNotifications]);
+ };
+
+ return (
+
+
+
+ );
+};
+
+export const InsideAPopup: StoryFn = () => {
+ const {notifications, actions} = useNotificationsWithActions();
+ const [isOpen, setIsOpen] = React.useState(false);
+ const ref = React.useRef(null);
+
+ return (
+ <>
+
+
+
+
+
+
+ >
+ );
+};
+
+export const Loading: StoryFn = () => {
+ return (
+
+
+
+ );
+};
+
+export const Error: StoryFn = () => {
+ return (
+
+
+ Some error occurred
+
+
+
+ >
+ }
+ />
+
+ );
+};
+
+export const Empty: StoryFn = () => {
+ return (
+
+
+
+ );
+};
+
+function useNotificationsWithActions() {
+ const [unreadNotifications, setUnreadNotifications] = React.useState({
+ tracker: true,
+ samurai: true,
+ });
+
+ const [archivedNotifications, setArchivedNotifications] = React.useState({});
+
+ const getSideActions = React.useCallback(
+ (
+ id: NotificationProps['id'],
+ unread: boolean | undefined,
+ archived: boolean | undefined,
+ ) => (
+ <>
+ {notificationSideActions.read(Boolean(unread), () =>
+ setUnreadNotifications((current) => ({...current, [id]: !unread})),
+ )}
+ {notificationSideActions.archive(() =>
+ setArchivedNotifications((current) => ({...current, [id]: !archived})),
+ )}
+ >
+ ),
+ [],
+ );
+
+ const notifications = React.useMemo(
+ () =>
+ mockNotifications.map((notification: NotificationProps) => {
+ const id = notification.id;
+ const unread = unreadNotifications[id];
+ const archived = archivedNotifications[id];
+
+ return {
+ ...notification,
+ unread,
+ archived,
+ sideActions: getSideActions(id, unread, archived),
+ };
+ }),
+ [unreadNotifications, archivedNotifications, getSideActions],
+ );
+
+ const actions = (
+ <>
+ {notificationsMockActions.unarchive(() => setArchivedNotifications({}))}
+ {notificationsMockActions.filter()}
+ >
+ );
+
+ return {notifications, actions};
+}
diff --git a/src/components/Notifications/__stories__/mockData.tsx b/src/components/Notifications/__stories__/mockData.tsx
index 657f8b6f..f7d5e872 100644
--- a/src/components/Notifications/__stories__/mockData.tsx
+++ b/src/components/Notifications/__stories__/mockData.tsx
@@ -1,8 +1,8 @@
/* eslint-disable no-console */
import React from 'react';
-import {Archive, Funnel, PencilToSquare, Plus, TrashBin} from '@gravity-ui/icons';
-import {DropdownMenu} from '@gravity-ui/uikit';
+import {Archive, ArrowRotateLeft, CircleCheck, Funnel, TrashBin} from '@gravity-ui/icons';
+import {DropdownMenu, Link} from '@gravity-ui/uikit';
import {NotificationAction} from '../../Notification/NotificationAction';
import {NotificationSwipeAction} from '../../Notification/NotificationSwipeAction';
@@ -13,11 +13,16 @@ import {
svgReactStoryIcon,
svgTrackerStoryIcon,
svgYandexStoryIcon,
+ trackerUserIcon,
} from './storyIcons';
-export const notificationsMockActions: JSX.Element = (
- <>
- console.log('ADD')}} />
+const LINK = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ';
+
+export const notificationsMockActions = {
+ unarchive: (onClick: () => void) => (
+
+ ),
+ filter: () => (
console.log('cloud')},
]}
/>
- >
-);
+ ),
+};
export const notificationsMockSwipeActions: NotificationSwipeActionsProps = {
left: {
@@ -59,16 +64,20 @@ export const notificationsMockSwipeActions: NotificationSwipeActionsProps = {
},
};
-export const notificationSideActions: JSX.Element = (
- <>
- console.log('FILTER')}}
- />
+export const notificationSideActions = {
+ read: (unread: boolean, onClick: () => void) => (
console.log('DELETE')}}
+ action={{
+ icon: unread ? CircleCheck : ArrowRotateLeft,
+ text: `Mark as ${unread ? 'read' : 'unread'}`,
+ onClick,
+ }}
/>
- >
-);
+ ),
+ archive: (onClick: () => void) => (
+
+ ),
+};
export const notificationBottomActions: JSX.Element = (
<>
@@ -85,27 +94,44 @@ export const mockNotifications: NotificationProps[] = [
{
id: 'tracker',
title: 'An unread notification',
- content: 'No one has read this notification yet...',
+ content: (
+
+ Shrek desperately wants your attention in this{' '}
+
+ ticket
+
+
+
+ ),
formattedDate: 'just now',
source: {
title: 'Tracker',
icon: svgTrackerStoryIcon,
- href: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
+ href: LINK,
},
- unread: true,
- sideActions: notificationSideActions,
swipeActions: notificationsMockSwipeActions,
},
{
id: 'samurai',
content: A samurai has no goal, only a path,
formattedDate: '12 seconds ago',
- unread: true,
swipeActions: notificationsMockSwipeActions,
},
{
id: 'minimum',
content: Bare minimum,
+ formattedDate: '13 seconds ago',
},
{
id: 'ninja',
@@ -124,11 +150,9 @@ export const mockNotifications: NotificationProps[] = [
source: {
title: 'Yandex',
icon: svgYandexStoryIcon,
- href: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
+ href: LINK,
},
- unread: false,
theme: 'info',
- sideActions: notificationSideActions,
swipeActions: notificationsMockSwipeActions,
},
{
@@ -140,7 +164,7 @@ export const mockNotifications: NotificationProps[] = [
source: {
title: 'Billing',
icon: svgCloudStoryIcon,
- href: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
+ href: LINK,
},
theme: 'success',
bottomActions: notificationBottomActions,
@@ -153,10 +177,19 @@ export const mockNotifications: NotificationProps[] = [
source: {
title: 'React',
icon: svgReactStoryIcon,
- href: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
+ href: LINK,
},
formattedDate: 'ethernity ago',
theme: 'danger',
swipeActions: {...notificationsMockSwipeActions, right: undefined},
},
];
+
+export function generateNotification(index: number): NotificationProps {
+ return {
+ id: `notification-${index}`,
+ title: `We are number ${index}`,
+ content: `You're viewing notification #${index}`,
+ formattedDate: `${Math.round(index / 2) + 2} minutes ago`,
+ };
+}
diff --git a/src/components/Notifications/__stories__/storyIcons.ts b/src/components/Notifications/__stories__/storyIcons.ts
index fc424e5a..7e5a5a04 100644
--- a/src/components/Notifications/__stories__/storyIcons.ts
+++ b/src/components/Notifications/__stories__/storyIcons.ts
@@ -2,3 +2,4 @@ export const svgCloudStoryIcon = ``;
export const svgTrackerStoryIcon = ``;
export const svgYandexStoryIcon = ``;
+export const trackerUserIcon = `data:image/png;base64,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`;
diff --git a/src/components/Notifications/definitions.ts b/src/components/Notifications/definitions.ts
index dd025413..13d0402b 100644
--- a/src/components/Notifications/definitions.ts
+++ b/src/components/Notifications/definitions.ts
@@ -7,7 +7,16 @@ export type NotificationsProps = {
actions?: React.ReactNode;
notifications: NotificationProps[];
+ areAllNotificationsLoaded?: boolean;
+ onLoadMoreNotifications?: () => Promise;
+
+ isLoading?: boolean;
+
+ errorContent?: React.ReactNode;
+ errorImage?: React.ReactNode;
+
+ emptyContent?: React.ReactNode;
+ emptyImage?: React.ReactNode;
- emptyMessage?: React.ReactNode;
swipeThreshold?: number;
};
diff --git a/src/components/Notifications/i18n/en.json b/src/components/Notifications/i18n/en.json
index 64586638..6a3ae547 100644
--- a/src/components/Notifications/i18n/en.json
+++ b/src/components/Notifications/i18n/en.json
@@ -1,3 +1,5 @@
{
- "title": "Notifications"
+ "title": "Notifications",
+ "notifications-error": "Error",
+ "no-notifications": "No notifications"
}
diff --git a/src/components/Notifications/i18n/ru.json b/src/components/Notifications/i18n/ru.json
index bc77c487..b789c20b 100644
--- a/src/components/Notifications/i18n/ru.json
+++ b/src/components/Notifications/i18n/ru.json
@@ -1,3 +1,5 @@
{
- "title": "Уведомления"
+ "title": "Уведомления",
+ "notifications-error": "Ошибка",
+ "no-notifications": "Нет уведомлений"
}
diff --git a/src/components/Notifications/index.ts b/src/components/Notifications/index.ts
index e2188db5..c85574ee 100644
--- a/src/components/Notifications/index.ts
+++ b/src/components/Notifications/index.ts
@@ -1,2 +1,3 @@
-export * from './definitions';
export * from './Notifications';
+export * from './NotificationsPopupWrapper';
+export * from './definitions';