Skip to content

Commit

Permalink
Convert test animation durations to PandaCSS (#2110).
Browse files Browse the repository at this point in the history
  • Loading branch information
raineorshine committed Aug 29, 2024
1 parent 6be0e3d commit 21cd2c2
Show file tree
Hide file tree
Showing 8 changed files with 39 additions and 20 deletions.
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
}
</style>
</head>
<body>
<body data-env="%MODE%">
<div id="root"></div>

<script>
Expand Down
23 changes: 23 additions & 0 deletions panda.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,28 @@ export default defineConfig({
},
},
},
animations: {
highlightPulseDuration: {
value: {
base: '500ms',
_test: '0s',
},
},
/** The animation duration for the slower opacity transition and horizontal shift of the LayoutTree as the depth of the cursor changes. */
layoutSlowShiftDuration: {
value: {
base: '750ms',
_test: '0s',
},
},
/** The animation duration of a node in the LayoutTree component. */
layoutNodeAnimationDuration: {
value: {
base: '150ms',
_test: '0s',
},
},
},
},
},
},
Expand All @@ -192,6 +214,7 @@ export default defineConfig({
conditions: {
light: '[data-color-mode=light] &',
dark: '[data-color-mode=dark] &',
test: '[data-env=test] &',
},

// The output directory for your css system
Expand Down
7 changes: 4 additions & 3 deletions src/components/LayoutTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import classNames from 'classnames'
import _ from 'lodash'
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useSelector } from 'react-redux'
import { token } from '../../styled-system/tokens'
import Index from '../@types/IndexType'
import LazyEnv from '../@types/LazyEnv'
import Path from '../@types/Path'
Expand All @@ -10,7 +11,7 @@ import State from '../@types/State'
import Thought from '../@types/Thought'
import ThoughtId from '../@types/ThoughtId'
import { isTouch } from '../browser'
import { HOME_PATH, LAYOUT_NODE_ANIMATION_DURATION } from '../constants'
import { HOME_PATH } from '../constants'
import testFlags from '../e2e/testFlags'
import attributeEquals from '../selectors/attributeEquals'
import findDescendant from '../selectors/findDescendant'
Expand Down Expand Up @@ -624,7 +625,7 @@ const LayoutTree = () => {
// Use translateX instead of marginLeft to prevent multiline thoughts from continuously recalculating layout as their width changes during the transition.
// Instead of using spaceAbove, we use -min(spaceAbove, c) + c, where c is the number of pixels of hidden thoughts above the cursor before cropping kicks in.
transform: `translateX(${1.5 - indent}em`,
transition: 'transform 0.75s ease-out',
transition: `transform ${token('animations.layoutSlowShiftDuration')} ease-out`,
// Add a negative marginRight equal to translateX to ensure the thought takes up the full width. Not animated for a more stable visual experience.
marginRight: `${-indent + (isTouch ? 2 : -1)}em`,
}}
Expand Down Expand Up @@ -677,7 +678,7 @@ const LayoutTree = () => {
// Unfortunately left causes layout recalculation, so we may want to hoist DropChild into a parent and manually control the position.
left: x,
top: y,
transition: `left ${LAYOUT_NODE_ANIMATION_DURATION}ms ease-out,top ${LAYOUT_NODE_ANIMATION_DURATION}ms ease-out`,
transition: `left ${token('animations.layoutNodeAnimationDuration')} ease-out,top ${token('animations.layoutNodeAnimationDuration')} ease-out`,
// Table col1 uses its exact width since cannot extend to the right edge of the screen.
// All other thoughts extend to the right edge of the screen. We cannot use width auto as it causes the text to wrap continuously during the counter-indentation animation, which is jarring. Instead, use a fixed width of the available space so that it changes in a stepped fashion as depth changes and the word wrap will not be animated. Use x instead of depth in order to accommodate ancestor tables.
// 1em + 10px is an eyeball measurement at font sizes 14 and 18
Expand Down
5 changes: 4 additions & 1 deletion src/components/Subthought.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useEffect, useMemo, useRef } from 'react'
import { shallowEqual, useSelector } from 'react-redux'
import { token } from '../../styled-system/tokens'
import Autofocus from '../@types/Autofocus'
import LazyEnv from '../@types/LazyEnv'
import Path from '../@types/Path'
Expand Down Expand Up @@ -105,7 +106,9 @@ const Subthought = ({
// If autofocus has not changed, it means that the thought is being rendered for the first time, such as the children of a thought that was just expanded. In this case, match the tree-node top animation (150ms) to ensure that the newly rendered thoughts fade in to fill the space that is being opened up from the next uncle animating down.
// Note that ease-in is used in contrast to the tree-node's ease-out. This gives a little more time for the next uncle to animate down and clear space before the newly rendered thought fades in. Otherwise they overlap too much during the transition.
const opacity = autofocus === 'show' ? '1' : autofocus === 'dim' ? '0.5' : '0'
const opacityTransition = autofocusChanged ? 'opacity 0.75s ease-out' : 'opacity 0.15s ease-in'
const opacityTransition = autofocusChanged
? `opacity ${token('animations.layoutSlowShiftDuration')} ease-out`
: `opacity ${token('animations.layoutNodeAnimationDuration')} ease-in`
useEffect(() => {
if (!ref.current) return
// start opacity at 0 and set to actual opacity in useEffect
Expand Down
9 changes: 4 additions & 5 deletions src/components/Thought.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import classNames from 'classnames'
import React, { useCallback, useMemo } from 'react'
import { shallowEqual, useDispatch, useSelector } from 'react-redux'
import { token } from '../../styled-system/tokens'
import DragThoughtZone from '../@types/DragThoughtZone'
import DropThoughtZone from '../@types/DropThoughtZone'
import LazyEnv from '../@types/LazyEnv'
Expand Down Expand Up @@ -90,9 +91,6 @@ export interface ThoughtContainerProps {
updateSize?: () => void
}

/** Animation to apply to a parent when one of its children is being hovered over. Disabled in puppeteer tests. */
const CHILD_IS_HOVERING_ANIMATION = navigator.webdriver ? undefined : 'pulse-light 0.5s linear infinite alternate'

/** Returns true if two lists of children are equal. Deeply compares id, value, and rank. */
const equalChildren = (a: Thought[], b: Thought[]) =>
a === b ||
Expand Down Expand Up @@ -272,10 +270,11 @@ const ThoughtContainer = ({
// See: https://stackoverflow.com/a/46452396/480608
const styleThought = useMemo(
(): React.CSSProperties => ({
/** Animation to apply to a parent when one of its children is being hovered over. Disabled in puppeteer tests. */
...(isChildHovering
? {
WebkitTextStrokeWidth: '0.05em',
animation: CHILD_IS_HOVERING_ANIMATION,
animation: `pulse-light ${token('animations.highlightPulseDuration')} linear infinite alternate`,
color: colors.highlight,
}
: null),
Expand Down Expand Up @@ -357,7 +356,7 @@ const ThoughtContainer = ({
style={{
// so that .thought can be sized at 100% and .thought .bullet-cursor-overlay bullet can be positioned correctly.
position: 'relative',
transition: 'transform 0.75s ease-out, opacity 0.75s ease-out',
transition: `transform ${token('animations.layoutSlowShiftDuration')} ease-out, opacity ${token('animations.layoutSlowShiftDuration')} ease-out`,
...style,
...styleContainer,
// extend the click area to the left (except if table column 2)
Expand Down
3 changes: 0 additions & 3 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -498,9 +498,6 @@ export const FREE_THOUGHTS_THROTTLE = 1000
/** Controls the delay when enabling distraction free typing. */
export const THROTTLE_DISTRACTION_FREE_TYPING = 100

/** The animation duration of a node in the LayoutTree component. */
export const LAYOUT_NODE_ANIMATION_DURATION = 150

/** The animation duration for a toolbar button press. */
export const TOOLBAR_PRESS_ANIMATION_DURATION = 80

Expand Down
4 changes: 0 additions & 4 deletions src/e2e/puppeteer/__tests__/cursor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { LAYOUT_NODE_ANIMATION_DURATION } from '../../../constants'
import sleep from '../../../util/sleep'
import helpers from '../helpers'

Expand Down Expand Up @@ -38,8 +37,6 @@ it('set the cursor on a subthought on load', async () => {
await waitForEditable('b')
await clickThought('b')
await waitForEditable('z')
// getting intermittent test failures so try waiting before clicking
await sleep(LAYOUT_NODE_ANIMATION_DURATION)
await clickThought('z')

// wait for browser selection to update
Expand Down Expand Up @@ -123,7 +120,6 @@ it('do nothing when clicking on a hidden great uncle', async () => {
// click c to hide d
// for some reason we need to sleep before clicking c, otherwise the cursor is moved to d
await waitForEditable('c')
await sleep(LAYOUT_NODE_ANIMATION_DURATION)
await clickThought('c')
await clickThought('d')

Expand Down
6 changes: 3 additions & 3 deletions src/redux-middleware/scrollCursorIntoView.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import _ from 'lodash'
import { ThunkMiddleware } from 'redux-thunk'
import { token } from '../../styled-system/tokens'
import Path from '../@types/Path'
import State from '../@types/State'
import { isSafari, isTouch } from '../browser'
import { LAYOUT_NODE_ANIMATION_DURATION } from '../constants'
import { PREVENT_AUTOSCROLL_TIMEOUT, isPreventAutoscrollInProgress } from '../device/preventAutoscroll'
import editingValueStore from '../stores/editingValue'
import scrollTopStore from '../stores/scrollTop'
Expand Down Expand Up @@ -100,7 +100,7 @@ const scrollCursorIntoView = () => {
scrollIntoViewIfNeeded(document.querySelector('.editing'))
},
// If this is the result of a navigation, wait for the layout animation to complete to not get false bounding rect values
userInteractedAfterNavigation ? 0 : LAYOUT_NODE_ANIMATION_DURATION,
userInteractedAfterNavigation ? 0 : parseInt(token('animations.layoutNodeAnimationDuration')),
)
}

Expand All @@ -110,7 +110,7 @@ editingValueStore.subscribe(
// Throttle aggressively since scrollCursorIntoView reads from the DOM and this is called on all edits.
_.throttle(() => {
// we need to wait for the cursor to animate into its final position before scrollCursorIntoView can accurately determine if it is in the viewport
setTimeout(scrollCursorIntoView, LAYOUT_NODE_ANIMATION_DURATION)
setTimeout(scrollCursorIntoView, parseInt(token('animations.layoutNodeAnimationDuration')))
}, 400),
)

Expand Down

0 comments on commit 21cd2c2

Please sign in to comment.