docs/react-v9/contributing/rfcs/react-components/convergence/portal-compat.md
This RFC proposes compatibility measures between components in different Fluent UI versions that use ReactDOM.createPortal() via:
z-indexOnce components from Fluent UI React v9 (@fluentui/react-components) are put inside popovers & dialogs from Fluent UI React v8 (@fluentui/react) or Fluent UI React v0 (@fluentui/react-northstar) they don't have proper styles 💥
Examples are below:
Another problem is related to z-index values. Fluent UI React v8 (@fluentui/react) defines values that win over everything:
export namespace ZIndexes {
export const Nav: number = 1;
/**
* @deprecated Do not use
*/
export const ScrollablePane: number = 1;
export const FocusStyle: number = 1;
export const Coachmark: number = 1000;
export const Layer: number = 1000000;
export const KeytipLayer: number = 1000001;
}
As v9 does not use such values components that use portals (Popover, Menu, Tooltip) appear under components from v8.
Demo: https://codesandbox.io/s/v8layer-v9popover-q33b37
z-index valuesThe simplest option that satisfies these requirements is to use same values in Fluent UI React v9.
z-index=1000000 is uglyIntroduce Pub/Sub context where:
In the prototype the publisher component is called CompatHostV9:
useThemeClassName()fui-FluentProvider* class to any portal element that was registered by subscribersFunctional prototype: https://codesandbox.io/s/portal-compat-prototype-f59rn
PortalInner in @fluentui/react-northstar & Layer in @fluentui/react should:
React.createPortal() as containerSample code is below:
import React from 'react';
// "PortalCompatContext" & "usePortalCompat()" hook is everything that will be included to v8 & v0
export const PortalCompatContext = React.createContext(
() => {},
() => {},
);
export function usePortalCompat() {
return React.useContext(PortalCompatContext);
}
function Portal() {
// ⚠️ "document.createElement" is used for demo purposes
// This should be a real element where a portal will be mounted
const portalElement = document.createElement('div');
const [registerPortalEl, unregisterPortalEl] = usePortalCompat();
React.useEffect(() => {
registerPortalEl(portalElement);
return () => {
unregisterPortalEl(portalElement);
};
}, [portalElement, registerPortalEl, unregisterPortalEl]);
}
With these changes v0 & v8 portals will get proper classes with variables ➡️ styles for v9 components will look properly.
Once changes to v0 & v8 will be implemented customers would be able to inject CompatHostV9 (should be a child of FluentProvider from v9):
<V9Provider>
<CompatHostV9>
<V0Provider></V0Provider>
</CompatHostV9>
</V9Provider>
As there might be customers that use only v9 it's reasonable to keep CompatHostV9 separated from V9Provider.
As this package will be a dependency of both v0 & v8 we will get it into the dependency graph:
-----------------------------------
| @fluentui/react-portal-compat@9 |
-----------------------------------
▲ ▲
/ \
/ \
/ \
/ \
/ \
/ \
--------------------- -------------------------------
| @fluentui/react@8 | | @fluentui/react-northstar@0 |
--------------------- -------------------------------
The suggestion is to keep it inside monorepo and put it into a new subdirectory /packages/shared/ or /packages/compat/.
body (or non React data provider)
Portal in v9 does):
N/A