Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
bddc4b8
adding `NotificationContainer` to be more inline with `Mantine`
BSd3v Mar 20, 2025
5e32dd3
adjustments to allow for the app to have the current state of notific…
BSd3v Mar 20, 2025
c4cd4c2
duplicating other notification tests for new flow
BSd3v Mar 20, 2025
bff0077
Merge remote-tracking branch 'upstream/master' into notifications-rework
BSd3v Mar 20, 2025
89f44fb
additional test to clear the queue and also tests the store status
BSd3v Mar 20, 2025
193daf9
adjusting last notification test to also use the api to clean all the…
BSd3v Mar 20, 2025
8814ee2
adjustments to avoid notifications being rendered into the dash conte…
BSd3v Mar 21, 2025
0723365
Merge remote-tracking branch 'upstream/master' into notifications-rework
BSd3v Apr 28, 2025
e5c2905
adjustments to split show, update and hide into different props
BSd3v Apr 28, 2025
51ad6f1
removing split prop notifications to `sendNotifications` and you can …
BSd3v Apr 28, 2025
3712aae
Merge remote-tracking branch 'upstream/master' into notifications-rework
BSd3v May 19, 2025
ebb012c
adding deprecation and changelog
BSd3v May 19, 2025
d429887
adding `portalProps`
BSd3v May 21, 2025
1bf0776
fixing doc strings for `Notification` typing of `sendNotifications`
BSd3v May 21, 2025
c902246
adjusting for error in build with html elements
BSd3v May 21, 2025
879a0f3
adding better error handling for action and positions that are passed…
BSd3v May 21, 2025
0412e32
Update src/ts/components/extensions/notifications/Notification.tsx
BSd3v May 21, 2025
01b8738
Update src/ts/components/extensions/notifications/NotificationProvide…
BSd3v May 21, 2025
30dcbdb
fix hide and handle dict ids
AnnMarieW May 22, 2025
ecb6465
update stringifyId
AnnMarieW May 22, 2025
db90984
test for hide and dict ids
AnnMarieW May 22, 2025
ff460aa
use throw new Error so errors show up in devtools
AnnMarieW May 23, 2025
cebe0cc
updated warning message
AnnMarieW May 29, 2025
0420d2d
Merge branch 'master' into notifications-rework
AnnMarieW Jun 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Change Log

# unreleased

### Added
- Added new 'NotificationContainer' that works more closely to upstream Notifications in Mantine.
- Exposed the `notifications` api of Mantine for granular control at `dash_mantine_components.appNotifications.api`
- Exposed the `store` of notifications at `dash_mantine_components.store`

### Changed
- Marked `Notification` and `NotificationProvider` for deprecation

# 1.3.0

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const Notification = (props: Props) => {
const { action, setProps, loading_state, ...others } = props;

useEffect(() => {
console.warn('this method of Notifications is deprecated and will be removed in the next major release')
Comment thread
BSd3v marked this conversation as resolved.
Outdated
switch (action) {
case "show":
notifications.show(others);
Expand Down
151 changes: 151 additions & 0 deletions src/ts/components/extensions/notifications/NotificationContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { Notifications, notifications, useNotifications, notificationsStore } from "@mantine/notifications";
import { BoxProps } from "props/box";
import { DashBaseProps } from "props/dash";
import { StylesApiProps } from "props/styles";
import React, {useEffect, useState} from "react";
import { getLoadingState, newRenderDashComponents, getContextPath } from "../../../utils/dash3";
import { MantineColor, MantineRadius } from "@mantine/core";
import {omit, equals} from 'ramda';

// Define appNotificationHolder as a mutable object
const appNotificationHolder: Record<string, any> = {};
const allowedActions = ["show", "update", "hide"] as const;
const allowedPositions = ['top-left', 'top-right', 'bottom-left', 'bottom-right', 'top-center', 'bottom-center'] as const;
export type Action = typeof allowedActions[number];
export type Position = typeof allowedPositions[number];


// Create a proxy for appNotifications
export const appNotifications = new Proxy(appNotificationHolder, {
get(target, key) {
if (typeof key === "symbol") {
return undefined; // Prevent errors when symbols are used
}
if (key === 'store') {
// Call the function when 'state' is accessed
return target[key]();
}
return target[key];
},
set(target, key, value) {
if (typeof key === "symbol") {
return false; // Prevent errors when symbols are used
}
target[key] = value;
return true;
}
});

// Define the Notification interface based on your requirements
interface Notification extends BoxProps, StylesApiProps {
/** Controls notification line or icon color, key of `theme.colors` or any valid CSS color, `theme.primaryColor` by default */
color?: MantineColor;
/** Key of `theme.radius` or any valid CSS value to set `border-radius`, `theme.defaultRadius` by default */
radius?: MantineRadius;
/** Notification icon, replaces color line */
icon?: any;
/** Notification title, displayed before body */
title?:any;
/** Replaces colored line or icon with Loader component */
loading?: boolean;
/** Determines whether notification should have a border, `false` by default */
withBorder?: boolean;
/** Determines whether close button should be visible, `true` by default */
withCloseButton?: boolean;
/** Props passed down to the close button */
closeButtonProps?: Record<string, any>;
/** Notification id, can be used to close or update notification */
id?: string;
/** Notification message, required for all notifications */
message: any;
/** Determines whether notification should be closed automatically,
* number is auto close timeout in ms, overrides `autoClose` from `Notifications`
* */
autoClose?: boolean | number;
/** action */
action: Action;
/** Position on the screen to display the notification. */
position?: Position;
}

interface Props extends BoxProps, StylesApiProps, DashBaseProps {
/** Notifications position, `'bottom-right'` by default */
position?: Position;
/** Auto close timeout for all notifications in ms, `false` to disable auto close, can be overwritten for individual notifications in `notifications.show` function, `4000` by defualt */
autoClose?: number | false;
/** Notification transition duration in ms, `250` by default */
transitionDuration?: number;
/** Notification width, cannot exceed 100%, `440` by default */
containerWidth?: number | string;
/** Notification `max-height`, used for transitions, `200` by default */
notificationMaxHeight?: number | string;
/** Maximum number of notifications displayed at a time, other new notifications will be added to queue, `5` by default */
limit?: number;
/** Notifications container z-index, `400` by default */
zIndex?: string | number;
/** Determines whether notifications container should be rendered inside `Portal`, `true` by default */
withinPortal?: boolean;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

missing the portalProps prop.
Also, do we need the store prop?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

store as in what? Is this to be from the notifications? If so, we'd have to have a hook from the store to know when it updated.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Ok, let's skip the store prop

/** Props to pass down to the Portal when withinPortal is true */
portalProps?: object;
/** Notifications to be passed to the API */
sendNotifications?: Notification[];
/** Notifications API: removes all notifications from the notifications state and queue*/
clean?: boolean;
/** Notifications API: removes all notifications from the queue*/
cleanQueue?: boolean;
}

/** NotificationContainer */
const NotificationContainer = (props: Props) => {
const { setProps, loading_state, sendNotifications, clean, cleanQueue, ...others } = props;

const componentPath = getContextPath()

useEffect(() => {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is there a reason for using useEffect rather than Mantine's UseDidUpdate ? https://mantine.dev/hooks/use-did-update/

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

useDidUpdate only runs on the prop updates, this would not capture sendNotifications initially sent to the component.

if (sendNotifications) {
sendNotifications.forEach((notification) => {
const {action, ...realNotification} = notification;
// Validate action is one of the accepted values
if (!allowedActions.includes(action || 'show')) {
console.error(`Invalid action: '${action}' passed to action prop; should be one of '${allowedActions.join("','")}'`);
return; // Skip this notification
}
if (!allowedPositions.includes(realNotification?.position || 'bottom-right')) {
console.error(`Invalid position: '${realNotification?.position}' passed to position prop; should be one of '${allowedPositions.join("','")}'`);
return; // Skip this notification
}
notifications[action || 'show'](newRenderDashComponents(realNotification, ['message', 'icon', 'title']));
});
setProps({ sendNotifications: [] }); // Avoid duplicate processing
}
}, [sendNotifications]);

useEffect(() => {
if (cleanQueue) {
notifications.cleanQueue()
setProps({cleanQueue: false})
}
}, [cleanQueue]);

useEffect(() => {
if (clean) {
notifications.clean()
setProps({clean: false})
}
}, [clean]);

useEffect(() => {
appNotifications['api'] = notifications
appNotifications['store'] = notificationsStore.getState
}, [])

return (
<Notifications
data-dash-is-loading={getLoadingState(loading_state) || undefined}
store={notificationsStore}
{...others}
/>
);
};

export default NotificationContainer
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Notifications } from "@mantine/notifications";
import { BoxProps } from "props/box";
import { DashBaseProps } from "props/dash";
import { StylesApiProps } from "props/styles";
import React from "react";
import React, {useEffect} from "react";
import { getLoadingState } from "../../../utils/dash3";

interface Props extends BoxProps, StylesApiProps, DashBaseProps {
Expand Down Expand Up @@ -34,6 +34,10 @@ interface Props extends BoxProps, StylesApiProps, DashBaseProps {
const NotificationProvider = (props: Props) => {
const { setProps, loading_state, ...others } = props;

useEffect(() => {
console.warn('this method of Notifications is deprecated and will be removed in the next major release')
Comment thread
BSd3v marked this conversation as resolved.
Outdated
}, [])

return (
<Notifications
data-dash-is-loading={getLoadingState(loading_state) || undefined}
Expand Down
3 changes: 3 additions & 0 deletions src/ts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ import CodeHighlightTabs from "./components/extensions/codehighlight/CodeHighlig
import InlineCodeHighlight from "./components/extensions/codehighlight/InlineCodeHighlight";
import Notification from "./components/extensions/notifications/Notification";
import NotificationProvider from "./components/extensions/notifications/NotificationProvider";
import NotificationContainer, {appNotifications} from "./components/extensions/notifications/NotificationContainer";
import NavigationProgress from "./components/extensions/nprogress/NavigationProgress";
import NavigationProgressProvider from "./components/extensions/nprogress/NavigationProgressProvider";
import MantineProvider from "./components/styles/MantineProvider";
Expand Down Expand Up @@ -258,6 +259,7 @@ export {
NavigationProgressProvider,
Notification,
NotificationProvider,
NotificationContainer,
NumberFormatter,
NumberInput,
Overlay,
Expand Down Expand Up @@ -326,4 +328,5 @@ export {
YearPickerInput,
RichTextEditor,
TypographyStylesProvider,
appNotifications
};
Loading