Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const ELEMENT_TYPE_SVG = 'svg';

type BaseOptions = {
attributes?: { [key: string]: string };
fallbackMountNode?: React.MutableRefObject<Node | null>;
};

type HtmlOptions = BaseOptions & {
Expand Down Expand Up @@ -144,6 +145,16 @@ const createPortalNode = <C extends Component<any>>(
parent = undefined;
lastPlaceholder = undefined;
}

if (parent === undefined && options?.fallbackMountNode?.current) {
const newParent = options?.fallbackMountNode.current;

newParent.appendChild(
portalNode.element
);

parent = newParent;
Copy link
Member

Choose a reason for hiding this comment

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

I would be nice to somehow do this using mount() instead, just so we have a single implementation for this instead of separating the fallback mount & normal mount cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll take a look at that when working through this on one of the other API options.

Can't remember the exact details - but I vaguely recall trying to keep things slightly distinct, as with this particular setup, I'd went for a container-like mount method to ensure a the hidden div would be wrapped the portal content without needing to directly consider the fallback wrapper. There are definitely other ways to handle that though, so I'll keep it in mind.

}
}
} as AnyPortalNode<C>;

Expand Down
105 changes: 105 additions & 0 deletions stories/html.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -426,3 +426,108 @@ export const ExampleFromREADME = () => {

return <MyComponent componentToShow='component-a' />
};

export const DefaultToFallbackElementWhenNotInUse = () => {
const fallbackRef = React.useRef<HTMLDivElement>(null);
const portalNode = React.useMemo(() => createHtmlPortalNode({ fallbackMountNode: fallbackRef }), []);
const [showPortal, setShowPortal] = React.useState(true);

return <div>
<div>
<InPortal node={portalNode}>
<video src="https://media.giphy.com/media/l0HlKghz8IvrQ8TYY/giphy.mp4" controls loop autoPlay />
</InPortal>

<p>
The video below should continue playing in the background toggled.
</p>

<button onClick={() => setShowPortal(!showPortal)}>
Click to toggle the OutPortal
</button>

<hr/>

<h2>Regular OutPortal</h2>
<Container>
{ showPortal === true && <OutPortal node={portalNode} /> }
</Container>

<h2>Fallback:</h2>
<Container>
<div ref={fallbackRef}></div>
</Container>
</div>
</div>;
};

export const PersistPlaybackWhilstDisplayedInAHiddenElement = () => {
const fallbackRef = React.useRef(null);
const portalNode = React.useMemo(() => createHtmlPortalNode({ fallbackMountNode: fallbackRef }), []);
const [showPortal, setShowPortal] = React.useState(true);

return <div>
<div ref={fallbackRef} style={{display: 'none'}}></div>
<div>
<InPortal node={portalNode}>
<video src="https://media.giphy.com/media/l0HlKghz8IvrQ8TYY/giphy.mp4" controls loop autoPlay />
</InPortal>

<p>
The video below should continue playing in the background toggled.
</p>

<button onClick={() => setShowPortal(!showPortal)}>
Click to toggle the OutPortal
</button>

<hr/>

<div>
{ showPortal === true && <OutPortal node={portalNode} /> }
</div>
</div>
</div>;
};

export const CombineFallbackContainerAndSwitchingBetweenTwoPlaces = () => {
const fallbackRef = React.useRef(null);
const portalNode = React.useMemo(() => createHtmlPortalNode({ fallbackMountNode: fallbackRef }), []);
const [showPortal, setShowPortal] = React.useState(true);
const [secondPortalSelected, setSecondPortalSelected] = React.useState(true);

return <div>
<div ref={fallbackRef} style={{display: 'none'}}></div>
<div>
<InPortal node={portalNode}>
<video src="https://media.giphy.com/media/l0HlKghz8IvrQ8TYY/giphy.mp4" controls loop autoPlay />
</InPortal>

<p>
The video below should continue playing in the background toggled.
</p>

<button onClick={() => setSecondPortalSelected(!secondPortalSelected)}>
Click to switch the OutPortal
</button>

<button onClick={() => setShowPortal(!showPortal)}>
Click to toggle the OutPortals
</button>

<hr/>

{ showPortal === true && <div>
<Container>
<h2>Portal 1:</h2>
{ !secondPortalSelected && <OutPortal node={portalNode} /> }
</Container>
<Container>
<h2>Portal 2:</h2>
{ secondPortalSelected && <OutPortal node={portalNode} /> }
</Container>
</div> }
</div>
</div>;
};