From a0fc5f8470a8d5190ffa35d41e233929c73fae4f Mon Sep 17 00:00:00 2001 From: charmi-v Date: Wed, 21 Aug 2024 18:27:31 +0530 Subject: [PATCH 1/2] fix: custom hook added for error logging --- .../basic/Notifications/Snackbar.mdx | 83 ++++++++++++ .../SnackbarManager.stories.tsx | 118 ++++++++++++++++++ .../Notifications/SnackbarManager/index.tsx | 72 +++++++++++ src/components/index.tsx | 4 + 4 files changed, 277 insertions(+) create mode 100644 src/components/basic/Notifications/Snackbar.mdx create mode 100644 src/components/basic/Notifications/SnackbarManager/SnackbarManager.stories.tsx create mode 100644 src/components/basic/Notifications/SnackbarManager/index.tsx diff --git a/src/components/basic/Notifications/Snackbar.mdx b/src/components/basic/Notifications/Snackbar.mdx new file mode 100644 index 00000000..bd71cb8b --- /dev/null +++ b/src/components/basic/Notifications/Snackbar.mdx @@ -0,0 +1,83 @@ +{/* +* Copyright (c) 2024 Contributors to the Eclipse Foundation +* +* See the NOTICE file(s) distributed with this work for additional +* information regarding copyright ownership. +* +* This program and the accompanying materials are made available under the +* terms of the Apache License, Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0. +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +* +* SPDX-License-Identifier: Apache-2.0 +*/} + +import { Canvas, Story, Meta } from '@storybook/blocks' +import * as SnackbarStories from './SnackbarManager/SnackbarManager.stories' + +import { GlobalSnackbarContainer, useSnackbar } from './SnackbarManager' +import { Button } from '../Button' + + + +## useSnackbar + + + +#### Details + +The useSnackbar hook provides a simple and efficient way to trigger snackbars in your React application. + +It leverages a singleton pattern to manage snackbar notifications globally, ensuring that snackbars can be displayed from any component without requiring explicit wrapping or context. + +This hook is especially useful in scenarios where you want to trigger a user notification (e.g., success, error, warning) in response to events like form submissions, API responses, or user interactions. + +Additionally, the hook supports customizable snackbar options such as message, severity, duration, and more as provided by the existing PageSnackbar component. + +#### Prerequisites: + +**Global Initialization:** Ensure that GlobalSnackbarContainer is properly initialized in your application root, so that the useSnackbar hook can function as expected. +typically in your index.tsx or App.tsx. + +```jsx + +import { GlobalSnackbarContainer, SharedThemeProvider } from '@catena-x/portal-shared-components' + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + + +) +``` + +#### Usage + +```jsx +import { useSnackbar } from '@catena-x/portal-shared-components'; + +const ExampleComponent: React.FC = () => { + const { showSnackbar } = useSnackbar(); + + const handleError = () => { + showSnackbar({ + message: 'This is an error message!', + severity: 'error', + }); + }; + + return ( +
+ +
+ ); +}; +export default ExampleComponent; +``` diff --git a/src/components/basic/Notifications/SnackbarManager/SnackbarManager.stories.tsx b/src/components/basic/Notifications/SnackbarManager/SnackbarManager.stories.tsx new file mode 100644 index 00000000..fdaf0b56 --- /dev/null +++ b/src/components/basic/Notifications/SnackbarManager/SnackbarManager.stories.tsx @@ -0,0 +1,118 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +import { type StoryFn } from '@storybook/react' + +import { GlobalSnackbarContainer, type SnackbarOptions, useSnackbar } from '.' +import { Button } from '../../Button' + +const Template: StoryFn = (args) => { + const { showSnackbar } = useSnackbar() + const handleAction = () => { + showSnackbar(args) + } + return ( + <> +
+ +
+ + + + ) +} + +export default { + title: 'Notifications', + component: Template, + tags: ['autodocs'], + argTypes: {}, +} +export const SnackbarHookDemo = Template.bind({}) +SnackbarHookDemo.args = { + severity: 'error', + autoClose: false, + title: 'Lorem Ipsum', + description: 'Notification sentence comes here', + showIcon: true, +} + +export const SnackbarExample = () => { + // Retrieve the showSnackbar function from the custom hook + const { showSnackbar } = useSnackbar() + + // Call the function to render the Snackbar with custom properties + const handleError = () => { + showSnackbar({ + severity: 'error', + autoClose: true, + title: 'Error message title', + showIcon: true, + }) + } + + const handleSuccess = () => { + showSnackbar({ + severity: 'success', + autoClose: true, + title: 'Success message title', + showIcon: true, + }) + } + return ( + <> +
+ +
+
+ +
+ + {/* Ensure Snackbar renders globally by adding GlobalSnackbarContainer in root */} + + + ) +} diff --git a/src/components/basic/Notifications/SnackbarManager/index.tsx b/src/components/basic/Notifications/SnackbarManager/index.tsx new file mode 100644 index 00000000..3b256dc6 --- /dev/null +++ b/src/components/basic/Notifications/SnackbarManager/index.tsx @@ -0,0 +1,72 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +import { useState, useEffect, useCallback } from 'react' +import { type PageSnackbarProps, PageSnackbar } from '../Snackbar' +import { PageSnackbarStack } from '../Snackbar/PageSnackbarStack' + +export interface SnackbarOptions extends Omit {} + +// Singleton pattern for managing the snackbar globally +const SnackbarManager = (() => { + let showSnackbar: ((options: SnackbarOptions) => void) | null = null + + const registerSnackbar = (callback: (options: SnackbarOptions) => void) => { + showSnackbar = callback + } + + const show = (options: SnackbarOptions) => { + if (showSnackbar) { + showSnackbar(options) + } + } + + return { registerSnackbar, show } +})() + +// Hook to expose the showSnackbar method to trigger snackbars +export const useSnackbar = () => { + const showSnackbar = SnackbarManager.show + return { showSnackbar } +} + +// Global container to render the snackbar component +export const GlobalSnackbarContainer = () => { + const [snackbar, setSnackbar] = useState(null) + + const openSnackbar = useCallback((options: SnackbarOptions) => { + setSnackbar(options) + }, []) + + const handleClose = useCallback(() => { + setSnackbar(null) + }, []) + + useEffect(() => { + SnackbarManager.registerSnackbar(openSnackbar) + }, [openSnackbar]) + + return ( + + {snackbar && ( + + )} + + ) +} diff --git a/src/components/index.tsx b/src/components/index.tsx index f4dab4b4..88b8f3d8 100644 --- a/src/components/index.tsx +++ b/src/components/index.tsx @@ -72,6 +72,10 @@ export { LoadMoreButton } from './basic/Button/LoadMoreButton' export { PageNotifications } from './basic/Notifications/PageNotification' export { PageSnackbar } from './basic/Notifications/Snackbar' export { PageSnackbarStack } from './basic/Notifications/Snackbar/PageSnackbarStack' +export { + GlobalSnackbarContainer, + useSnackbar, +} from './basic/Notifications/SnackbarManager' export { ErrorPage } from './basic/ErrorPage' export { MultiSelectList } from './basic/MultiSelectList' export { ProcessList } from './basic/ProcessList' From 35fd310f141e51b737f536faa655dc8b86f8381e Mon Sep 17 00:00:00 2001 From: charmi-v Date: Thu, 29 Aug 2024 12:55:24 +0530 Subject: [PATCH 2/2] fix: added errorHandler prop to components --- src/components/basic/Icon/index.tsx | 16 ++- src/components/basic/Image/index.tsx | 15 ++- .../basic/Notifications/Snackbar.mdx | 83 ------------ .../SnackbarManager.stories.tsx | 118 ------------------ .../Notifications/SnackbarManager/index.tsx | 72 ----------- src/components/index.tsx | 4 - 6 files changed, 25 insertions(+), 283 deletions(-) delete mode 100644 src/components/basic/Notifications/Snackbar.mdx delete mode 100644 src/components/basic/Notifications/SnackbarManager/SnackbarManager.stories.tsx delete mode 100644 src/components/basic/Notifications/SnackbarManager/index.tsx diff --git a/src/components/basic/Icon/index.tsx b/src/components/basic/Icon/index.tsx index 1eea6fe8..5ad40553 100644 --- a/src/components/basic/Icon/index.tsx +++ b/src/components/basic/Icon/index.tsx @@ -38,6 +38,7 @@ declare module '@mui/material/SvgIcon' { } export interface IconProps extends SvgIconProps { iconName: keyof typeof MUIIcons + onError?: () => void } // Use a switch case for scalable integration of additional icon libraries. @@ -47,11 +48,22 @@ const getIconComponent = (iconName: string) => { ] as React.ComponentType } -export const Icon: React.FC = ({ iconName, ...props }) => { +// defining default error handler +const defaultOnError = (iconName: string) => { + console.warn(`Icon ${iconName} does not exist in @mui/icons-material`) +} + +export const Icon: React.FC = ({ + iconName, + onError = () => { + defaultOnError(iconName) + }, + ...props +}) => { const IconComponent = iconName ? getIconComponent(iconName) : null if (!IconComponent) { - console.warn(`Icon ${iconName} does not exist in @mui/icons-material`) + onError() return null } diff --git a/src/components/basic/Image/index.tsx b/src/components/basic/Image/index.tsx index 4ac7f346..58a48888 100644 --- a/src/components/basic/Image/index.tsx +++ b/src/components/basic/Image/index.tsx @@ -47,9 +47,16 @@ interface ImageProps { alt?: string style?: React.CSSProperties loader?: (src: string) => Promise + onError?: (e: Error) => void } -export const Image = ({ src, alt, style, loader }: ImageProps): JSX.Element => { +export const Image = ({ + src, + alt, + style, + loader, + onError, +}: ImageProps): JSX.Element => { const [data, setData] = useState(LogoGrayData) const [error, setError] = useState(false) @@ -62,14 +69,14 @@ export const Image = ({ src, alt, style, loader }: ImageProps): JSX.Element => { IMAGE_TYPES[firstByte] ?? IMAGE_TYPES[first3Bytes] ?? 'image/*' setData(URL.createObjectURL(new Blob([buffer], { type: imageType }))) } catch (e) { + // defining default error handler + onError ? onError(e as Error) : console.error(e) setData(LogoGrayData) } }, [src, loader]) useEffect(() => { - getData().catch((e) => { - console.error(e) - }) + void getData() }, [getData]) return ( diff --git a/src/components/basic/Notifications/Snackbar.mdx b/src/components/basic/Notifications/Snackbar.mdx deleted file mode 100644 index bd71cb8b..00000000 --- a/src/components/basic/Notifications/Snackbar.mdx +++ /dev/null @@ -1,83 +0,0 @@ -{/* -* Copyright (c) 2024 Contributors to the Eclipse Foundation -* -* See the NOTICE file(s) distributed with this work for additional -* information regarding copyright ownership. -* -* This program and the accompanying materials are made available under the -* terms of the Apache License, Version 2.0 which is available at -* https://www.apache.org/licenses/LICENSE-2.0. -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -* License for the specific language governing permissions and limitations -* under the License. -* -* SPDX-License-Identifier: Apache-2.0 -*/} - -import { Canvas, Story, Meta } from '@storybook/blocks' -import * as SnackbarStories from './SnackbarManager/SnackbarManager.stories' - -import { GlobalSnackbarContainer, useSnackbar } from './SnackbarManager' -import { Button } from '../Button' - - - -## useSnackbar - - - -#### Details - -The useSnackbar hook provides a simple and efficient way to trigger snackbars in your React application. - -It leverages a singleton pattern to manage snackbar notifications globally, ensuring that snackbars can be displayed from any component without requiring explicit wrapping or context. - -This hook is especially useful in scenarios where you want to trigger a user notification (e.g., success, error, warning) in response to events like form submissions, API responses, or user interactions. - -Additionally, the hook supports customizable snackbar options such as message, severity, duration, and more as provided by the existing PageSnackbar component. - -#### Prerequisites: - -**Global Initialization:** Ensure that GlobalSnackbarContainer is properly initialized in your application root, so that the useSnackbar hook can function as expected. -typically in your index.tsx or App.tsx. - -```jsx - -import { GlobalSnackbarContainer, SharedThemeProvider } from '@catena-x/portal-shared-components' - -ReactDOM.createRoot(document.getElementById('root')!).render( - - - - -) -``` - -#### Usage - -```jsx -import { useSnackbar } from '@catena-x/portal-shared-components'; - -const ExampleComponent: React.FC = () => { - const { showSnackbar } = useSnackbar(); - - const handleError = () => { - showSnackbar({ - message: 'This is an error message!', - severity: 'error', - }); - }; - - return ( -
- -
- ); -}; -export default ExampleComponent; -``` diff --git a/src/components/basic/Notifications/SnackbarManager/SnackbarManager.stories.tsx b/src/components/basic/Notifications/SnackbarManager/SnackbarManager.stories.tsx deleted file mode 100644 index fdaf0b56..00000000 --- a/src/components/basic/Notifications/SnackbarManager/SnackbarManager.stories.tsx +++ /dev/null @@ -1,118 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -import { type StoryFn } from '@storybook/react' - -import { GlobalSnackbarContainer, type SnackbarOptions, useSnackbar } from '.' -import { Button } from '../../Button' - -const Template: StoryFn = (args) => { - const { showSnackbar } = useSnackbar() - const handleAction = () => { - showSnackbar(args) - } - return ( - <> -
- -
- - - - ) -} - -export default { - title: 'Notifications', - component: Template, - tags: ['autodocs'], - argTypes: {}, -} -export const SnackbarHookDemo = Template.bind({}) -SnackbarHookDemo.args = { - severity: 'error', - autoClose: false, - title: 'Lorem Ipsum', - description: 'Notification sentence comes here', - showIcon: true, -} - -export const SnackbarExample = () => { - // Retrieve the showSnackbar function from the custom hook - const { showSnackbar } = useSnackbar() - - // Call the function to render the Snackbar with custom properties - const handleError = () => { - showSnackbar({ - severity: 'error', - autoClose: true, - title: 'Error message title', - showIcon: true, - }) - } - - const handleSuccess = () => { - showSnackbar({ - severity: 'success', - autoClose: true, - title: 'Success message title', - showIcon: true, - }) - } - return ( - <> -
- -
-
- -
- - {/* Ensure Snackbar renders globally by adding GlobalSnackbarContainer in root */} - - - ) -} diff --git a/src/components/basic/Notifications/SnackbarManager/index.tsx b/src/components/basic/Notifications/SnackbarManager/index.tsx deleted file mode 100644 index 3b256dc6..00000000 --- a/src/components/basic/Notifications/SnackbarManager/index.tsx +++ /dev/null @@ -1,72 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -import { useState, useEffect, useCallback } from 'react' -import { type PageSnackbarProps, PageSnackbar } from '../Snackbar' -import { PageSnackbarStack } from '../Snackbar/PageSnackbarStack' - -export interface SnackbarOptions extends Omit {} - -// Singleton pattern for managing the snackbar globally -const SnackbarManager = (() => { - let showSnackbar: ((options: SnackbarOptions) => void) | null = null - - const registerSnackbar = (callback: (options: SnackbarOptions) => void) => { - showSnackbar = callback - } - - const show = (options: SnackbarOptions) => { - if (showSnackbar) { - showSnackbar(options) - } - } - - return { registerSnackbar, show } -})() - -// Hook to expose the showSnackbar method to trigger snackbars -export const useSnackbar = () => { - const showSnackbar = SnackbarManager.show - return { showSnackbar } -} - -// Global container to render the snackbar component -export const GlobalSnackbarContainer = () => { - const [snackbar, setSnackbar] = useState(null) - - const openSnackbar = useCallback((options: SnackbarOptions) => { - setSnackbar(options) - }, []) - - const handleClose = useCallback(() => { - setSnackbar(null) - }, []) - - useEffect(() => { - SnackbarManager.registerSnackbar(openSnackbar) - }, [openSnackbar]) - - return ( - - {snackbar && ( - - )} - - ) -} diff --git a/src/components/index.tsx b/src/components/index.tsx index 3857df61..31b43fbd 100644 --- a/src/components/index.tsx +++ b/src/components/index.tsx @@ -72,10 +72,6 @@ export { LoadMoreButton } from './basic/Button/LoadMoreButton' export { PageNotifications } from './basic/Notifications/PageNotification' export { PageSnackbar } from './basic/Notifications/Snackbar' export { PageSnackbarStack } from './basic/Notifications/Snackbar/PageSnackbarStack' -export { - GlobalSnackbarContainer, - useSnackbar, -} from './basic/Notifications/SnackbarManager' export { ErrorPage } from './basic/ErrorPage' export { MultiSelectList } from './basic/MultiSelectList' export { ProcessList } from './basic/ProcessList'