diff --git a/.changeset/fix-mui-breadcrumb-styling.md b/.changeset/fix-mui-breadcrumb-styling.md new file mode 100644 index 0000000000000..b21ff3128a202 --- /dev/null +++ b/.changeset/fix-mui-breadcrumb-styling.md @@ -0,0 +1,11 @@ +--- +"@refinedev/mui": patch +--- + +fix(mui): use MuiLink in Breadcrumb LinkRouter to respect styling props + +The `LinkRouter` helper in the Breadcrumb component was spreading MUI `LinkProps` (sx, underline, color, variant) onto a plain `` element, which silently ignored all styling. Replaced with `` so props are properly processed by MUI's styling system. + +This was a regression from the v5 migration (commit 5d63ada, #6945). + +Fixes #7462 diff --git a/.changeset/fix-notification-type.md b/.changeset/fix-notification-type.md new file mode 100644 index 0000000000000..366432e13f895 --- /dev/null +++ b/.changeset/fix-notification-type.md @@ -0,0 +1,12 @@ +--- +"@refinedev/antd": patch +"@refinedev/mantine": patch +--- + +fix(antd,mantine): respect notification type in notification providers + +**@refinedev/antd**: Use `notification.success()` and `notification.error()` shortcut methods instead of `notification.open({ type })`, which does not render type-specific icons or colors in Ant Design. + +**@refinedev/mantine**: Properly map notification types to distinct colors and icons — `success` renders with green/check, `error` renders with red/x — instead of treating all non-success types as error. + +Fixes #7477 diff --git a/packages/antd/src/components/undoableNotification/index.tsx b/packages/antd/src/components/undoableNotification/index.tsx index bb8645d4003cf..3630061eee2c5 100644 --- a/packages/antd/src/components/undoableNotification/index.tsx +++ b/packages/antd/src/components/undoableNotification/index.tsx @@ -37,6 +37,7 @@ export const UndoableNotification: React.FC = ({ onClick={cancelMutation} disabled={undoableTimeout === 0} icon={} + aria-label="undo" /> ); diff --git a/packages/antd/src/providers/notificationProvider/index.spec.tsx b/packages/antd/src/providers/notificationProvider/index.spec.tsx index 0cf483d96f8ea..48d565d7df45c 100644 --- a/packages/antd/src/providers/notificationProvider/index.spec.tsx +++ b/packages/antd/src/providers/notificationProvider/index.spec.tsx @@ -22,22 +22,24 @@ describe("Antd useNotificationProvider", () => { }); const notificationOpenSpy = vi.spyOn(notification, "open"); + const notificationSuccessSpy = vi.spyOn(notification, "success"); + const notificationErrorSpy = vi.spyOn(notification, "error"); const notificationCloseSpy = vi.spyOn(notification, "destroy"); - it("should render notification type succes notification", async () => { + it("should render notification type success notification using success method", async () => { const { result } = renderHook(() => useNotificationProvider(), {}); result.current.open?.(mockNotification); - expect(notificationOpenSpy).toHaveBeenCalledTimes(1); - expect(notificationOpenSpy).toHaveBeenCalledWith({ - ...mockNotification, + expect(notificationSuccessSpy).toHaveBeenCalledTimes(1); + expect(notificationSuccessSpy).toHaveBeenCalledWith({ + key: mockNotification.key, message: null, description: mockNotification.message, }); }); - it("should render notification type error notification", async () => { + it("should render notification type error notification using error method", async () => { const { result } = renderHook(() => useNotificationProvider(), {}); result.current.open?.({ @@ -45,12 +47,11 @@ describe("Antd useNotificationProvider", () => { type: "error", }); - expect(notificationOpenSpy).toHaveBeenCalledTimes(1); - expect(notificationOpenSpy).toHaveBeenCalledWith({ - ...mockNotification, + expect(notificationErrorSpy).toHaveBeenCalledTimes(1); + expect(notificationErrorSpy).toHaveBeenCalledWith({ + key: mockNotification.key, message: null, description: mockNotification.message, - type: "error", }); }); @@ -62,15 +63,15 @@ describe("Antd useNotificationProvider", () => { description: "Notification Description", }); - expect(notificationOpenSpy).toHaveBeenCalledTimes(1); - expect(notificationOpenSpy).toHaveBeenCalledWith({ - ...mockNotification, + expect(notificationSuccessSpy).toHaveBeenCalledTimes(1); + expect(notificationSuccessSpy).toHaveBeenCalledWith({ + key: mockNotification.key, message: "Notification Description", description: "Test Notification Message", }); }); - it("should render notification type error notification", async () => { + it("should render progress notification using open method", async () => { const { result } = renderHook(() => useNotificationProvider(), {}); result.current.open?.({ @@ -109,12 +110,16 @@ describe("Antd useNotificationProvider", () => { describe("using with Ant design's App component", () => { const openFn = vi.fn(); + const successFn = vi.fn(); + const errorFn = vi.fn(); const destroyFn = vi.fn(); beforeAll(() => { vi.spyOn(App, "useApp").mockReturnValue({ notification: { open: openFn, + success: successFn, + error: errorFn, destroy: destroyFn, }, } as unknown as ReturnType); @@ -124,7 +129,7 @@ describe("Antd useNotificationProvider", () => { vi.clearAllMocks(); }); - it("should render notification type succes notification", async () => { + it("should render notification type success notification using success method", async () => { const { result } = renderHook(() => useNotificationProvider()); act(() => { @@ -132,16 +137,16 @@ describe("Antd useNotificationProvider", () => { }); await waitFor(() => { - expect(openFn).toHaveBeenCalledTimes(1); - expect(openFn).toHaveBeenCalledWith({ - ...mockNotification, + expect(successFn).toHaveBeenCalledTimes(1); + expect(successFn).toHaveBeenCalledWith({ + key: mockNotification.key, message: null, description: mockNotification.message, }); }); }); - it("should render notification type error notification", async () => { + it("should render notification type error notification using error method", async () => { const { result } = renderHook(() => useNotificationProvider()); act(() => { @@ -152,12 +157,11 @@ describe("Antd useNotificationProvider", () => { }); await waitFor(() => { - expect(openFn).toHaveBeenCalledTimes(1); - expect(openFn).toHaveBeenCalledWith({ - ...mockNotification, + expect(errorFn).toHaveBeenCalledTimes(1); + expect(errorFn).toHaveBeenCalledWith({ + key: mockNotification.key, message: null, description: mockNotification.message, - type: "error", }); }); }); @@ -173,16 +177,16 @@ describe("Antd useNotificationProvider", () => { }); await waitFor(() => { - expect(openFn).toHaveBeenCalledTimes(1); - expect(openFn).toHaveBeenCalledWith({ - ...mockNotification, + expect(successFn).toHaveBeenCalledTimes(1); + expect(successFn).toHaveBeenCalledWith({ + key: mockNotification.key, message: "Notification Description", description: "Test Notification Message", }); }); }); - it("should render notification type error notification", async () => { + it("should render progress notification using open method", async () => { const { result } = renderHook(() => useNotificationProvider()); act(() => { diff --git a/packages/antd/src/providers/notificationProvider/index.tsx b/packages/antd/src/providers/notificationProvider/index.tsx index 9d77c91325344..da6d3310073a5 100644 --- a/packages/antd/src/providers/notificationProvider/index.tsx +++ b/packages/antd/src/providers/notificationProvider/index.tsx @@ -39,11 +39,15 @@ export const useNotificationProvider = (): NotificationProvider => { closeIcon: <>, }); } else { - notification.open({ + const notificationMethod = + type && type in notification + ? notification[type as "success" | "error"] + : notification.open; + + notificationMethod({ key, description: message, message: description ?? null, - type, }); } }, diff --git a/packages/mantine/src/providers/notificationProvider.tsx b/packages/mantine/src/providers/notificationProvider.tsx index d4caf5b1e3d69..2e247e5680cb1 100644 --- a/packages/mantine/src/providers/notificationProvider.tsx +++ b/packages/mantine/src/providers/notificationProvider.tsx @@ -59,6 +59,7 @@ export const useNotificationProvider = (): NotificationProvider => { { cancelMutation?.(); if (key) { @@ -97,6 +98,7 @@ export const useNotificationProvider = (): NotificationProvider => { { cancelMutation?.(); if (key) { @@ -123,16 +125,22 @@ export const useNotificationProvider = (): NotificationProvider => { }); } } else { + const notificationColor = + type === "success" ? "primary" : type === "error" ? "red" : "primary"; + const notificationIcon = + type === "success" ? ( + + ) : type === "error" ? ( + + ) : ( + + ); + if (isNotificationActive(key)) { updateNotification({ id: key!, - color: type === "success" ? "primary" : "red", - icon: - type === "success" ? ( - - ) : ( - - ), + color: notificationColor, + icon: notificationIcon, message, title: description, autoClose: 5000, @@ -141,13 +149,8 @@ export const useNotificationProvider = (): NotificationProvider => { addNotification(key); showNotification({ id: key!, - color: type === "success" ? "primary" : "red", - icon: - type === "success" ? ( - - ) : ( - - ), + color: notificationColor, + icon: notificationIcon, message, title: description, onClose: () => { diff --git a/packages/mui/src/components/breadcrumb/index.tsx b/packages/mui/src/components/breadcrumb/index.tsx index d9428810aa51f..2013a547621fd 100644 --- a/packages/mui/src/components/breadcrumb/index.tsx +++ b/packages/mui/src/components/breadcrumb/index.tsx @@ -39,7 +39,9 @@ export const Breadcrumb: React.FC = ({ const { to, children, ...restProps } = props; return ( - {children} + + {children} + ); }; diff --git a/packages/mui/src/components/crud/create/index.tsx b/packages/mui/src/components/crud/create/index.tsx index dffda0cab8627..d8033de1cb89a 100644 --- a/packages/mui/src/components/crud/create/index.tsx +++ b/packages/mui/src/components/crud/create/index.tsx @@ -121,6 +121,7 @@ export const Create: React.FC = ({ goBackFromProps ) : ( = ({ goBackFromProps ) : ( = ({ goBackFromProps ) : ( = ({ > {!siderCollapsed && ( - setSiderCollapsed(true)}> + setSiderCollapsed(true)} + > {} )} diff --git a/packages/mui/src/providers/notificationProvider/index.tsx b/packages/mui/src/providers/notificationProvider/index.tsx index 0d327d4c82d87..668b01fa32c1d 100644 --- a/packages/mui/src/providers/notificationProvider/index.tsx +++ b/packages/mui/src/providers/notificationProvider/index.tsx @@ -30,6 +30,7 @@ export const useNotificationProvider = (): NotificationProvider => { closeSnackbar(key); }} color="inherit" + aria-label="undo" >