Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Disable animations until the first user interaction #2970

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useRef, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import ReactDOM, { unmountComponentAtNode } from 'react-dom';

import AppLayout from '~components/app-layout';
import Header from '~components/header';
import ScreenreaderOnly from '~components/internal/components/screenreader-only';
import awsuiPlugins from '~components/internal/plugins';
import Link from '~components/link';
import SideNavigation, { SideNavigationProps } from '~components/side-navigation';
import SpaceBetween from '~components/space-between';
Expand All @@ -16,6 +18,37 @@ import labels from './utils/labels';

function createView(name: string) {
return function View() {
const drawerId = `circle-global-${name}`;
useEffect(() => {
awsuiPlugins.appLayout.registerDrawer({
id: drawerId,
type: 'global',
preserveInactiveContent: true,
defaultActive: true,
resizable: true,
defaultSize: 320,

ariaLabels: {
closeButton: 'Close button',
content: 'Content',
triggerButton: 'Trigger button',
resizeHandle: 'Resize handle',
},

trigger: {
iconSvg: `<svg viewBox="0 0 16 16" focusable="false">
<circle stroke-width="2" stroke="currentColor" fill="none" cx="8" cy="8" r="7" />
<circle stroke-width="2" stroke="currentColor" fill="none" cx="8" cy="8" r="3" />
</svg>`,
},

mountContent: container => {
ReactDOM.render(<div>global widget content circle {name}</div>, container);
},
unmountContent: container => unmountComponentAtNode(container),
});
}, [drawerId]);

return (
<AppLayout
{...{ __disableRuntimeDrawers: true }}
Expand All @@ -34,6 +67,9 @@ function createView(name: string) {
</Link>

<div>Page content: {name}</div>

<button onClick={() => awsuiPlugins.appLayout.openDrawer(drawerId)}>open drawer</button>
<button onClick={() => awsuiPlugins.appLayout.closeDrawer(drawerId)}>close drawer</button>
</SpaceBetween>
}
tools={<Tools>Tools content: {name}</Tools>}
Expand All @@ -45,7 +81,7 @@ function createView(name: string) {
const ROUTES: Array<{ navLink: SideNavigationProps.Link; View: React.ComponentType }> = [
{ navLink: { type: 'link', text: 'Page 1', href: 'page1' }, View: createView('page1') },
{ navLink: { type: 'link', text: 'Page 2', href: 'page2' }, View: createView('page2') },
{ navLink: { type: 'link', text: 'Page 3', href: 'page3' }, View: createView('page2') },
{ navLink: { type: 'link', text: 'Page 3', href: 'page3' }, View: createView('page3') },
];

export default function () {
Expand Down
2 changes: 2 additions & 0 deletions src/app-layout/__tests__/runtime-drawers.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -980,6 +980,8 @@ describe('toolbar mode only features', () => {

wrapper.findDrawerTriggerById('global-drawer-1')!.click();

await delay();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

the test does not work without this condition anymore, because now setting openDrawersHistory works asynchronously


expect(globalDrawersWrapper.findDrawerById('global-drawer-1')!.isActive()).toBe(true);
wrapper.findDrawerTriggerById('global-drawer-1')!.click();

Expand Down
1 change: 1 addition & 0 deletions src/app-layout/split-panel/provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface SplitPanelProviderProps extends SplitPanelContextBaseProps {
reportSize: (size: number) => void;
getMaxHeight: () => number;
children?: React.ReactNode;
animationDisabled?: boolean;
}

export function SplitPanelProvider({
Expand Down
11 changes: 4 additions & 7 deletions src/app-layout/visual-refresh-toolbar/drawer/global-drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@ import clsx from 'clsx';

import { InternalButton } from '../../../button/internal';
import PanelResizeHandle from '../../../internal/components/panel-resize-handle';
import { NonCancelableEventHandler } from '../../../internal/events';
import customCssProps from '../../../internal/generated/custom-css-properties';
import { getLimitedValue } from '../../../split-panel/utils/size-utils';
import { AppLayoutProps } from '../../interfaces';
import { getDrawerTopOffset } from '../compute-layout';
import { AppLayoutInternals } from '../interfaces';
import { AppLayoutInternals, InternalDrawer } from '../interfaces';
import { useResize } from './use-resize';

import sharedStyles from '../../resize/styles.css.js';
Expand All @@ -21,9 +19,7 @@ import styles from './styles.css.js';
interface AppLayoutGlobalDrawerImplementationProps {
appLayoutInternals: AppLayoutInternals;
show: boolean;
activeGlobalDrawer:
| (AppLayoutProps.Drawer & { onShow?: NonCancelableEventHandler; onHide?: NonCancelableEventHandler })
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 realized that onShow and onHide are not used here anymore, since onVisibilityChange is implemented on runtime component wrapper level

| undefined;
activeGlobalDrawer: InternalDrawer | undefined;
}

function AppLayoutGlobalDrawerImplementation({
Expand Down Expand Up @@ -68,6 +64,7 @@ function AppLayoutGlobalDrawerImplementation({
const size = getLimitedValue(minDrawerSize, activeDrawerSize, maxDrawerSize);
const lastOpenedDrawerId = drawersOpenQueue.length ? drawersOpenQueue[0] : null;
const hasTriggerButton = !!activeGlobalDrawer?.trigger;
const animationDisabled = activeGlobalDrawer?.defaultActive && !drawersOpenQueue.includes(activeGlobalDrawer.id);

return (
<Transition nodeRef={drawerRef} in={show} appear={show} timeout={0}>
Expand All @@ -81,7 +78,7 @@ function AppLayoutGlobalDrawerImplementation({
styles.drawer,
styles['drawer-global'],
styles[state],
sharedStyles['with-motion-horizontal'],
!animationDisabled && sharedStyles['with-motion-horizontal'],
{
[styles['drawer-hidden']]: !show,
[styles['last-opened']]: lastOpenedDrawerId === activeDrawerId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export function AppLayoutDrawerImplementation({ appLayoutInternals }: AppLayoutD
const isLegacyDrawer = drawersOpenQueue === undefined;
const size = getLimitedValue(minDrawerSize, activeDrawerSize, maxDrawerSize);
const lastOpenedDrawerId = drawersOpenQueue?.length ? drawersOpenQueue[0] : activeDrawerId;
const animationDisabled = activeDrawer?.defaultActive && !drawersOpenQueue.includes(activeDrawer.id);

return (
<Transition nodeRef={drawerRef} in={!!activeDrawer} appear={true} timeout={0}>
Expand All @@ -70,7 +71,8 @@ export function AppLayoutDrawerImplementation({ appLayoutInternals }: AppLayoutD
id={activeDrawerId}
aria-hidden={!activeDrawer}
aria-label={computedAriaLabels.content}
className={clsx(styles.drawer, sharedStyles['with-motion-horizontal'], {
className={clsx(styles.drawer, {
[sharedStyles['with-motion-horizontal']]: !animationDisabled,
[styles['last-opened']]: lastOpenedDrawerId === activeDrawerId,
[styles.legacy]: isLegacyDrawer,
[testutilStyles['active-drawer']]: !toolsOnlyMode && activeDrawerId,
Expand Down
7 changes: 7 additions & 0 deletions src/app-layout/visual-refresh-toolbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ const AppLayoutVisualRefreshToolbar = React.forwardRef<AppLayoutProps.Ref, AppLa
const [toolbarState, setToolbarState] = useState<'show' | 'hide'>('show');
const [toolbarHeight, setToolbarHeight] = useState(0);
const [notificationsHeight, setNotificationsHeight] = useState(0);
const [navigationAnimationDisabled, setNavigationAnimationDisabled] = useState(true);
const [splitPanelAnimationDisabled, setSplitPanelAnimationDisabled] = useState(true);

const [toolsOpen = false, setToolsOpen] = useControllable(controlledToolsOpen, onToolsChange, false, {
componentName: 'AppLayout',
Expand Down Expand Up @@ -165,6 +167,7 @@ const AppLayoutVisualRefreshToolbar = React.forwardRef<AppLayoutProps.Ref, AppLa
);

const onSplitPanelToggleHandler = () => {
setSplitPanelAnimationDisabled(false);
setSplitPanelOpen(!splitPanelOpen);
splitPanelFocusControl.setLastInteraction({ type: splitPanelOpen ? 'close' : 'open' });
fireNonCancelableEvent(onSplitPanelToggle, { open: !splitPanelOpen });
Expand Down Expand Up @@ -212,6 +215,7 @@ const AppLayoutVisualRefreshToolbar = React.forwardRef<AppLayoutProps.Ref, AppLa
const splitPanelFocusControl = useSplitPanelFocusControl([splitPanelPreferences, splitPanelOpen]);

const onNavigationToggle = useStableCallback((open: boolean) => {
setNavigationAnimationDisabled(false);
navigationFocusControl.setFocus();
fireNonCancelableEvent(onNavigationChange, { open });
});
Expand Down Expand Up @@ -451,6 +455,7 @@ const AppLayoutVisualRefreshToolbar = React.forwardRef<AppLayoutProps.Ref, AppLa
navigation={resolvedNavigation && <AppLayoutNavigation appLayoutInternals={appLayoutInternals} />}
navigationOpen={resolvedNavigationOpen}
navigationWidth={navigationWidth}
navigationAnimationDisabled={navigationAnimationDisabled}
tools={drawers && drawers.length > 0 && <AppLayoutDrawer appLayoutInternals={appLayoutInternals} />}
globalTools={
<ActiveDrawersContext.Provider value={activeGlobalDrawersIds}>
Expand All @@ -465,6 +470,7 @@ const AppLayoutVisualRefreshToolbar = React.forwardRef<AppLayoutProps.Ref, AppLa
<AppLayoutSplitPanelSide
appLayoutInternals={appLayoutInternals}
splitPanelInternals={splitPanelInternals}
animationDisabled={splitPanelAnimationDisabled}
>
{splitPanel}
</AppLayoutSplitPanelSide>
Expand All @@ -475,6 +481,7 @@ const AppLayoutVisualRefreshToolbar = React.forwardRef<AppLayoutProps.Ref, AppLa
<AppLayoutSplitPanelBottom
appLayoutInternals={appLayoutInternals}
splitPanelInternals={splitPanelInternals}
animationDisabled={splitPanelAnimationDisabled}
>
{splitPanel}
</AppLayoutSplitPanelBottom>
Expand Down
10 changes: 6 additions & 4 deletions src/app-layout/visual-refresh-toolbar/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { FocusControlMultipleStates, FocusControlState } from '../utils/use-focu
import { SplitPanelFocusControlState } from '../utils/use-split-panel-focus-control';
import { VerticalLayoutOutput } from './compute-layout';

export type InternalDrawer = AppLayoutProps.Drawer & { defaultActive?: boolean };

// Widgetization notice: structures in this file are shared multiple app layout instances, possibly different minor versions.
// Treat thsese structures as an API and do not make incompatible changes.
export interface AppLayoutInternals {
Expand All @@ -25,18 +27,18 @@ export interface AppLayoutInternals {
splitPanelFocusControl: SplitPanelFocusControlState;
splitPanelToggleConfig: SplitPanelSideToggleProps;
isMobile: boolean;
activeDrawer: AppLayoutProps.Drawer | undefined;
activeDrawer: InternalDrawer | undefined;
activeDrawerSize: number;
minDrawerSize: number;
maxDrawerSize: number;
minGlobalDrawersSizes: Record<string, number>;
maxGlobalDrawersSizes: Record<string, number>;
drawers: ReadonlyArray<AppLayoutProps.Drawer>;
drawers: ReadonlyArray<InternalDrawer>;
drawersFocusControl: FocusControlState;
globalDrawersFocusControl: FocusControlMultipleStates;
activeGlobalDrawersIds: ReadonlyArray<string>;
activeGlobalDrawers: ReadonlyArray<AppLayoutProps.Drawer>;
globalDrawers: ReadonlyArray<AppLayoutProps.Drawer>;
activeGlobalDrawers: ReadonlyArray<InternalDrawer>;
globalDrawers: ReadonlyArray<InternalDrawer>;
activeGlobalDrawersSizes: Record<string, number>;
stickyNotifications: AppLayoutPropsWithDefaults['stickyNotifications'];
breadcrumbs: React.ReactNode;
Expand Down
4 changes: 3 additions & 1 deletion src/app-layout/visual-refresh-toolbar/skeleton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ interface SkeletonLayoutProps
bottomSplitPanel?: React.ReactNode;
globalTools?: React.ReactNode;
globalToolsOpen?: boolean;
navigationAnimationDisabled?: boolean;
}

export const SkeletonLayout = React.forwardRef<HTMLDivElement, SkeletonLayoutProps>(
Expand All @@ -65,6 +66,7 @@ export const SkeletonLayout = React.forwardRef<HTMLDivElement, SkeletonLayoutPro
maxContentWidth,
disableContentPaddings,
globalToolsOpen,
navigationAnimationDisabled,
},
ref
) => {
Expand Down Expand Up @@ -92,7 +94,7 @@ export const SkeletonLayout = React.forwardRef<HTMLDivElement, SkeletonLayoutPro
styles.navigation,
!navigationOpen && styles['panel-hidden'],
toolsOpen && styles['unfocusable-mobile'],
sharedStyles['with-motion-horizontal']
!navigationAnimationDisabled && sharedStyles['with-motion-horizontal']
)}
>
{navigation}
Expand Down
12 changes: 10 additions & 2 deletions src/app-layout/visual-refresh-toolbar/split-panel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,19 @@ interface AppLayoutSplitPanelDrawerSideImplementationProps {
appLayoutInternals: AppLayoutInternals;
splitPanelInternals: SplitPanelProviderProps;
children: React.ReactNode;
animationDisabled?: boolean;
}

export function AppLayoutSplitPanelDrawerSideImplementation({
children,
appLayoutInternals,
splitPanelInternals,
animationDisabled,
}: AppLayoutSplitPanelDrawerSideImplementationProps) {
const { splitPanelControlId, placement, verticalOffsets } = appLayoutInternals;
const drawerTopOffset = verticalOffsets.drawers ?? placement.insetBlockStart;
return (
<SplitPanelProvider {...splitPanelInternals}>
<SplitPanelProvider {...splitPanelInternals} animationDisabled={animationDisabled}>
<section
id={splitPanelControlId}
className={styles['split-panel-side']}
Expand All @@ -41,13 +43,19 @@ export interface AppLayoutSplitPanelDrawerBottomImplementationProps {
appLayoutInternals: AppLayoutInternals;
splitPanelInternals: SplitPanelProviderProps;
children: React.ReactNode;
animationDisabled?: boolean;
}

export function AppLayoutSplitPanelDrawerBottomImplementation({
children,
splitPanelInternals,
animationDisabled,
}: AppLayoutSplitPanelDrawerBottomImplementationProps) {
return <SplitPanelProvider {...splitPanelInternals}>{children}</SplitPanelProvider>;
return (
<SplitPanelProvider {...splitPanelInternals} animationDisabled={animationDisabled}>
{children}
</SplitPanelProvider>
);
}

export const createWidgetizedAppLayoutSplitPanelDrawerSide = createWidgetizedComponent(
Expand Down
1 change: 1 addition & 0 deletions src/internal/context/split-panel-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export interface SplitPanelContextBaseProps {

export interface SplitPanelContextProps extends SplitPanelContextBaseProps {
relativeSize: number;
animationDisabled?: boolean;
}

const SplitPanelContext = createContext<SplitPanelContextProps | null>(null);
Expand Down
35 changes: 18 additions & 17 deletions src/split-panel/bottom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,15 @@ export function SplitPanelContentBottom({
}: SplitPanelContentBottomProps) {
const isRefresh = useVisualRefresh();
const isToolbar = useAppLayoutToolbarEnabled();
const { bottomOffset, leftOffset, rightOffset, disableContentPaddings, contentWrapperPaddings, reportHeaderHeight } =
useSplitPanelContext();
const {
bottomOffset,
leftOffset,
rightOffset,
disableContentPaddings,
contentWrapperPaddings,
reportHeaderHeight,
animationDisabled,
} = useSplitPanelContext();
const isMobile = useMobile();

const headerRef = useRef<HTMLDivElement>(null);
Expand All @@ -54,21 +61,15 @@ export function SplitPanelContentBottom({
return (
<div
{...baseProps}
className={clsx(
baseProps.className,
styles.drawer,
styles['position-bottom'],
testUtilStyles.root,
sharedStyles['with-motion-vertical'],
{
[testUtilStyles['open-position-bottom']]: isOpen,
[styles['drawer-closed']]: !isOpen,
[styles['drawer-mobile']]: isMobile,
[styles['drawer-disable-content-paddings']]: disableContentPaddings,
[styles.refresh]: isRefresh,
[styles['with-toolbar']]: isToolbar,
}
)}
className={clsx(baseProps.className, styles.drawer, styles['position-bottom'], testUtilStyles.root, {
[sharedStyles['with-motion-vertical']]: !animationDisabled,
[testUtilStyles['open-position-bottom']]: isOpen,
[styles['drawer-closed']]: !isOpen,
[styles['drawer-mobile']]: isMobile,
[styles['drawer-disable-content-paddings']]: disableContentPaddings,
[styles.refresh]: isRefresh,
[styles['with-toolbar']]: isToolbar,
})}
onClick={() => !isOpen && onToggle()}
style={{
insetBlockEnd: bottomOffset,
Expand Down
22 changes: 8 additions & 14 deletions src/split-panel/side.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,19 @@ export function SplitPanelContentSide({
panelHeaderId,
onToggle,
}: SplitPanelContentSideProps) {
const { topOffset, bottomOffset } = useSplitPanelContext();
const { topOffset, bottomOffset, animationDisabled } = useSplitPanelContext();
const isRefresh = useVisualRefresh();
const isToolbar = useAppLayoutToolbarEnabled();
return (
<div
{...baseProps}
className={clsx(
baseProps.className,
styles.drawer,
styles['position-side'],
testUtilStyles.root,
sharedStyles['with-motion-horizontal'],
{
[testUtilStyles['open-position-side']]: isOpen,
[styles['drawer-closed']]: !isOpen,
[styles['with-toolbar']]: isToolbar,
[styles.refresh]: isRefresh,
}
)}
className={clsx(baseProps.className, styles.drawer, styles['position-side'], testUtilStyles.root, {
[sharedStyles['with-motion-horizontal']]: !animationDisabled,
[testUtilStyles['open-position-side']]: isOpen,
[styles['drawer-closed']]: !isOpen,
[styles['with-toolbar']]: isToolbar,
[styles.refresh]: isRefresh,
})}
style={{
width: isOpen ? cappedSize : isRefresh ? '0px' : undefined,
maxWidth: isRefresh ? '100%' : undefined,
Expand Down
Loading