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

Fix side panel exit animation #350

Merged
merged 15 commits into from
May 10, 2024
5 changes: 5 additions & 0 deletions .changeset/beige-tips-swim.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'playroom': patch
---

Fixes a bug in the side panel exit animation that was causing the contents to vanish abruptly
4 changes: 2 additions & 2 deletions cypress/e2e/snippets.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ describe('Snippets', () => {
assertSnippetsListIsVisible();
assertCodePaneLineCount(8);
filterSnippets('{esc}');
assertCodePaneLineCount(1);
assertCodePaneLineCount(1, { wait: 500 });
typeCode(`${isMac() ? '{cmd}' : '{ctrl}'}k`);
assertSnippetsListIsVisible();
assertCodePaneLineCount(8);
Expand All @@ -89,7 +89,7 @@ describe('Snippets', () => {
// Close without persisting
filterSnippets('{esc}');
assertCodePaneContains('<div>Initial <span>code</span></div>');
assertCodePaneLineCount(1);
assertCodePaneLineCount(1, { wait: 500 });

// Re-open and persist
typeCode(`${isMac() ? '{cmd}' : '{ctrl}'}k`);
Expand Down
7 changes: 6 additions & 1 deletion cypress/support/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,15 @@ export const assertCodePaneContains = (text) => {
});
};

export const assertCodePaneLineCount = (lines) => {
export const assertCodePaneLineCount = (lines, { wait } = {}) => {
getCodeEditor().within(() =>
cy.get('.CodeMirror-line').should('have.length', lines)
);

// Wait after check to ensure original focus is restored
if (typeof wait === 'number') {
cy.wait(wait);
}
};

export const assertFramesMatch = (matches) =>
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
"re-resizable": "^6.9.9",
"react-docgen-typescript": "^2.2.2",
"react-helmet": "^6.1.0",
"react-transition-group": "^4.4.5",
"react-use": "^17.4.0",
"read-pkg-up": "^7.0.1",
"scope-eval": "^1.0.0",
Expand All @@ -114,6 +115,7 @@
"@octokit/rest": "^19.0.5",
"@types/jest": "^29.2.4",
"@types/react-helmet": "^6.1.6",
"@types/react-transition-group": "^4.4.10",
"concurrently": "^7.6.0",
"cypress": "^13.6.6",
"eslint": "^8.44.0",
Expand Down
33 changes: 33 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 36 additions & 8 deletions src/Playroom/Toolbar/Toolbar.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,24 +92,52 @@ export const panel = style([
position: 'relative',
display: 'flex',
overflow: 'auto',
transition: 'slow',
pointerEvents: 'auto',
}),
{
width: toolbarOpenSize,
backgroundColor: colorPaletteVars.background.surface,
borderLeft: `${toolbarBorderThickness} solid ${colorPaletteVars.border.standard}`,
selectors: {
[`${root}:not(${isOpen}) &`]: {
transform: `translateX(${calc(`${toolbarOpenSize}px`).multiply(0.3)})`,
opacity: 0,
pointerEvents: 'none',
},
},
},
]);

export const preference = sprinkles({
position: 'absolute',
inset: 0,
});

export const transitionStyles = {
enter: style({
opacity: 0,
transform: `translateX(30%)`,
}),
enterActive: style([
sprinkles({
transition: 'slow',
}),
{
opacity: 1,
transform: `translateX(0)`,
},
]),
enterDone: style({
opacity: 1,
transform: `translateX(0)`,
}),
exit: style({
opacity: 1,
}),
exitActive: style([
sprinkles({
transition: 'slow',
}),
{
opacity: 0,
transform: `translateX(30%)`,
},
]),
exitDone: style({
opacity: 0,
transform: `translateX(30%)`,
}),
};
104 changes: 57 additions & 47 deletions src/Playroom/Toolbar/Toolbar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useContext, useState, useCallback } from 'react';
import { useContext, useState, useCallback, useEffect } from 'react';
import { useTimeoutFn } from 'react-use';
import classnames from 'classnames';
import type { PlayroomProps } from '../Playroom';
Expand All @@ -17,13 +17,16 @@ import SettingsPanel from '../SettingsPanel/SettingsPanel';
import SettingsIcon from '../icons/SettingsIcon';
import { isMac } from '../../utils/formatting';

import { CSSTransition } from 'react-transition-group';

interface Props {
themes: PlayroomProps['themes'];
widths: PlayroomProps['widths'];
snippets: PlayroomProps['snippets'];
}

export const toolbarItemCount = 5;
const ANIMATION_TIMEOUT = 300;

export default ({ themes: allThemes, widths: allWidths, snippets }: Props) => {
const [
Expand Down Expand Up @@ -58,6 +61,15 @@ export default ({ themes: allThemes, widths: allWidths, snippets }: Props) => {
const isSettingsOpen = activeToolbarPanel === 'settings';
const isPreviewOpen = activeToolbarPanel === 'preview';

const [lastActivePanel, setLastActivePanel] =
useState<typeof activeToolbarPanel>(undefined);

useEffect(() => {
if (activeToolbarPanel) {
setLastActivePanel(activeToolbarPanel);
}
}, [activeToolbarPanel]);

const hasSnippets = snippets && snippets.length > 0;
const hasFilteredFrames =
visibleThemes.length > 0 || visibleWidths.length > 0;
Expand Down Expand Up @@ -148,58 +160,56 @@ export default ({ themes: allThemes, widths: allWidths, snippets }: Props) => {
</ToolbarItem>
</div>
</div>

<div className={styles.panel}>
{isSnippetsOpen && (
<div
hidden={isSnippetsOpen ? undefined : true}
className={styles.preference}
>
<Snippets
snippets={snippets}
onHighlight={(snippet) => {
dispatch({
type: 'previewSnippet',
payload: { snippet },
});
}}
onClose={(snippet) => {
if (snippet) {
<CSSTransition
in={isOpen}
timeout={ANIMATION_TIMEOUT}
classNames={styles.transitionStyles}
mountOnEnter
unmountOnExit
onExited={() => setLastActivePanel(undefined)}
>
<div className={styles.panel} id="custom-id">
<div className={styles.preference}>
{lastActivePanel === 'snippets' && (
<Snippets
snippets={snippets}
onHighlight={(snippet) => {
dispatch({
type: 'persistSnippet',
type: 'previewSnippet',
payload: { snippet },
});
} else {
dispatch({ type: 'closeToolbar' });
}
}}
/>
</div>
)}
<div
hidden={isFramesOpen ? undefined : true}
className={styles.preference}
>
<FramesPanel
availableWidths={allWidths}
availableThemes={allThemes}
/>
</div>
}}
onClose={(snippet) => {
if (snippet) {
dispatch({
type: 'persistSnippet',
payload: { snippet },
});
} else {
dispatch({ type: 'closeToolbar' });
}
}}
/>
)}

<div
hidden={isPreviewOpen ? undefined : true}
className={styles.preference}
>
<PreviewPanel themes={allThemes} visibleThemes={visibleThemes} />
</div>
{lastActivePanel === 'frames' && (
<FramesPanel
availableWidths={allWidths}
availableThemes={allThemes}
/>
)}

{lastActivePanel === 'preview' && (
<PreviewPanel
themes={allThemes}
visibleThemes={visibleThemes}
/>
)}

<div
hidden={isSettingsOpen ? undefined : true}
className={styles.preference}
>
<SettingsPanel />
{lastActivePanel === 'settings' && <SettingsPanel />}
</div>
</div>
</div>
</CSSTransition>
</div>
</div>
);
Expand Down