Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(notifications): use chakra notifications #384

Merged
merged 16 commits into from
May 10, 2023
8 changes: 7 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@
"error",
{ "peerDependencies": false }
],


// turning these off as they
"import/named": 0,
"import/namespace": 0,
"import/default": 0,
"import/no-named-as-default-member": 0,

"react-hooks/exhaustive-deps": "error",
"space-before-function-paren": 0,
"react/prop-types": 0,
Expand Down
1 change: 0 additions & 1 deletion .storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ const config: StorybookConfig = {
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'storybook-dark-mode',
'@chakra-ui/storybook-addon',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it added no value and rendered a second dark mode button which didn't work fine, because we have chakra+rebass :(

],
framework: {
name: '@storybook/react-vite',
Expand Down
39 changes: 30 additions & 9 deletions .storybook/preview.jsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,46 @@
import { ColorModeScript } from '@chakra-ui/react';
import { DocsContainer } from '@storybook/addon-docs';
import { themes } from '@storybook/theming';
import React from 'react';
import { useDarkMode } from 'storybook-dark-mode';
import { ChakraThemeProvider } from '../src/chakra';

import ThemeProvider from '../src/theme/ThemeProvider';

export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
layout: 'centered',
controls: { expanded: true },
chakra: {},
docs: {
container: (props) => {
const isDark = useDarkMode();

return (
<DocsContainer {...props} theme={isDark ? themes.dark : themes.light} />
);
},
},
darkMode: {
current: 'light',
dark: { ...themes.dark, appBg: themes.dark.appBg },
light: { ...themes.light, appBg: themes.light.appBg },
dark: { ...themes.dark },
light: { ...themes.light },
stylePreview: true,
},
};

export const decorators = [
(Story) => (
<ThemeProvider colorMode={useDarkMode() ? 'dark' : 'light'}>
<Story />
</ThemeProvider>
),
(Story) => {
const isDark = useDarkMode();

return (
<>
<ColorModeScript />

<ChakraThemeProvider>
<ThemeProvider colorMode={isDark ? 'dark' : 'light'}>
<Story />
</ThemeProvider>
</ChakraThemeProvider>
</>
);
},
];
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
"react-router-dom": "^6.3.0"
},
"devDependencies": {
"@chakra-ui/storybook-addon": "^4.0.16",
"@playwright/experimental-ct-react": "^1.31.2",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/commit-analyzer": "^9.0.2",
Expand Down Expand Up @@ -110,12 +109,14 @@
".releaserc.json"
],
"dependencies": {
"@chakra-ui/react": "^2.5.5",
"@chakra-ui/react": "^2.6.1",
"@codemirror/view": "^6.9.5",
"@emotion/core": "^10.3.1",
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
"@rebass/forms": "^4.0.6",
"@storybook/jest": "^0.1.0",
"@storybook/testing-library": "^0.1.0",
"@styled-system/css": "^5.1.5",
"@tanem/react-nprogress": "^5.0.34",
"@uiw/codemirror-extensions-langs": "^4.19.16",
Expand Down
4 changes: 3 additions & 1 deletion src/chakra.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export { useDisclosure } from '@chakra-ui/react';
export { useDisclosure, ColorModeScript } from '@chakra-ui/react';

export { ChakraThemeProvider } from './theme-chakra/ChakraThemeProvider';

export * from './components/notifier';
2 changes: 1 addition & 1 deletion src/components/notifications/notification.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const Notification: FC<SystemNotification> = ({
NotificationManager.remove(id);
}, duration);

setTimeoutNumber(timeout);
setTimeoutNumber(timeout);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

Expand Down
7 changes: 4 additions & 3 deletions src/components/notifications/notifications.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import { Meta } from '@storybook/react';
import React, { useCallback } from 'react';
import { Story, Meta } from '@storybook/react';
import { Flex } from 'rebass';
import NotificationManager from './notifications-manager';
// Components
import NotificationsContainer from './index';
import { Button, Label, Labeling } from '../../index';
import NotificationsContainer from './index';

export default {
title: 'Quartz/NotificationsContainer',
component: NotificationsContainer,
} as Meta;

const Template: Story = () => {
const Template = () => {
const notifyHandler = useCallback(() => {
NotificationManager.create({
isError: true,
type: <Label fontSize="18px">Title</Label>,
content: (
<Flex alignItems="center">
Expand Down
67 changes: 67 additions & 0 deletions src/components/notifier/Notification.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {
Alert,
AlertDescription,
AlertProps,
AlertTitle,
CloseButton,
} from '@chakra-ui/react';
import React, { ReactNode } from 'react';
import { Flex } from '../flex';

interface Props extends Omit<AlertProps, 'title'> {
onClose: any;
title: string | ReactNode;
content: string | ReactNode;
}

export const Notification = ({
onClose,
content,
title,
status,
...restProps
}: Props) => {
return (
<Alert
variant="left-accent"
status={status}
borderLeftWidth="5px"
flexDirection="column"
alignItems="start"
minW="350px"
bg="white"
_dark={{
bg: 'dark.white',
}}
gap={1}
pb={4}
pl={6}
{...restProps}
>
<Flex width="100%" justifyContent="space-between" alignItems="flex-start">
{React.isValidElement(title) ? (
title
) : (
<AlertTitle fontSize="md">{title}</AlertTitle>
)}
<CloseButton onClick={onClose} />
</Flex>

{React.isValidElement(content) ? (
content
) : (
<AlertDescription fontSize="xs">{content}</AlertDescription>
)}
</Alert>
);
};

export interface INotification {
/** Title of the alert. */
title: string | ReactNode;
/** Content under the title. */
content: string | ReactNode;
/** Duration in milliseconds. E.g. 5000 by default */
duration?: number;
isError?: boolean;
}
1 change: 1 addition & 0 deletions src/components/notifier/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { useNotifier, createNotifier } from './notifier';
87 changes: 87 additions & 0 deletions src/components/notifier/notifier.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { AlertStatus, ToastId, useToast } from '@chakra-ui/react';
import React, { ReactNode, useCallback } from 'react';
import { standaloneToast } from '../../theme-chakra/ChakraThemeProvider';
import { Notification } from './Notification';

export interface INotification {
title: string | ReactNode;
content: string | ReactNode;
duration?: number;
status?: AlertStatus;
}

export const useNotifier = () => {
const toast = useToast();

const notify = useCallback(
(status: AlertStatus) => (notification: INotification) => {
const render = ({ onClose, id }: { onClose(): void; id: ToastId }) => {
return (
<Notification
title={notification.title}
content={notification.content}
onClose={onClose}
status={status}
onMouseEnter={() => hoverHandler(id)}
onMouseLeave={() => unhoverHandler(id)}
/>
);
};

const hoverHandler = (id: ToastId) => {
toast.update(id, { duration: 1e6, render });
};

const unhoverHandler = (id: ToastId) => {
toast.update(id, {
duration: notification.duration ?? 5000,
render,
});
};

toast({
status,
duration: notification.duration ?? 5000,
isClosable: true,
render,
});
},
[toast],
);

return {
success: notify('success'),
error: notify('error'),
info: notify('info'),
warning: notify('warning'),
closeAll: toast.closeAll,
close: toast.close,
};
};

export const createNotifier = () => {
const notify = (status: AlertStatus) => (notification: INotification) =>
standaloneToast({
status,
duration: notification.duration ?? 5000,
isClosable: true,
position: 'top-right',
render: ({ onClose }) => {
return (
<Notification
title={notification.title}
content={notification.content}
onClose={onClose}
status={status}
/>
);
},
});

return {
snqb marked this conversation as resolved.
Show resolved Hide resolved
success: notify('success'),
error: notify('error'),
closeAll: standaloneToast.closeAll,
close: standaloneToast.close,
};
};
Loading