diff --git a/change/@fluentui-react-portal-ea64a7c1-d032-4c04-a5b8-083f1685c723.json b/change/@fluentui-react-portal-ea64a7c1-d032-4c04-a5b8-083f1685c723.json new file mode 100644 index 00000000000000..0c1107003b8a57 --- /dev/null +++ b/change/@fluentui-react-portal-ea64a7c1-d032-4c04-a5b8-083f1685c723.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "refactor: remove Griffel dependency from usePortalMountNode", + "packageName": "@fluentui/react-portal", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-portal/library/src/components/Portal/usePortalMountNode.ts b/packages/react-components/react-portal/library/src/components/Portal/usePortalMountNode.ts index 60f46090a85790..adb6cf794765b8 100644 --- a/packages/react-components/react-portal/library/src/components/Portal/usePortalMountNode.ts +++ b/packages/react-components/react-portal/library/src/components/Portal/usePortalMountNode.ts @@ -6,10 +6,9 @@ import { useFluent_unstable as useFluent, usePortalMountNode as usePortalMountNodeContext, } from '@fluentui/react-shared-contexts'; -import { mergeClasses } from '@griffel/react'; import { useFocusVisible } from '@fluentui/react-tabster'; -import { usePortalMountNodeStylesStyles } from './usePortalMountNodeStyles.styles'; +import { usePortalMountNodeStyles } from './usePortalMountNodeStyles'; const useInsertionEffect = (React as never)['useInsertion' + 'Effect'] as typeof React.useLayoutEffect | undefined; @@ -198,16 +197,14 @@ const useModernElementFactory: UseElementFactory = options => { return; } - const classesToApply = className.split(' ').filter(Boolean); - - elementProxy.classList.add(...classesToApply); + elementProxy.setAttribute('class', className); elementProxy.setAttribute('dir', dir); elementProxy.setAttribute('data-portal-node', 'true'); focusVisibleRef.current = elementProxy; return () => { - elementProxy.classList.remove(...classesToApply); + elementProxy.removeAttribute('class'); elementProxy.removeAttribute('dir'); }; }, [className, dir, elementProxy, focusVisibleRef]); @@ -243,7 +240,6 @@ export const usePortalMountNode = (options: UsePortalMountNodeOptions): HTMLElem // eslint-disable-next-line @typescript-eslint/no-deprecated const focusVisibleRef = useFocusVisible() as React.MutableRefObject; - const classes = usePortalMountNodeStylesStyles(); const themeClassName = useThemeClassName(); const factoryOptions: UseElementFactoryOptions = { @@ -251,9 +247,11 @@ export const usePortalMountNode = (options: UsePortalMountNodeOptions): HTMLElem disabled: options.disabled, focusVisibleRef, - className: mergeClasses(themeClassName, classes.root, options.className), + className: [themeClassName, options.className].filter(Boolean).join(' '), targetNode: mountNode ?? targetDocument?.body, }; + usePortalMountNodeStyles(options.disabled); + return useElementFactory(factoryOptions); }; diff --git a/packages/react-components/react-portal/library/src/components/Portal/usePortalMountNodeStyles.styles.ts b/packages/react-components/react-portal/library/src/components/Portal/usePortalMountNodeStyles.styles.ts deleted file mode 100644 index ed71de80786792..00000000000000 --- a/packages/react-components/react-portal/library/src/components/Portal/usePortalMountNodeStyles.styles.ts +++ /dev/null @@ -1,18 +0,0 @@ -'use client'; - -import { makeStyles } from '@griffel/react'; - -export const usePortalMountNodeStylesStyles = makeStyles({ - root: { - // Creates new stacking context to prevent z-index issues - // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_positioned_layout/Understanding_z-index/Stacking_context - // - // Also keeps a portal on top of a page to prevent scrollbars from appearing - position: 'absolute', - top: 0, - left: 0, - right: 0, - - zIndex: 1000000, - }, -}); diff --git a/packages/react-components/react-portal/library/src/components/Portal/usePortalMountNodeStyles.test.tsx b/packages/react-components/react-portal/library/src/components/Portal/usePortalMountNodeStyles.test.tsx new file mode 100644 index 00000000000000..af3093466b5189 --- /dev/null +++ b/packages/react-components/react-portal/library/src/components/Portal/usePortalMountNodeStyles.test.tsx @@ -0,0 +1,97 @@ +import { renderHook } from '@testing-library/react-hooks'; +import { Provider_unstable as Provider } from '@fluentui/react-shared-contexts'; +import * as React from 'react'; + +import { usePortalMountNodeStyles, PORTAL_STYLE_ELEMENT_ID, setPortalRefCount } from './usePortalMountNodeStyles'; + +function queryStyleElement(): HTMLStyleElement | null { + return document.head.querySelector(`#${PORTAL_STYLE_ELEMENT_ID}`); +} + +function createWrapper(targetDocument: Document | undefined) { + return (props: { children?: React.ReactNode }) => ( + {props.children} + ); +} + +describe('usePortalMountNodeStyles', () => { + afterEach(() => { + // Clean up any leftover style elements and reset the ref count + queryStyleElement()?.remove(); + setPortalRefCount(document, 0); + }); + + it('injects a