Skip to content

Commit

Permalink
Merge branch 'main' into Adam-Meza/test-all-config
Browse files Browse the repository at this point in the history
  • Loading branch information
AdamMeza-Bentley authored Mar 27, 2024
2 parents 3227bad + 15013b7 commit 3061a51
Show file tree
Hide file tree
Showing 15 changed files with 104 additions and 62 deletions.
5 changes: 5 additions & 0 deletions .changeset/great-news-beg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@itwin/itwinui-react": patch
---

Fixed a regression in `Tabs` where the panel content was not occupying the full width of the container.
5 changes: 5 additions & 0 deletions .changeset/little-mugs-tap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@itwin/itwinui-css": patch
---

Fixed a regression in `iui-tabs` where the panel content was not occupying the full width of the container.
5 changes: 5 additions & 0 deletions .changeset/nine-fishes-itch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@itwin/itwinui-react": patch
---

Fixed an issue in older Safari versions where visually-hidden styles inside `ProgressRadial` were not being applied.
3 changes: 0 additions & 3 deletions examples/Tabs.actions.css

This file was deleted.

2 changes: 1 addition & 1 deletion examples/Tabs.actions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Tabs, Button } from '@itwin/itwinui-react';

export default () => {
return (
<Tabs.Wrapper className='demo-container'>
<Tabs.Wrapper>
<Tabs.TabList>
<Tabs.Tab value='apple' label='Apple' key='apple' />
<Tabs.Tab value='orange' label='Orange' key='orange' />
Expand Down
3 changes: 0 additions & 3 deletions examples/Tabs.borderless.css

This file was deleted.

2 changes: 1 addition & 1 deletion examples/Tabs.borderless.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Tabs } from '@itwin/itwinui-react';

export default () => {
return (
<Tabs.Wrapper type='borderless' className='demo-container'>
<Tabs.Wrapper type='borderless'>
<Tabs.TabList>
<Tabs.Tab value='apple' label='Apple' key='apple' />
<Tabs.Tab value='orange' label='Orange' key='orange' />
Expand Down
3 changes: 0 additions & 3 deletions examples/Tabs.controlled.css

This file was deleted.

1 change: 0 additions & 1 deletion examples/Tabs.controlled.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export default () => {
value={currentTabValue}
onValueChange={(value) => setCurrentTabValue(value)}
defaultValue='pear'
className='demo-container'
>
<Tabs.TabList>
<Tabs.Tab value='apple' label='Apple' key='apple' />
Expand Down
3 changes: 0 additions & 3 deletions examples/Tabs.main.css

This file was deleted.

2 changes: 1 addition & 1 deletion examples/Tabs.main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Tabs } from '@itwin/itwinui-react';

export default () => {
return (
<Tabs.Wrapper className='demo-container'>
<Tabs.Wrapper>
<Tabs.TabList>
<Tabs.Tab value='apple' label='Apple' key='apple' />
<Tabs.Tab value='orange' label='Orange' key='orange' />
Expand Down
3 changes: 0 additions & 3 deletions examples/Tabs.pill.css

This file was deleted.

2 changes: 1 addition & 1 deletion examples/Tabs.pill.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Tabs } from '@itwin/itwinui-react';

export default () => {
return (
<Tabs.Wrapper type='pill' className='demo-container'>
<Tabs.Wrapper type='pill'>
<Tabs.TabList>
<Tabs.Tab value='apple' label='Apple' key='apple' />
<Tabs.Tab value='orange' label='Orange' key='orange' />
Expand Down
1 change: 1 addition & 0 deletions packages/itwinui-css/src/tabs/base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ $borderless-horizontal-tab-min-height: calc(
grid-template-columns: 1fr auto;
grid-template-rows: auto 1fr;
contain: inline-size;
inline-size: 100%;

.iui-tabs {
display: flex;
Expand Down
126 changes: 84 additions & 42 deletions packages/itwinui-react/src/core/utils/components/ShadowRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,54 +4,24 @@
*--------------------------------------------------------------------------------------------*/
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { useLatestRef, useLayoutEffect } from '../hooks/index.js';

const isBrowser = typeof document !== 'undefined';
const supportsDSD =
isBrowser && 'shadowRootMode' in HTMLTemplateElement.prototype;
const supportsAdoptedStylesheets =
isBrowser && 'adoptedStyleSheets' in Document.prototype;

type ShadowRootProps = { children: React.ReactNode; css?: string };

/**
* Wrapper around `<template>` element that attaches shadow root to its parent
* and moves its children into the shadow root.
*
* @private
*/
export const ShadowRoot = ({
children,
css,
}: {
children: React.ReactNode;
css?: string;
}) => {
const [shadowRoot, setShadowRoot] = React.useState<ShadowRoot>();
export const ShadowRoot = ({ children, css }: ShadowRootProps) => {
const isFirstRender = useIsFirstRender();
const styleSheet = React.useRef<CSSStyleSheet>();

const attachShadowRef = React.useCallback(
(template: HTMLTemplateElement | null) => {
const parent = template?.parentElement;
if (!template || !parent) {
return;
}
if (parent.shadowRoot) {
parent.shadowRoot.replaceChildren(); // Remove previous shadowroot content
}
queueMicrotask(() => {
const shadow =
parent.shadowRoot || parent.attachShadow({ mode: 'open' });

if (css && supportsAdoptedStylesheets) {
styleSheet.current ||= new CSSStyleSheet();
styleSheet.current.replaceSync(css);
shadow.adoptedStyleSheets = [styleSheet.current];
}

ReactDOM.flushSync(() => setShadowRoot(shadow));
});
},
[css],
);

if (!isBrowser) {
return (
Expand All @@ -68,19 +38,91 @@ export const ShadowRoot = ({
return null;
}

return (
<>
{shadowRoot ? (
ReactDOM.createPortal(children, shadowRoot)
) : (
<template ref={attachShadowRef} />
)}
</>
return <ClientShadowRoot css={css}>{children}</ClientShadowRoot>;
};

// ----------------------------------------------------------------------------

const ClientShadowRoot = ({ children, css }: ShadowRootProps) => {
const templateRef = React.useRef<HTMLTemplateElement>(null);
const shadowRoot = useShadowRoot(templateRef, { css });

// fallback to <style> tag if adoptedStyleSheets is not supported
const fallbackCss =
!supportsAdoptedStylesheets && css ? <style>{css}</style> : null;

return shadowRoot ? (
ReactDOM.createPortal(
<>
{fallbackCss}
{children}
</>,
shadowRoot,
)
) : (
<template ref={templateRef} />
);
};

// ----------------------------------------------------------------------------

/**
* Given a ref, this hook will return a shadowRoot attached to its parent element.
*
* The css will be added to the shadowRoot using `adoptedStyleSheets` (if supported).
*/
function useShadowRoot(
templateRef: React.RefObject<HTMLElement>,
{ css = '' },
) {
const [shadowRoot, setShadowRoot] = React.useState<ShadowRoot | null>(null);
const styleSheet = React.useRef<CSSStyleSheet>();
const latestCss = useLatestRef(css);

useLayoutEffect(() => {
const parent = templateRef.current?.parentElement;
if (!parent) {
return;
}

if (parent.shadowRoot) {
parent.shadowRoot.replaceChildren(); // Remove previous shadowroot content
}

const shadow = parent.shadowRoot || parent.attachShadow({ mode: 'open' });

if (supportsAdoptedStylesheets) {
// create an empty stylesheet and add it to the shadowRoot
const currentWindow = shadow.ownerDocument.defaultView || globalThis;
styleSheet.current = new currentWindow.CSSStyleSheet();
shadow.adoptedStyleSheets = [styleSheet.current];

// add the CSS immediately to avoid FOUC (one-time)
if (latestCss.current) {
styleSheet.current.replaceSync(latestCss.current);
}
}

queueMicrotask(() => {
// Flush the state immediately to ensure layout measurements in parent component are correct
ReactDOM.flushSync(() => setShadowRoot(shadow));
});

return () => void setShadowRoot(null);
}, [templateRef, latestCss]);

// Synchronize `css` with contents of the existing stylesheet
useLayoutEffect(() => {
if (css && supportsAdoptedStylesheets) {
styleSheet.current?.replaceSync(css);
}
}, [css]);

return shadowRoot;
}

// ----------------------------------------------------------------------------

function useIsFirstRender() {
const [isFirstRender, setIsFirstRender] = React.useState(true);
React.useEffect(() => setIsFirstRender(false), []);
Expand Down

0 comments on commit 3061a51

Please sign in to comment.