Skip to content

Commit

Permalink
fix(react-drawer): add correct animation direction when RTL (#32813)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcosmoura authored Sep 13, 2024
1 parent 680bbaa commit b5f5ef0
Show file tree
Hide file tree
Showing 10 changed files with 113 additions and 102 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fix: add correct direction of animations when RTL",
"packageName": "@fluentui/react-drawer",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type { DialogSurfaceProps } from '@fluentui/react-dialog';
import type { DialogSurfaceSlots } from '@fluentui/react-dialog';
import type { ForwardRefComponent } from '@fluentui/react-utilities';
import type { PresenceMotionSlotProps } from '@fluentui/react-motion';
import { ProviderContextValue_unstable } from '@fluentui/react-shared-contexts';
import * as React_2 from 'react';
import type { Slot } from '@fluentui/react-utilities';
import type { SlotClassNames } from '@fluentui/react-utilities';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import { presenceMotionSlot } from '@fluentui/react-motion';
import { getIntrinsicElementProps, slot } from '@fluentui/react-utilities';
import * as React from 'react';
import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';

import { type DrawerMotionParams, InlineDrawerMotion } from '../../shared/drawerMotions';
import { useDrawerDefaultProps } from '../../shared/useDrawerDefaultProps';
Expand All @@ -25,6 +26,7 @@ const STATIC_MOTION = {
export const useInlineDrawer_unstable = (props: InlineDrawerProps, ref: React.Ref<HTMLElement>): InlineDrawerState => {
const { size, position, open } = useDrawerDefaultProps(props);
const { separator = false, surfaceMotion } = props;
const { dir } = useFluent();

const state: InlineDrawerState = {
components: {
Expand Down Expand Up @@ -52,6 +54,7 @@ export const useInlineDrawer_unstable = (props: InlineDrawerProps, ref: React.Re
defaultProps: {
position,
size,
dir,
visible: open,
unmountOnExit: true,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import { Dialog } from '@fluentui/react-dialog';
import { slot } from '@fluentui/react-utilities';
import * as React from 'react';
import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';

import { OverlayDrawerMotion, OverlaySurfaceBackdropMotion } from '../../shared/drawerMotions';
import { useDrawerDefaultProps } from '../../shared/useDrawerDefaultProps';
Expand Down Expand Up @@ -30,6 +31,7 @@ export const useOverlayDrawer_unstable = (
): OverlayDrawerState => {
const { open, size, position } = useDrawerDefaultProps(props);
const { backdropMotion, modalType = 'modal', inertTrapFocus, onOpenChange, surfaceMotion, mountNode } = props;
const { dir } = useFluent();

const backdropProps = slot.resolveShorthand(props.backdrop);
const hasCustomBackdrop = modalType !== 'non-modal' && backdropProps !== null;
Expand All @@ -56,7 +58,7 @@ export const useOverlayDrawer_unstable = (
onOpenChange,
inertTrapFocus,
modalType,
surfaceMotion: mergePresenceSlots(surfaceMotion, OverlayDrawerMotion, { position, size }),
surfaceMotion: mergePresenceSlots(surfaceMotion, OverlayDrawerMotion, { position, size, dir }),
/**
* children is not needed here because we construct the children in the render function,
* but it's required by DialogProps
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { DrawerMotionParams, InlineDrawerMotion } from './drawerMotions';
import { mergePresenceSlots } from './drawerMotionUtils';

const TestComponent = InlineDrawerMotion;
const testProps: DrawerMotionParams = { position: 'start', size: 'medium' };
const testProps: DrawerMotionParams = { position: 'start', size: 'medium', dir: 'ltr' };

describe('mergePresenceSlots', () => {
it('should return null if inputSlot is null', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { getPositionTransform } from './drawerMotions';

describe('getPositionTransform', () => {
it('should return the correct transform', () => {
expect(getPositionTransform('start', 'medium', 'ltr')).toBe('translate3d(var(medium), 0, 0)');
expect(getPositionTransform('start', 'medium', 'rtl')).toBe('translate3d(calc(var(medium) * -1), 0, 0)');
expect(getPositionTransform('end', 'medium', 'ltr')).toBe('translate3d(calc(var(medium) * -1), 0, 0)');
expect(getPositionTransform('end', 'medium', 'rtl')).toBe('translate3d(var(medium), 0, 0)');
expect(getPositionTransform('bottom', 'medium', 'ltr')).toBe('translate3d(0, var(medium), 0)');
expect(getPositionTransform('bottom', 'medium', 'rtl')).toBe('translate3d(0, var(medium), 0)');

// In case of undefined or unknown position
expect(getPositionTransform('top' as unknown as undefined, 'medium', 'ltr')).toBe('translate3d(0, 0, 0)');
});
});
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { createPresenceComponent, motionTokens } from '@fluentui/react-motion';
import { tokens } from '@fluentui/react-theme';
import { ProviderContextValue_unstable as FluentProviderContextValue } from '@fluentui/react-shared-contexts';

import type { DrawerBaseProps } from './DrawerBase.types';
import { drawerCSSVars } from './useDrawerBaseStyles.styles';

export type DrawerMotionParams = Required<Pick<DrawerBaseProps, 'size' | 'position'>>;
export type DrawerMotionParams = Required<
Pick<DrawerBaseProps, 'size' | 'position'> & Pick<FluentProviderContextValue, 'dir'>
>;
export type OverlayDrawerSurfaceMotionParams = Required<Pick<DrawerBaseProps, 'size'>>;

const durations: Record<NonNullable<DrawerBaseProps['size']>, number> = {
Expand All @@ -17,19 +20,41 @@ const durations: Record<NonNullable<DrawerBaseProps['size']>, number> = {
/**
* @internal
*/
export const InlineDrawerMotion = createPresenceComponent<DrawerMotionParams>(({ position, size }) => {
export function getPositionTransform(
position: DrawerBaseProps['position'],
sizeVar: string,
dir: FluentProviderContextValue['dir'],
) {
const leftToRightTransform = `translate3d(var(${sizeVar}), 0, 0)`;
const rightToLeftTransform = `translate3d(calc(var(${sizeVar}) * -1), 0, 0)`;
const bottomToTopTransform = `translate3d(0, var(${sizeVar}), 0)`;

if (position === 'start') {
return dir === 'rtl' ? rightToLeftTransform : leftToRightTransform;
}

if (position === 'end') {
return dir === 'rtl' ? leftToRightTransform : rightToLeftTransform;
}

if (position === 'bottom') {
return bottomToTopTransform;
}

return 'translate3d(0, 0, 0)';
}

/**
* @internal
*/
export const InlineDrawerMotion = createPresenceComponent<DrawerMotionParams>(({ position, size, dir }) => {
const keyframes: Keyframe[] = [
{
...(position === 'start' && {
transform: `translate3d(calc(var(${drawerCSSVars.drawerSizeVar}) * -1), 0, 0)`,
}),
...(position === 'end' && {
transform: `translate3d(var(${drawerCSSVars.drawerSizeVar}), 0, 0)`,
}),
...(position === 'bottom' && {
transform: `translate3d(0, var(${drawerCSSVars.drawerSizeVar}), 0)`,
}),

/**
* TODO: Once the #31663 lands, we should update the RTL logic to use Motion APIs
* The work will be done in the #32817
*/
transform: getPositionTransform(position, drawerCSSVars.drawerSizeVar, dir),
opacity: 0,
},
{ transform: 'translate3d(0, 0, 0)', opacity: 1 },
Expand All @@ -53,19 +78,14 @@ export const InlineDrawerMotion = createPresenceComponent<DrawerMotionParams>(({
/**
* @internal
*/
export const OverlayDrawerMotion = createPresenceComponent<DrawerMotionParams>(({ position, size }) => {
export const OverlayDrawerMotion = createPresenceComponent<DrawerMotionParams>(({ position, size, dir }) => {
const keyframes: Keyframe[] = [
{
...(position === 'start' && {
transform: `translate3D(calc(var(${drawerCSSVars.drawerSizeVar}) * -1), 0, 0)`,
}),
...(position === 'end' && {
transform: `translate3d(calc(var(${drawerCSSVars.drawerSizeVar}) * 1), 0, 0)`,
}),
...(position === 'bottom' && {
transform: `translate3d(0, calc(var(${drawerCSSVars.drawerSizeVar}) * 1), 0)`,
}),

/**
* TODO: Once the #31663 lands, we should update the RTL logic to use Motion APIs
* The work will be done in the #32817
*/
transform: getPositionTransform(position, drawerCSSVars.drawerSizeVar, dir),
boxShadow: `0px ${tokens.colorTransparentBackground}`,
opacity: 0,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,34 +20,20 @@ const useStyles = makeStyles({
},
});

const setTitle = (position: DrawerProps['position']) => {
switch (position) {
case 'start':
return 'Left';

case 'end':
return 'Right';

case 'bottom':
return 'Bottom';

default:
return undefined;
}
};
const capitalize = (str: string) => str.charAt(0).toUpperCase() + str.slice(1);

export const Position = () => {
const styles = useStyles();

const [isOpen, setIsOpen] = React.useState(false);
const [position, setPosition] = React.useState<DrawerProps['position']>('start');
const [position, setPosition] = React.useState<Required<DrawerProps>['position']>('start');

const onClickLeftButton = React.useCallback(() => {
const onClickStartButton = React.useCallback(() => {
setPosition('start');
setIsOpen(true);
}, []);

const onClickRightButton = React.useCallback(() => {
const onClickEndButton = React.useCallback(() => {
setPosition('end');
setIsOpen(true);
}, []);
Expand All @@ -71,7 +57,7 @@ export const Position = () => {
/>
}
>
{setTitle(position)} Drawer
{capitalize(position)} Drawer
</DrawerHeaderTitle>
</DrawerHeader>

Expand All @@ -81,12 +67,12 @@ export const Position = () => {
</OverlayDrawer>

<div className={styles.content}>
<Button appearance="primary" onClick={onClickLeftButton}>
Open left
<Button appearance="primary" onClick={onClickStartButton}>
Open start
</Button>

<Button appearance="primary" onClick={onClickRightButton}>
Open right
<Button appearance="primary" onClick={onClickEndButton}>
Open end
</Button>

<Button appearance="primary" onClick={onClickBottomButton}>
Expand All @@ -101,7 +87,7 @@ Position.parameters = {
docs: {
description: {
story: [
'When a Drawer is invoked, it slides in from either the left or right side, or bottom of the screen.',
'When a Drawer is invoked, it slides in from either the start or end side, or bottom of the screen.',
'This can be specified by the `position` prop.',
].join('\n'),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,29 +65,29 @@ const DrawerSeparatorExample: React.FC<DrawerSeparatorExampleProps> = ({ open, s
export const Separator = () => {
const styles = useStyles();

const [leftOpen, setLeftOpen] = React.useState(true);
const [rightOpen, setRightOpen] = React.useState(true);
const [bottomOpen, setBottomOpen] = React.useState(false);
const [startOpen, setStartOpen] = React.useState(true);
const [endOpen, setEndOpen] = React.useState(true);
const [bottomOpen, setBottomOpen] = React.useState(true);

return (
<div className={mergeClasses(styles.root, styles.flexColumn)}>
<div className={styles.root} style={{ borderBottomWidth: 0 }}>
<DrawerSeparatorExample open={leftOpen} setOpen={setLeftOpen} position="start" />
<DrawerSeparatorExample open={startOpen} setOpen={setStartOpen} position="start" />

<div className={styles.content}>
<Button appearance="primary" onClick={() => setLeftOpen(!leftOpen)}>
Toggle left
<Button appearance="primary" onClick={() => setStartOpen(!startOpen)}>
Toggle start
</Button>

<Button appearance="primary" onClick={() => setRightOpen(!rightOpen)}>
Toggle right
<Button appearance="primary" onClick={() => setEndOpen(!endOpen)}>
Toggle end
</Button>

<Button appearance="primary" onClick={() => setBottomOpen(!bottomOpen)}>
Toggle bottom
</Button>
</div>
<DrawerSeparatorExample open={rightOpen} setOpen={setRightOpen} position="end" />
<DrawerSeparatorExample open={endOpen} setOpen={setEndOpen} position="end" />
</div>
<DrawerSeparatorExample open={bottomOpen} setOpen={setBottomOpen} position="bottom" />
</div>
Expand Down
Loading

0 comments on commit b5f5ef0

Please sign in to comment.