Skip to content

Commit

Permalink
Merge pull request #5468 from matuzalemsteles/issue-5421
Browse files Browse the repository at this point in the history
feat: add motion reduction config
  • Loading branch information
matuzalemsteles committed Apr 13, 2023
2 parents 7c265c3 + 7e266bb commit eba91e6
Show file tree
Hide file tree
Showing 11 changed files with 133 additions and 93 deletions.
84 changes: 0 additions & 84 deletions clayui.com/content/docs/css/utilities/c-prefers-reduced-motion.md

This file was deleted.

2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ module.exports = {
statements: 95,
},
'./packages/clay-panel/src/': {
branches: 93,
branches: 88,
functions: 100,
lines: 100,
statements: 100,
Expand Down
41 changes: 41 additions & 0 deletions packages/clay-core/docs/reduced-motion.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
title: 'Reduced Motion'
description: 'Provider component which aggregates the main Clay, Icon, and Modal.'
packageNpm: '@clayui/core'
---

<div class="nav-toc-absolute">
<div class="nav-toc">

- [Introduction](#introduction)
- [JS](#js)
- [CSS](#css)

</div>
</div>

## Introduction

Setting the motion reduction of animations can be done in two different ways in Clay, transitions that are done purely in CSS can use the `c-prefers-reduced-motion` class and transitions that are made with JS are defined using Clay's `Provider` component.

## JS

To remove transition from components in React, you need to declare the [`Provider`](/docs/components/provider.html) component at the root of your application and set the `reducedMotion` property to whichever option you want.

- `user`: Respect user's device setting.
- `always`: Enforce reduced motion.
- `never`: Don't reduce motion.

```jsx
import {Provider} from '@clayui/core';

<Provider spritemap={spritemap} reducedMotion="user">
<App />
</Provider>;
```

## CSS

The class `c-prefers-reduced-motion` removes transitions from child elements. It allows the site creator to remove any CSS transition in a specific section or on the whole page without having to depend on the operating system's non-essential motion setting.

It also removes `scroll-behavior: smooth` if it is enabled. You can place `c-prefers-reduced-motion` on whatever element has the CSS property `scroll-behavior: smooth` to remove it. If this class is placed on the `html` element, it will remove all CSS transitions for the page.
5 changes: 4 additions & 1 deletion packages/clay-core/src/tree-view/TreeViewGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/

import {useProvider} from '@clayui/provider';
import {setElementFullHeight} from '@clayui/shared';
import classNames from 'classnames';
import React from 'react';
Expand Down Expand Up @@ -39,6 +40,8 @@ export function TreeViewGroup<T extends Record<any, any>>({
}: ITreeViewGroupProps<T>) {
const {expandedKeys} = useTreeViewContext();

const {prefersReducedMotion} = useProvider();

const item = useItem();

return (
Expand Down Expand Up @@ -71,7 +74,7 @@ export function TreeViewGroup<T extends Record<any, any>>({
onExiting={(element) =>
element.setAttribute('style', 'height: 0px')
}
timeout={250}
timeout={prefersReducedMotion ? 0 : 250}
unmountOnExit
>
<div>
Expand Down
7 changes: 6 additions & 1 deletion packages/clay-core/src/vertical-bar/Panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/

import {useProvider} from '@clayui/provider';
import classNames from 'classnames';
import React, {useContext, useEffect, useRef} from 'react';
import {CSSTransition} from 'react-transition-group';
Expand Down Expand Up @@ -58,6 +59,8 @@ export function Panel({children, keyValue = null, tabIndex}: Props) {

const previousActivePanelRef = useRef<React.Key | null>(null);

const {prefersReducedMotion} = useProvider();

const isFirst = useIsFirstRender();

const isPanelOpen = () => {
Expand Down Expand Up @@ -102,7 +105,9 @@ export function Panel({children, keyValue = null, tabIndex}: Props) {
}}
role="tabpanel"
tabIndex={tabIndex}
timeout={panelNext ? 0 : {enter: 300, exit: 200}}
timeout={
panelNext || prefersReducedMotion ? 0 : {enter: 300, exit: 200}
}
unmountOnExit
>
<div
Expand Down
1 change: 1 addition & 0 deletions packages/clay-navigation-bar/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@clayui/icon": "^3.56.0",
"@clayui/layout": "^3.65.1",
"@clayui/shared": "^3.92.0",
"@clayui/provider": "^3.77.0",
"classnames": "^2.2.6",
"react-transition-group": "^4.4.1",
"warning": "^4.0.3"
Expand Down
9 changes: 6 additions & 3 deletions packages/clay-navigation-bar/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
import ClayButton from '@clayui/button';
import ClayIcon from '@clayui/icon';
import ClayLayout from '@clayui/layout';
import {useProvider} from '@clayui/provider';
import {setElementFullHeight} from '@clayui/shared';
import classNames from 'classnames';
import React from 'react';
import React, {useState} from 'react';
import {CSSTransition} from 'react-transition-group';
import warning from 'warning';

Expand Down Expand Up @@ -59,7 +60,9 @@ function ClayNavigationBar({
triggerLabel,
...otherProps
}: IProps) {
const [expanded, setExpanded] = React.useState(false);
const [expanded, setExpanded] = useState(false);

const {prefersReducedMotion} = useProvider();

const activeElementsCount = React.Children.map(
children,
Expand Down Expand Up @@ -133,7 +136,7 @@ function ClayNavigationBar({
onExiting={(element) =>
element.setAttribute('style', `height: 0px`)
}
timeout={250}
timeout={prefersReducedMotion ? 0 : 250}
>
<div>
<ClayLayout.ContainerFluid>
Expand Down
1 change: 1 addition & 0 deletions packages/clay-panel/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@clayui/button": "^3.92.0",
"@clayui/icon": "^3.56.0",
"@clayui/shared": "^3.92.0",
"@clayui/provider": "^3.77.0",
"classnames": "^2.2.6",
"react-transition-group": "^4.4.1"
},
Expand Down
5 changes: 4 additions & 1 deletion packages/clay-panel/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import ClayButton from '@clayui/button';
import ClayIcon from '@clayui/icon';
import {useProvider} from '@clayui/provider';
import {
InternalDispatch,
setElementFullHeight,
Expand Down Expand Up @@ -98,6 +99,8 @@ function ClayPanel({
value: expanded,
});

const {prefersReducedMotion} = useProvider();

return (
<div
{...otherProps}
Expand Down Expand Up @@ -192,7 +195,7 @@ function ClayPanel({
element.style.height = '';
}}
role="tabpanel"
timeout={250}
timeout={!prefersReducedMotion ? 250 : 0}
>
<div>{children}</div>
</CSSTransition>
Expand Down
23 changes: 21 additions & 2 deletions packages/clay-provider/src/Provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,20 @@ import {ClayIconSpriteContext} from '@clayui/icon';
import React, {useContext, useMemo} from 'react';

import {DataClient} from './DataClient';
import {useReducedMotion} from './useReducedMotion';

interface IProviderProps extends Omit<IProviderContext, 'client'> {
interface IProviderProps
extends Omit<IProviderContext, 'client' | 'prefersReducedMotion'> {
/**
* The content of the Provider.
*/
children: React.ReactNode;

/**
* Defines the transition for the entire tree.
*/
reducedMotion?: 'user' | 'always' | 'never';

/**
* Path to the location of the spritemap resource.
*/
Expand All @@ -30,6 +37,8 @@ interface IProviderProps extends Omit<IProviderContext, 'client'> {
interface IProviderContext {
client: DataClient;

prefersReducedMotion?: boolean;

/**
* The theme corresponds to a CSS class to scope the application.
*/
Expand All @@ -42,6 +51,7 @@ Context.displayName = 'ClayProviderContext';

export const Provider = ({
children,
reducedMotion = 'user',
spritemap,
storageMaxSize = 20,
theme,
Expand All @@ -54,8 +64,17 @@ export const Provider = ({
[storageMaxSize]
);

const isReducedMotion = useReducedMotion(reducedMotion);

return (
<Context.Provider value={{client, theme, ...otherProps}}>
<Context.Provider
value={{
client,
prefersReducedMotion: isReducedMotion,
theme,
...otherProps,
}}
>
<ClayIconSpriteContext.Provider value={spritemap}>
{theme ? <div className={theme}>{children}</div> : children}
</ClayIconSpriteContext.Provider>
Expand Down
48 changes: 48 additions & 0 deletions packages/clay-provider/src/useReducedMotion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* SPDX-FileCopyrightText: © 2021 Liferay, Inc. <https://liferay.com>
* SPDX-License-Identifier: BSD-3-Clause
*/

import {useEffect, useState} from 'react';

export function useReducedMotion(mode: 'user' | 'always' | 'never') {
const [reduceMotion, setReduceMotion] = useState(false);

useEffect(() => {
switch (mode) {
case 'always':
setReduceMotion(true);
break;
case 'never':
break;
default: {
if (window.matchMedia) {
const motionMediaQuery = window.matchMedia(
'(prefers-reduced-motion)'
);

const setReducedMotionPreferences = (
mediaQuery: MediaQueryListEvent
) => setReduceMotion(mediaQuery.matches);

motionMediaQuery.addEventListener(
'change',
setReducedMotionPreferences
);

return () => {
motionMediaQuery.removeEventListener(
'change',
setReducedMotionPreferences
);
};
} else {
setReduceMotion(false);
}
break;
}
}
}, []);

return reduceMotion;
}

0 comments on commit eba91e6

Please sign in to comment.