Skip to content

Commit

Permalink
feat: Add MotionProvider to disable motion globally (#41)
Browse files Browse the repository at this point in the history
* feat: add root closer

* test: add test case

* test: fix test case
  • Loading branch information
zombieJ authored Apr 18, 2023
1 parent a90f1f4 commit 75bffd8
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 27 deletions.
16 changes: 11 additions & 5 deletions src/CSSMotion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { STATUS_NONE, STEP_PREPARE, STEP_START } from './interface';
import useStatus from './hooks/useStatus';
import DomWrapper from './DomWrapper';
import { isActive } from './hooks/useStepQueue';
import { Context } from './context';

export type CSSMotionConfig =
| boolean
Expand Down Expand Up @@ -112,8 +113,8 @@ export function genCSSMotion(
({ transitionSupport } = config);
}

function isSupportTransition(props: CSSMotionProps) {
return !!(props.motionName && transitionSupport);
function isSupportTransition(props: CSSMotionProps, contextMotion?: boolean) {
return !!(props.motionName && transitionSupport && contextMotion !== false);
}

const CSSMotion = React.forwardRef<any, CSSMotionProps>((props, ref) => {
Expand All @@ -129,7 +130,9 @@ export function genCSSMotion(
eventProps,
} = props;

const supportMotion = isSupportTransition(props);
const { motion: contextMotion } = React.useContext(Context);

const supportMotion = isSupportTransition(props, contextMotion);

// Ref to the react node, it may be a HTMLElement
const nodeRef = useRef<any>();
Expand Down Expand Up @@ -181,7 +184,10 @@ export function genCSSMotion(
if (!children) {
// No children
motionChildren = null;
} else if (status === STATUS_NONE || !isSupportTransition(props)) {
} else if (
status === STATUS_NONE ||
!isSupportTransition(props, contextMotion)
) {
// Stable children
if (mergedVisible) {
motionChildren = children({ ...mergedProps }, setNodeRef);
Expand All @@ -190,7 +196,7 @@ export function genCSSMotion(
{ ...mergedProps, className: leavedClassName },
setNodeRef,
);
} else if ( forceRender || (!removeOnLeave && !leavedClassName)) {
} else if (forceRender || (!removeOnLeave && !leavedClassName)) {
motionChildren = children(
{ ...mergedProps, style: { display: 'none' } },
setNodeRef,
Expand Down
14 changes: 14 additions & 0 deletions src/context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as React from 'react';

interface MotionContextProps {
motion?: boolean;
}

export const Context = React.createContext<MotionContextProps>({});

export default function MotionProvider({
children,
...props
}: MotionContextProps & { children?: React.ReactNode }) {
return <Context.Provider value={props}>{children}</Context.Provider>;
}
1 change: 1 addition & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import CSSMotionList from './CSSMotionList';
import type { CSSMotionProps } from './CSSMotion';
import type { CSSMotionListProps } from './CSSMotionList';
import type { MotionEventHandler, MotionEndEventHandler } from './interface';
export { default as Provider } from './context';

export { CSSMotionList };

Expand Down
79 changes: 57 additions & 22 deletions tests/CSSMotion.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import React from 'react';
import { act } from 'react-dom/test-utils';
import classNames from 'classnames';
import { render, fireEvent } from '@testing-library/react';
import type { CSSMotionProps } from '../src/CSSMotion';
import type { CSSMotionProps } from '../src';
import { Provider } from '../src';
import RefCSSMotion, { genCSSMotion } from '../src/CSSMotion';
import ReactDOM from 'react-dom';

Expand Down Expand Up @@ -160,27 +161,27 @@ describe('CSSMotion', () => {
);

it('leaveClassName should add to dom', () => {
const genMotion = (props) => {
const {visible,leavedClassName} = props
const genMotion = props => {
const { visible, leavedClassName } = props;
return (
<CSSMotion
motionName="transition"
visible={visible}
removeOnLeave={false}
leavedClassName={leavedClassName}
>
{({ style, className }) => {
return (
<div
style={style}
className={classNames('motion-box', className)}
/>
);
}}
</CSSMotion>
<CSSMotion
motionName="transition"
visible={visible}
removeOnLeave={false}
leavedClassName={leavedClassName}
>
{({ style, className }) => {
return (
<div
style={style}
className={classNames('motion-box', className)}
/>
);
}}
</CSSMotion>
);
};
const { container, rerender } = render(genMotion({visible:false}));
const { container, rerender } = render(genMotion({ visible: false }));

rerender(genMotion({ visible: true }));

Expand All @@ -189,7 +190,7 @@ describe('CSSMotion', () => {
});

expect(container.querySelector('.motion-box')).toBeTruthy();
rerender(genMotion({ visible: false,leavedClassName:'removed'}));
rerender(genMotion({ visible: false, leavedClassName: 'removed' }));
act(() => {
jest.runAllTimers();
});
Expand All @@ -208,8 +209,9 @@ describe('CSSMotion', () => {
});

fireEvent.transitionEnd(container.querySelector('.motion-box'));
expect(container.querySelector('.motion-box')?.classList.contains('removed')).toBeFalsy();

expect(
container.querySelector('.motion-box')?.classList.contains('removed'),
).toBeFalsy();
});

it('stop transition if config motion to false', () => {
Expand Down Expand Up @@ -479,6 +481,39 @@ describe('CSSMotion', () => {
});
});

it('MotionProvider to disable motion', () => {
const Demo = ({
motion,
visible,
}: {
motion?: boolean;
visible?: boolean;
}) => (
<Provider motion={motion}>
<CSSMotion
motionName="test"
visible={visible}
removeOnLeave={false}
leavedClassName="hidden"
>
{({ style, className }) => (
<div
style={style}
className={classNames('motion-box', className)}
/>
)}
</CSSMotion>
</Provider>
);

const { container, rerender } = render(<Demo motion={false} visible />);
expect(container.querySelector('.motion-box')).toBeTruthy();

// hide immediately since motion is disabled
rerender(<Demo motion={false} visible={false} />);
expect(container.querySelector('.hidden')).toBeTruthy();
});

it('no transition', () => {
const NoCSSTransition = genCSSMotion({
transitionSupport: false,
Expand Down

1 comment on commit 75bffd8

@vercel
Copy link

@vercel vercel bot commented on 75bffd8 Apr 18, 2023

Choose a reason for hiding this comment

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

Please sign in to comment.