This is an automated email from the ASF dual-hosted git repository. msyavuz pushed a commit to branch msyavuz/fix/refactor-notifications in repository https://gitbox.apache.org/repos/asf/superset.git
commit e9400222aa364cbd839ae64314e2b894991aea9b Author: Mehmet Salih Yavuz <salih.ya...@proton.me> AuthorDate: Sat Sep 20 16:09:23 2025 +0300 feat(Notification): add Ant Design notifications --- .../Notification/NotificationProvider.tsx | 59 ++++++++++++++++++++++ .../src/components/Notification/index.tsx | 26 ++++++++++ .../src/components/Notification/types.ts | 57 +++++++++++++++++++++ .../superset-ui-core/src/components/index.ts | 2 +- .../src/views/RootContextProviders.tsx | 32 ++++++------ 5 files changed, 160 insertions(+), 16 deletions(-) diff --git a/superset-frontend/packages/superset-ui-core/src/components/Notification/NotificationProvider.tsx b/superset-frontend/packages/superset-ui-core/src/components/Notification/NotificationProvider.tsx new file mode 100644 index 0000000000..bc15b86a69 --- /dev/null +++ b/superset-frontend/packages/superset-ui-core/src/components/Notification/NotificationProvider.tsx @@ -0,0 +1,59 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://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. + */ +import { notification as antdNotification } from 'antd'; +import { createContext, useContext, useMemo, ReactNode } from 'react'; +import type { NotificationContextType } from './types'; + +const NotificationContext = createContext<NotificationContextType | undefined>( + undefined, +); + +export const NotificationProvider = ({ children }: { children: ReactNode }) => { + const [api, contextHolder] = antdNotification.useNotification(); + + const value = useMemo<NotificationContextType>( + () => ({ + api, + success: args => api.success(args), + error: args => api.error(args), + warning: args => api.warning(args), + info: args => api.info(args), + open: args => api.open(args), + destroy: (key?: string) => api.destroy(key), + }), + [api], + ); + + return ( + <NotificationContext.Provider value={value}> + {contextHolder} + {children} + </NotificationContext.Provider> + ); +}; + +export const useNotification = (): NotificationContextType => { + const context = useContext(NotificationContext); + if (!context) { + throw new Error( + 'useNotification must be used within a NotificationProvider', + ); + } + return context; +}; diff --git a/superset-frontend/packages/superset-ui-core/src/components/Notification/index.tsx b/superset-frontend/packages/superset-ui-core/src/components/Notification/index.tsx new file mode 100644 index 0000000000..baba6ed3ee --- /dev/null +++ b/superset-frontend/packages/superset-ui-core/src/components/Notification/index.tsx @@ -0,0 +1,26 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://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. + */ +export { NotificationProvider, useNotification } from './NotificationProvider'; +export type { + NotificationApi, + NotificationContextType, + NotificationArgsProps, + NotificationPlacement, + NotificationType, +} from './types'; \ No newline at end of file diff --git a/superset-frontend/packages/superset-ui-core/src/components/Notification/types.ts b/superset-frontend/packages/superset-ui-core/src/components/Notification/types.ts new file mode 100644 index 0000000000..ad9d915573 --- /dev/null +++ b/superset-frontend/packages/superset-ui-core/src/components/Notification/types.ts @@ -0,0 +1,57 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://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. + */ +import type { NotificationInstance } from 'antd/es/notification/interface'; +import type { ReactNode } from 'react'; + +export type NotificationType = 'success' | 'info' | 'warning' | 'error'; + +export type NotificationPlacement = + | 'top' + | 'topLeft' + | 'topRight' + | 'bottom' + | 'bottomLeft' + | 'bottomRight'; + +export interface NotificationArgsProps { + message: ReactNode; + description?: ReactNode; + btn?: ReactNode; + key?: string; + onClose?: () => void; + duration?: number | null; + icon?: ReactNode; + placement?: NotificationPlacement; + className?: string; + onClick?: () => void; + closeIcon?: boolean | ReactNode; + role?: 'alert' | 'status'; +} + +export type NotificationApi = NotificationInstance; + +export interface NotificationContextType { + api: NotificationApi; + success: (args: NotificationArgsProps) => void; + error: (args: NotificationArgsProps) => void; + warning: (args: NotificationArgsProps) => void; + info: (args: NotificationArgsProps) => void; + open: (args: NotificationArgsProps & { type?: NotificationType }) => void; + destroy: (key?: string) => void; +} \ No newline at end of file diff --git a/superset-frontend/packages/superset-ui-core/src/components/index.ts b/superset-frontend/packages/superset-ui-core/src/components/index.ts index 6590688076..d1e49bb7a9 100644 --- a/superset-frontend/packages/superset-ui-core/src/components/index.ts +++ b/superset-frontend/packages/superset-ui-core/src/components/index.ts @@ -161,7 +161,6 @@ export { export { Image, type ImageProps } from './Image'; export { Popconfirm, type PopconfirmProps } from './Popconfirm'; export { Upload, type UploadFile, type UploadChangeParam } from './Upload'; -// Add these to your index.ts export * from './Menu'; export * from './Popover'; export * from './Radio'; @@ -176,6 +175,7 @@ export * from './TelemetryPixel'; export * from './UnsavedChangesModal'; export * from './constants'; export * from './Result'; +export * from './Notification'; export { ThemedAgGridReact, type ThemedAgGridReactProps, diff --git a/superset-frontend/src/views/RootContextProviders.tsx b/superset-frontend/src/views/RootContextProviders.tsx index fbff76b64c..7caa87943f 100644 --- a/superset-frontend/src/views/RootContextProviders.tsx +++ b/superset-frontend/src/views/RootContextProviders.tsx @@ -18,7 +18,7 @@ */ import { Route } from 'react-router-dom'; -import { getExtensionsRegistry } from '@superset-ui/core'; +import { getExtensionsRegistry, NotificationProvider } from '@superset-ui/core'; import { Provider as ReduxProvider } from 'react-redux'; import { QueryParamProvider } from 'use-query-params'; import { DndProvider } from 'react-dnd'; @@ -48,20 +48,22 @@ export const RootContextProviders: React.FC = ({ children }) => { <FlashProvider messages={common.flash_messages}> <EmbeddedUiConfigProvider> <DynamicPluginProvider> - <QueryParamProvider - ReactRouterRoute={Route} - stringifyOptions={{ encode: false }} - > - <ExtensionsProvider> - {RootContextProviderExtension ? ( - <RootContextProviderExtension> - {children} - </RootContextProviderExtension> - ) : ( - children - )} - </ExtensionsProvider> - </QueryParamProvider> + <NotificationProvider> + <QueryParamProvider + ReactRouterRoute={Route} + stringifyOptions={{ encode: false }} + > + <ExtensionsProvider> + {RootContextProviderExtension ? ( + <RootContextProviderExtension> + {children} + </RootContextProviderExtension> + ) : ( + children + )} + </ExtensionsProvider> + </QueryParamProvider> + </NotificationProvider> </DynamicPluginProvider> </EmbeddedUiConfigProvider> </FlashProvider>