Skip to content

feat(js,react): inbox and styles under the shadow root #8262

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

Merged
merged 4 commits into from
May 16, 2025

Conversation

LetItRock
Copy link
Contributor

What changed? Why was the change needed?

Inbox
The new containerElement prop allows specifying the "container" that will be used to render Bell, Inbox and Popover, and its internal styles. The containerElement prop takes a Node which can be an HTMLElement or ShadowRoot, or a CSS selector.

Example of usage:

<Inbox
  {...config}
  containerElement={document.getElementById("inbox-root")?.shadowRoot}
/>

Screenshots

Screenshot 2025-05-07 at 13 59 47

Screen.Recording.2025-05-07.at.13.57.40.mov
Screen.Recording.2025-05-07.at.13.56.18.mov

Copy link

linear bot commented May 7, 2025

Copy link

netlify bot commented May 7, 2025

Deploy Preview for dashboard-v2-novu-staging canceled.

Name Link
🔨 Latest commit b4dde02
🔍 Latest deploy log https://app.netlify.com/projects/dashboard-v2-novu-staging/deploys/6826f52b4418c30008a8cc35

@@ -129,7 +129,7 @@ export const DefaultNotification = (props: DefaultNotificationProps) => {
{(subject) => (
<Markdown
appearanceKey="notificationSubject"
class="nt-text-start nt-font-medium"
class="nt-text-start nt-font-medium nt-whitespace-pre-wrap [word-break:break-word]"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

fix for long text breaking UI:
Screenshot 2025-05-07 at 17 02 35

@@ -67,33 +68,40 @@ type RendererProps = {
preferencesFilter?: PreferencesFilter;
routerPush?: RouterPush;
novu?: Novu;
containerElement?: Node | null | undefined;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

the new prop that defines where the Inbox and its styles will be embedded; the name TBD;

const id = 'novu-default-css';
const el = document.getElementById(id);
const id = NOVU_DEFAULT_CSS_ID;
const root = props.containerElement instanceof ShadowRoot ? props.containerElement : document;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

if the containerElement is a shadow root, then use it to find the default styles

Comment on lines 89 to 90
const stylesContainer = props.containerElement ?? document.head;
stylesContainer.insertBefore(styleEl, stylesContainer.firstChild);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

inject the default styles to the containerElement if provided otherwise document head

if (
active() !== floating() ||
floating()?.contains(e.target as Node) ||
(container && (e.target as Element).shadowRoot === container)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

if the target shadow root is the container where the inbox is embedded, then do not close it;

Comment on lines 16 to 20
const getRoot = () => {
const container = containerElement();

return container instanceof ShadowRoot ? container : document;
};
Copy link
Contributor Author

Choose a reason for hiding this comment

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

handle tab and keyboard navigation on components, for example tabs


return (
<Show when={open()}>
{/* we can safely use portal to document.body here as this element
won't be focused and close other portals (outside solid world) as a result */}
<Portal>
<Portal mount={portalContainer()}>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

mount tooltips inside the "container" not body

@@ -28,7 +28,7 @@ export function TooltipRoot(props: TooltipRootProps) {

const position = useFloating(reference, floating, {
placement: props.placement || 'top',
strategy: 'absolute',
strategy: 'fixed',
Copy link
Contributor Author

Choose a reason for hiding this comment

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

the absolute strategy doesn't work well for the tooltips, that's why changed to fixed

Comment on lines +67 to +72
const defaultCssStyles = root.getElementById(NOVU_DEFAULT_CSS_ID);
if (defaultCssStyles) {
stylesContainer.insertBefore(styleEl, defaultCssStyles.nextSibling);
} else {
stylesContainer.appendChild(styleEl);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

default styles and appearance styles should come together one after another

Comment on lines 76 to 78
if (typeof containerElement === 'string') {
return document.querySelector(containerElement) ?? document.getElementById(containerElement);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

if the containerElement is CSS selector then try to find elements

@@ -38,6 +38,7 @@ export type BaseProps = {
localization?: Localization;
tabs?: Array<Tab>;
preferencesFilter?: PreferencesFilter;
containerElement?: Node | null;
Copy link
Contributor

Choose a reason for hiding this comment

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

@LetItRock what do you think about: container similar to Radix terminology?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@scopsy yes, Emotion is using similar name as well ;)

Copy link
Contributor

Choose a reason for hiding this comment

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

Cool! Than let's go with it?

Copy link

pkg-pr-new bot commented May 15, 2025

Open in StackBlitz

@novu/js

npm i https://pkg.pr.new/novuhq/novu/@novu/js@8262

@novu/nextjs

npm i https://pkg.pr.new/novuhq/novu/@novu/nextjs@8262

novu

npm i https://pkg.pr.new/novuhq/novu@8262

@novu/react

npm i https://pkg.pr.new/novuhq/novu/@novu/react@8262

@novu/react-native

npm i https://pkg.pr.new/novuhq/novu/@novu/react-native@8262

commit: b4dde02

@LetItRock LetItRock merged commit 92d1001 into next May 16, 2025
28 checks passed
@LetItRock LetItRock deleted the nv-5817-inbox-shadow-root-as-container branch May 16, 2025 09:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants