Skip to content

Commit

Permalink
feat(Portal): Allow shadow root
Browse files Browse the repository at this point in the history
  • Loading branch information
tchock committed Dec 18, 2024
1 parent 4e2643a commit 28fa1d1
Show file tree
Hide file tree
Showing 15 changed files with 111 additions and 84 deletions.
12 changes: 1 addition & 11 deletions site/docs/components/Box.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ To set text color you use the `textColor` prop and for background color `bgColor
You either can use colors from the Pablo theme (e.g. `brand.main`) or css hex or named color (e.g. `#ffffff` or `tomato`).

```jsx live
<Box p={4} bgColor="brand.main" centerFlex textColor="papayawhip">
<Box p={4} bgColor="brand.main" textColor="papayawhip">
I'm a Tomato
</Box>
```
Expand All @@ -81,16 +81,6 @@ align the left and right edges back to the content outside.
We are planning to make a shortcut component that does this for you automatically as this setup is quite common.
### Using the `centerFlex` prop
You can use the `centerFlex` prop to center the content of the box horizontally and vertically.
```jsx live
<Box centerFlex p={1} size={500} bgColor="blue">
<Box mx={1} size={50} bgColor="red" />
</Box>
```
### Custom CSS
You can use the `css` prop to pass custom CSS code using the `css` tag literal from `emotion`.
Expand Down
10 changes: 5 additions & 5 deletions site/docs/components/FlexGrid.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ a column of 6 columns takes half of the width. You can also set `size="fillup"`

```jsx live
function Component() {
const columnStyleProps = { bgColor: 'brand.main', textColor: 'brand.contrastText', p: 2, centerFlex: true };
const columnStyleProps = { bgColor: 'brand.main', textColor: 'brand.contrastText', p: 2, display: 'flex', justifyContent: 'center', alignItems: 'center' };
return (
<FlexGrid width="100%">
<FlexGrid.Column size={4} {...columnStyleProps}>
Expand All @@ -53,13 +53,13 @@ Like with any `Box` style props, you can use the `size` prop to define the colum

```jsx live
function Component() {
const columnStyleProps = { bgColor: 'brand.main', textColor: 'brand.contrastText', p: 2, centerFlex: true };
const columnStyleProps = { bgColor: 'brand.main', textColor: 'brand.contrastText', p: 2, display: 'flex', justifyContent: 'center', alignItems: 'center' };
return (
<FlexGrid width="100%">
<FlexGrid.Column size={{ _: 12, sm: 6 }} bgColor="brand.main" textColor="brand.contrastText" p={2} centerFlex>
<FlexGrid.Column size={{ _: 12, sm: 6 }} bgColor="brand.main" textColor="brand.contrastText" p={2}>
<Paragraph>Size 12 on mobile, Size 6 on portrait tablet and higher</Paragraph>
</FlexGrid.Column>
<FlexGrid.Column size={{ _: 12, sm: 6 }} bgColor="brand.main" textColor="brand.contrastText" p={2} centerFlex>
<FlexGrid.Column size={{ _: 12, sm: 6 }} bgColor="brand.main" textColor="brand.contrastText" p={2}>
<Paragraph>Size 12 on mobile, Size 6 on portrait tablet and higher</Paragraph>
</FlexGrid.Column>
</FlexGrid>
Expand All @@ -73,7 +73,7 @@ If you create multiple columns that don't fit into one row, they will automatica

```jsx live
function Component() {
const columnStyleProps = { bgColor: 'brand.main', textColor: 'brand.contrastText', p: 2, centerFlex: true };
const columnStyleProps = { bgColor: 'brand.main', textColor: 'brand.contrastText', p: 2, display: 'flex', justifyContent: 'center', alignItems: 'center' };
return (
<FlexGrid width="100%" gap={1}>
<FlexGrid.Column size={8} {...columnStyleProps}>
Expand Down
4 changes: 3 additions & 1 deletion src/FlexGrid/FlexGrid.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ const ColumnItem = ({ height = 100, size, ...props }: Omit<FlexGridColumnProps,
ref={ref}
bgColor="brand.main"
textColor="brand.contrastText"
centerFlex
display="flex"
justifyContent="center"
alignItems="center"
{...props}
>
<Paragraph>Size {sizeText}</Paragraph>
Expand Down
26 changes: 23 additions & 3 deletions src/Portal/Portal.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import {
render,
getByTestId as globalGetByTestId,
getAllByTestId as globalGetAllByTestId,
waitFor,

Check failure on line 5 in src/Portal/Portal.spec.tsx

View workflow job for this annotation

GitHub Actions / Test

'waitFor' is defined but never used
} from '@testing-library/react';
import React from 'react';
import { PabloThemeProvider } from '../theme';
import { PabloThemeProvider, rootContext } from '../theme';

Check failure on line 8 in src/Portal/Portal.spec.tsx

View workflow job for this annotation

GitHub Actions / Test

'rootContext' is defined but never used
import { Portal } from './Portal';

test('Render Portal component', () => {
Expand All @@ -27,9 +28,28 @@ test('Forward ref function', () => {
expect(ref.mock.calls[0][0].getAttribute('data-testid')).toBe('pbl-portal-mountpoint');
});

function renderComponent(props) {
test('Forward ref object with ShadowRoot', async () => {
const ref: any = {};
const shadowRoot = document.createElement('div').attachShadow({ mode: 'open' });
renderComponent({ ref }, shadowRoot);
expect(ref.current).toBeDefined();
expect(ref.current.getAttribute('data-testid')).toBe('pbl-portal-mountpoint');
expect(ref.current.parentElement).toBe(shadowRoot);
});

test('Forward ref function with ShadowRoot', () => {
const ref = jest.fn();
const shadowRoot = document.createElement('div').attachShadow({ mode: 'open' });
renderComponent({ ref }, shadowRoot);
expect(ref).toHaveBeenCalledTimes(1);
expect(ref.mock.calls[0][0].getAttribute('data-testid')).toBe('pbl-portal-mountpoint');
});

function renderComponent(props: object, rootElement: ShadowRoot | Document = document) {
console.log('document', rootElement);

const { baseElement, unmount } = render(
<PabloThemeProvider>
<PabloThemeProvider root={rootElement}>
<Portal name="portal" {...props}>
<div data-testid="outside-elem">Outside content</div>
</Portal>
Expand Down
19 changes: 14 additions & 5 deletions src/Portal/Portal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { forwardRef, ReactNode, useEffect, useState } from 'react';
import { forwardRef, ReactNode, useContext, useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import { setRef } from '../utils/setRef';
import { rootContext } from '../theme';

export interface PortalProps {
children: ReactNode;
Expand All @@ -9,18 +10,26 @@ export interface PortalProps {

export const Portal = forwardRef<unknown, PortalProps>(({ children, name }, ref) => {
const [mountPoint, setMountPoint] = useState<Element | null>(null);
const rootElement = useContext(rootContext);
useEffect(() => setMountPoint(document.createElement('div')), []);
useEffect(() => {
if (mountPoint) {
setRef(ref, mountPoint);
mountPoint.setAttribute('data-testid', `pbl-${name}-mountpoint`);
document.body.appendChild(mountPoint);
const mountPointParent = rootElement instanceof ShadowRoot ? rootElement : rootElement.body;

console.log('Appending mountPoint to:', mountPointParent);
mountPointParent.appendChild(mountPoint);
setRef(ref, mountPoint);
setTimeout(() => {
console.log('mountPointParent', mountPointParent, mountPoint.parentElement);
});
return () => {
document.body.removeChild(mountPoint);
console.log('Removing mountPoint from:', mountPointParent);
mountPointParent.removeChild(mountPoint);
};
}
return () => {};
}, [name, mountPoint, ref]);
}, [name, mountPoint, ref, rootElement]);

if (!mountPoint) {
return null;
Expand Down
8 changes: 4 additions & 4 deletions src/animation/FadeAnimation.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { css } from '@emotion/react';
import { createInOutAnimation } from './InOutAnimation';

const fadeAnimationBase = () => css`
const fadeAnimationExited = () => css`
opacity: 0;
`;

const fadeAnimationEnter = css`
const fadeAnimationEntered = css`
opacity: 1;
`;

export const FadeAnimation = createInOutAnimation({
baseStyles: fadeAnimationBase,
enterStyles: fadeAnimationEnter,
exitedStyles: fadeAnimationExited,
enteredStyles: fadeAnimationEntered,
});
34 changes: 24 additions & 10 deletions src/animation/InOutAnimation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ export interface AnimationSetupProps {

export interface AnimationStyleSetup<T> {
baseStyles?: Interpolation<T>;
enterStyles?: Interpolation<T>;
exitStyles?: Interpolation<T>;
exitedStyles?: Interpolation<T>;
exitingStyles?: Interpolation<T>;
enteredStyles?: Interpolation<T>;
enteringStyles?: Interpolation<T>;
preEnterStyles?: Interpolation<T>;
}

export interface AnimationAdditionalProps {
Expand All @@ -43,13 +46,18 @@ export type InOutAnimationProps<T extends object = object> = AnimationAdditional
export const InnerInOutAnimation = styled.div<AnimationStyleProps<any>>`
transition: all ${(props) => props.duration}ms ${(props) => props.easing};
${(props) => props.baseStyles}
${({ status, enterStyles, exitStyles }) => {
${({ status, preEnterStyles, enteredStyles, exitedStyles, exitingStyles, enteringStyles }) => {
switch (status) {
case 'entered':
case 'preEnter':
return preEnterStyles;
case 'entering':
return enterStyles;
return enteringStyles || enteredStyles;
case 'entered':
return enteredStyles;
case 'exiting':
return exitingStyles || exitedStyles;
case 'exited':
return exitStyles;
return exitedStyles;
default:
return null;
}
Expand Down Expand Up @@ -97,16 +105,22 @@ const InOutAnimation = forwardRef<HTMLDivElement, InOutAnimationProps<any>>(

export function createInOutAnimation<P extends object>({
baseStyles,
enterStyles,
exitStyles,
preEnterStyles,
enteredStyles,
enteringStyles,
exitedStyles,
exitingStyles,
}: AnimationStyleSetup<AnimationStyleProps<P>>) {
return forwardRef<HTMLDivElement, InOutAnimationProps<P>>((props, ref) => {
return (
<InOutAnimation
ref={ref}
baseStyles={baseStyles}
enterStyles={enterStyles}
exitStyles={exitStyles}
preEnterStyles={preEnterStyles}
enteringStyles={enteringStyles}
enteredStyles={enteredStyles}
exitedStyles={exitedStyles}
exitingStyles={exitingStyles}
{...props}
/>
);
Expand Down
6 changes: 4 additions & 2 deletions src/animation/NoAnimation.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { css } from '@emotion/react';
import { createInOutAnimation } from './InOutAnimation';

const noAnimationExit = css`
const style = css`
visibility: hidden;
transition: none;
`;

export const NoAnimation = createInOutAnimation({
exitStyles: noAnimationExit,
exitedStyles: style,
exitingStyles: style,
});
18 changes: 6 additions & 12 deletions src/animation/ScaleAnimation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,22 @@ const transformInterpolateFn = (shrink: boolean, props: AnimationStyleProps) =>
return props.visible ? `scale(1)` : `scale(${1 + growMultiplier * 0.25})`;
};

const animationBase = () => {
return css``;
};

const animationEnter = css`
const animationEntered = css`
opacity: 1;
transform: scale(1);
`;

const animationExit = (shrink: boolean) => (props: AnimationStyleProps) => css`
const animationExited = (shrink: boolean) => (props: AnimationStyleProps) => css`
opacity: 0;
transform: ${transformInterpolateFn(shrink, props)};
`;

export const DropInAnimation = createInOutAnimation({
baseStyles: animationBase,
enterStyles: animationEnter,
exitStyles: animationExit(true),
enteredStyles: animationEntered,
exitedStyles: animationExited(true),
});

export const PopOutAnimation = createInOutAnimation({
baseStyles: animationBase,
enterStyles: animationEnter,
exitStyles: animationExit(false),
enteredStyles: animationEntered,
exitedStyles: animationExited(false),
});
8 changes: 4 additions & 4 deletions src/animation/SlideAnimation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const leftStyles = (props: SlideStyleProps) => css`
transform: ${transformInterpolateFn('X', -1, props)};
`;

const slideAnimationBase: any = (props: SlideStyleProps) => css`
const slideAnimationExited: any = (props: SlideStyleProps) => css`
opacity: 0;
${conditionalStyles<SlideStyleProps>('side', {
top: topStyles,
Expand All @@ -49,12 +49,12 @@ const slideAnimationBase: any = (props: SlideStyleProps) => css`
})(props)}
`;

const slideAnimationEnter = css`
const slideAnimationEntered = css`
opacity: 1;
transform: translateY(0) translateX(0);
`;

export const SlideAnimation = createInOutAnimation<SlideAnimationProps>({
baseStyles: slideAnimationBase,
enterStyles: slideAnimationEnter,
exitedStyles: slideAnimationExited,
enteredStyles: slideAnimationEntered,
});
25 changes: 13 additions & 12 deletions src/animation/StackAnimation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,27 @@ import { AnimationStyleProps, createInOutAnimation } from './InOutAnimation';

const getMarginTop = (props: AnimationStyleProps) => (props.selfHeight ? -props.selfHeight : 0);

const stackAnimationBase = (props: AnimationStyleProps) => css`
margin-top: ${getMarginTop(props)}px;
const stackAnimationPreEnter = () => css`
opacity: 0;
margin-top: 0;
transform: translateY(-50%);
transition: none;
`;

const stackAnimationExited = (props: AnimationStyleProps) => css`
opacity: 0;
margin-top: ${getMarginTop(props)}px;
transform: translateY(50%);
`;

const stackAnimationEnter = css`
const stackAnimationEntered = css`
opacity: 1;
margin-top: 0;
transform: translateY(0);
`;

const stackAnimationExit = (props: AnimationStyleProps) => css`
opacity: 0;
margin-top: ${getMarginTop(props)}px;
transform: translateY(-50%);
`;

export const StackAnimation = createInOutAnimation({
baseStyles: stackAnimationBase,
enterStyles: stackAnimationEnter,
exitStyles: stackAnimationExit,
exitedStyles: stackAnimationExited,
preEnterStyles: stackAnimationPreEnter,
enteredStyles: stackAnimationEntered,
});
16 changes: 4 additions & 12 deletions src/animation/animation.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const BaseStory = ({ component: Component, ...args }) => {
`}
alignItems="stretch"
>
<Box centerFlex flexGrow={1} flexBasis={0}>
<Flex center flexGrow={1} flexBasis={0}>
<Box mb={4}>
<Checkbox
mb={1}
Expand All @@ -56,24 +56,16 @@ const BaseStory = ({ component: Component, ...args }) => {
<option>linear</option>
</NativeSelect>
</Box>
</Box>
<Box
display="flex"
justifyContent="center"
alignItems="center"
flexGrow={1}
flexShrink={0}
flexBasis={0}
bgColor="gray.50"
>
</Flex>
<Flex center flexGrow={1} flexShrink={0} flexBasis={0} bgColor="gray.50">
<Box maxWidth={400} height={200} p={3}>
<Component duration={duration} visible={visible} easing={easing} {...args}>
<Card>
<Title>I am animated</Title>
</Card>
</Component>
</Box>
</Box>
</Flex>
</Flex>
);
};
Expand Down
Loading

0 comments on commit 28fa1d1

Please sign in to comment.