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 resize animations in Dialog #11643

Merged
merged 15 commits into from
Nov 28, 2024
2 changes: 2 additions & 0 deletions app/common/src/text/english.json
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,8 @@
"cannotCreateAssetsHere": "You do not have the permissions to create assets here.",
"enableVersionChecker": "Enable Version Checker",
"enableVersionCheckerDescription": "Show a dialog if the current version of the desktop app does not match the latest version.",
"disableAnimations": "Disable animations",
"disableAnimationsDescription": "Disable all animations in the app.",
"removeTheLocalDirectoryXFromFavorites": "remove the local folder '$0' from your favorites",
"changeLocalRootDirectoryInSettings": "Change your root folder in Settings.",
"localStorage": "Local Storage",
Expand Down
20 changes: 16 additions & 4 deletions app/gui/.storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import type { Preview as ReactPreview } from '@storybook/react'
import type { Preview as VuePreview } from '@storybook/vue3'
import isChromatic from 'chromatic/isChromatic'
import { useLayoutEffect, useState } from 'react'
import { StrictMode, useLayoutEffect, useState } from 'react'

import invariant from 'tiny-invariant'
import UIProviders from '../src/dashboard/components/UIProviders'
Expand Down Expand Up @@ -59,22 +59,34 @@ const reactPreview: ReactPreview = {

return (
<UIProviders locale="en-US" portalRoot={portalRoot}>
{Story(context)}
<Story {...context} />
</UIProviders>
)
},

(Story, context) => (
<>
<div className="enso-dashboard">{Story(context)}</div>
<div className="enso-dashboard">
<Story {...context} />
</div>
<div id="enso-portal-root" className="enso-portal-root" />
</>
),

(Story, context) => {
const [queryClient] = useState(() => createQueryClient())
return <QueryClientProvider client={queryClient}>{Story(context)}</QueryClientProvider>
return (
<QueryClientProvider client={queryClient}>
<Story {...context} />
</QueryClientProvider>
)
},

(Story, context) => (
<StrictMode>
<Story {...context} />
</StrictMode>
),
],
}

Expand Down
82 changes: 34 additions & 48 deletions app/gui/src/dashboard/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,6 @@ import ModalProvider, * as modalProvider from '#/providers/ModalProvider'
import * as navigator2DProvider from '#/providers/Navigator2DProvider'
import SessionProvider from '#/providers/SessionProvider'
import * as textProvider from '#/providers/TextProvider'
import type { Spring } from 'framer-motion'
import { MotionConfig } from 'framer-motion'

import ConfirmRegistration from '#/pages/authentication/ConfirmRegistration'
import ForgotPassword from '#/pages/authentication/ForgotPassword'
Expand Down Expand Up @@ -105,16 +103,6 @@ import { InvitedToOrganizationModal } from '#/modals/InvitedToOrganizationModal'
// === Global configuration ===
// ============================

const DEFAULT_TRANSITION_OPTIONS: Spring = {
type: 'spring',
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
stiffness: 200,
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
damping: 30,
mass: 1,
velocity: 0,
}

declare module '#/utilities/LocalStorage' {
/** */
interface LocalStorageData {
Expand Down Expand Up @@ -532,42 +520,40 @@ function AppRouter(props: AppRouterProps) {

return (
<FeatureFlagsProvider>
<MotionConfig reducedMotion="user" transition={DEFAULT_TRANSITION_OPTIONS}>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here we only move the MotionConfig from <App /> to <UIProvider /> to reuse it in Storybook

<RouterProvider navigate={navigate}>
<SessionProvider
saveAccessToken={authService.cognito.saveAccessToken.bind(authService.cognito)}
mainPageUrl={mainPageUrl}
userSession={userSession}
registerAuthEventListener={registerAuthEventListener}
refreshUserSession={refreshUserSession}
>
<BackendProvider remoteBackend={remoteBackend} localBackend={localBackend}>
<AuthProvider
shouldStartInOfflineMode={isAuthenticationDisabled}
authService={authService}
onAuthenticated={onAuthenticated}
>
<InputBindingsProvider inputBindings={inputBindings}>
{/* Ideally this would be in `Drive.tsx`, but it currently must be all the way out here
* due to modals being in `TheModal`. */}
<DriveProvider>
<errorBoundary.ErrorBoundary>
<LocalBackendPathSynchronizer />
<VersionChecker />
{routes}
<suspense.Suspense>
<errorBoundary.ErrorBoundary>
<devtools.EnsoDevtools />
</errorBoundary.ErrorBoundary>
</suspense.Suspense>
</errorBoundary.ErrorBoundary>
</DriveProvider>
</InputBindingsProvider>
</AuthProvider>
</BackendProvider>
</SessionProvider>
</RouterProvider>
</MotionConfig>
<RouterProvider navigate={navigate}>
<SessionProvider
saveAccessToken={authService.cognito.saveAccessToken.bind(authService.cognito)}
mainPageUrl={mainPageUrl}
userSession={userSession}
registerAuthEventListener={registerAuthEventListener}
refreshUserSession={refreshUserSession}
>
<BackendProvider remoteBackend={remoteBackend} localBackend={localBackend}>
<AuthProvider
shouldStartInOfflineMode={isAuthenticationDisabled}
authService={authService}
onAuthenticated={onAuthenticated}
>
<InputBindingsProvider inputBindings={inputBindings}>
{/* Ideally this would be in `Drive.tsx`, but it currently must be all the way out here
* due to modals being in `TheModal`. */}
<DriveProvider>
<errorBoundary.ErrorBoundary>
<LocalBackendPathSynchronizer />
<VersionChecker />
{routes}
<suspense.Suspense>
<errorBoundary.ErrorBoundary>
<devtools.EnsoDevtools />
</errorBoundary.ErrorBoundary>
</suspense.Suspense>
</errorBoundary.ErrorBoundary>
</DriveProvider>
</InputBindingsProvider>
</AuthProvider>
</BackendProvider>
</SessionProvider>
</RouterProvider>
</FeatureFlagsProvider>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import type { Meta, StoryObj } from '@storybook/react'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useLayoutEffect, useRef } from 'react'
import { DialogTrigger } from 'react-aria-components'
import { Button } from '../Button'
import { Dialog, type DialogProps } from './Dialog'

type Story = StoryObj<DialogProps>

export default {
title: 'AriaComponents/Dialog',
component: Dialog,
render: (args) => (
<DialogTrigger defaultOpen>
<Button>Open Dialog</Button>

<Dialog {...args} />
</DialogTrigger>
),
args: {
type: 'modal',
title: 'Dialog Title',
children: 'Dialog Content',
},
} as Meta<DialogProps>

export const Default = {}

// Use a random query key to avoid caching
const QUERY_KEY = Math.random().toString()

function SuspenseContent({ delay = 10_000 }: { delay?: number }): React.ReactNode {
useSuspenseQuery({
queryKey: [QUERY_KEY],
gcTime: 0,
initialDataUpdatedAt: 0,
queryFn: () =>
new Promise((resolve) => {
setTimeout(() => {
resolve('resolved')
}, delay)
}),
})

return (
<div className="flex h-[250px] flex-col items-center justify-center text-center">
Unsuspended content
</div>
)
}

export const Suspened = {
args: {
children: <SuspenseContent delay={10_000_000_000} />,
},
}

function BrokenContent(): React.ReactNode {
throw new Error('💣')
}

export const Broken = {
args: {
children: <BrokenContent />,
},
}

function ResizableContent() {
const divRef = useRef<HTMLDivElement>(null)

useLayoutEffect(() => {
const getRandomHeight = () => Math.floor(Math.random() * 250 + 100)

if (divRef.current) {
divRef.current.style.height = `${getRandomHeight()}px`

setInterval(() => {
if (divRef.current) {
divRef.current.style.height = `${getRandomHeight()}px`
}
}, 2_000)
}
}, [])

return (
<div ref={divRef} className="flex flex-none items-center justify-center text-center">
This dialog should resize with animation
</div>
)
}

export const AnimateSize: Story = {
args: {
children: <ResizableContent />,
},
parameters: {
chromatic: { disableSnapshot: true },
},
}

export const Fullscreen = {
args: {
type: 'fullscreen',
},
}
Loading
Loading