From 2e080a3a3401f38fb03d2651a86c2c76d763b2a4 Mon Sep 17 00:00:00 2001 From: Antoine Clausse Date: Thu, 10 Oct 2024 09:38:08 +0200 Subject: [PATCH] [web] Migrate IDE page loading screen to BS5 (#20896) * [web] Add `.loading-screen` style * [web] Add `.loading-screen-error` style * [web] Nest styles in `.loading-screen` * [web] Simplify code, make a more valuable Storybook * [web] Add a reusable bootstrap-switcher argument to loading.stories.tsx * [web] Make `isBootstrap5()` work in storybook * [web] Revert unrelated changes around `ConnectionError` type * [web] Remove comment about unhandled error codes https://github.com/overleaf/internal/pull/20896/files#r1790572314 * [web] Don't repeat the `errorCode` prop type * [web] Remove unused CSS and magic padding * [web] Fixup SCSS division * [storybook] Revert Storybook changes (moved to another branch) * [web] Fixup SCSS division again (lint) * [web] Render with `Boolean(errorCode) && ...` instead of `errorCode && ...` * [web] Remove importants; use spacing var Addresses Tim's comments GitOrigin-RevId: e8b5623f4bb9aa72a255851f46b45b652a0dbb16 --- .../ide-react/components/loading-error.tsx | 36 ++++--- .../features/ide-react/components/loading.tsx | 29 +++--- .../stories/loading/loading-error.stories.tsx | 33 ------- .../stories/loading/loading.stories.tsx | 41 ++++++-- .../pages/editor/loading-screen.scss | 95 +++++++++++-------- 5 files changed, 121 insertions(+), 113 deletions(-) delete mode 100644 services/web/frontend/stories/loading/loading-error.stories.tsx diff --git a/services/web/frontend/js/features/ide-react/components/loading-error.tsx b/services/web/frontend/js/features/ide-react/components/loading-error.tsx index 2e73e7dd56..e5d391a2ea 100644 --- a/services/web/frontend/js/features/ide-react/components/loading-error.tsx +++ b/services/web/frontend/js/features/ide-react/components/loading-error.tsx @@ -2,25 +2,23 @@ import { FC } from 'react' import { ConnectionError } from '@/features/ide-react/connection/types/connection-state' import getMeta from '@/utils/meta' +const errorMessages = { + 'io-not-loaded': 'ol-translationIoNotLoaded', + 'unable-to-join': 'ol-translationUnableToJoin', + 'i18n-error': 'ol-translationLoadErrorMessage', +} as const + +const isHandledCode = (key: string): key is keyof typeof errorMessages => + key in errorMessages + +export type LoadingErrorProps = { + errorCode: ConnectionError | 'i18n-error' | '' +} + // NOTE: i18n translations might not be loaded in the client at this point, // so these translations have to be loaded from meta tags -export const LoadingError: FC<{ - connectionStateError: ConnectionError | '' - i18nError?: Error -}> = ({ connectionStateError, i18nError }) => { - if (connectionStateError) { - switch (connectionStateError) { - case 'io-not-loaded': - return <>{getMeta('ol-translationIoNotLoaded')} - - case 'unable-to-join': - return <>{getMeta('ol-translationUnableToJoin')} - } - } - - if (i18nError) { - return <>{getMeta('ol-translationLoadErrorMessage')} - } - - return null +export const LoadingError: FC = ({ errorCode }) => { + return isHandledCode(errorCode) ? ( +

{getMeta(errorMessages[errorCode])}

+ ) : null } diff --git a/services/web/frontend/js/features/ide-react/components/loading.tsx b/services/web/frontend/js/features/ide-react/components/loading.tsx index cddab30c0e..3f1aeaed3d 100644 --- a/services/web/frontend/js/features/ide-react/components/loading.tsx +++ b/services/web/frontend/js/features/ide-react/components/loading.tsx @@ -4,7 +4,7 @@ import useWaitForI18n from '@/shared/hooks/use-wait-for-i18n' import getMeta from '@/utils/meta' import { useConnectionContext } from '../context/connection-context' import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context' -import { LoadingError } from './loading-error' +import { LoadingError, LoadingErrorProps } from './loading-error' type Part = 'initial' | 'render' | 'connection' | 'translations' | 'project' @@ -58,23 +58,30 @@ export const Loading: FC<{ // Use loading text from the server, because i18n will not be ready initially const label = getMeta('ol-loadingText') - const hasError = Boolean(connectionState.error || i18n.error) + const errorCode = connectionState.error ?? (i18n.error ? 'i18n-error' : '') + return +} + +type LoadingUiProps = { + progress: number + label: string + errorCode: LoadingErrorProps['errorCode'] +} + +export const LoadingUI: FC = ({ + progress, + label, + errorCode, +}) => { return (
- {hasError && ( -

- -

- )} + {Boolean(errorCode) && }
) } diff --git a/services/web/frontend/stories/loading/loading-error.stories.tsx b/services/web/frontend/stories/loading/loading-error.stories.tsx deleted file mode 100644 index 753f4e0207..0000000000 --- a/services/web/frontend/stories/loading/loading-error.stories.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react' -import { LoadingError } from '@/features/ide-react/components/loading-error' - -const meta: Meta = { - title: 'Loading Page / Loading Error', - component: LoadingError, -} - -export default meta - -type Story = StoryObj - -export const IoNotLoaded: Story = { - render: () => { - window.metaAttributesCache.set( - 'ol-translationIoNotLoaded', - 'Could not connect to the WebSocket server' - ) - - return - }, -} - -export const UnableToJoin: Story = { - render: () => { - window.metaAttributesCache.set( - 'ol-translationUnableToJoin', - 'Could not connect to the collaboration server' - ) - - return - }, -} diff --git a/services/web/frontend/stories/loading/loading.stories.tsx b/services/web/frontend/stories/loading/loading.stories.tsx index 28a62fe71b..0a643d7224 100644 --- a/services/web/frontend/stories/loading/loading.stories.tsx +++ b/services/web/frontend/stories/loading/loading.stories.tsx @@ -1,25 +1,46 @@ import type { Meta, StoryObj } from '@storybook/react' -import { Loading } from '@/features/ide-react/components/loading' +import { LoadingUI } from '@/features/ide-react/components/loading' import { EditorProviders } from '../../../test/frontend/helpers/editor-providers' import { bsVersionDecorator } from '../../../.storybook/utils/with-bootstrap-switcher' -const meta: Meta = { +const meta: Meta = { title: 'Loading Page / Loading', - component: Loading, + component: LoadingUI, argTypes: { - setLoaded: { action: 'setLoaded' }, + errorCode: { + control: 'select', + options: [ + '', + 'io-not-loaded', + 'unable-to-join', + 'i18n-error', + 'unhandled-error-code', + ], + }, + progress: { control: { type: 'range', min: 0, max: 100 } }, ...bsVersionDecorator.argTypes, }, } export default meta -type Story = StoryObj +type Story = StoryObj + +const errorMessages = { + translationIoNotLoaded: 'Could not connect to the WebSocket server', + translationLoadErrorMessage: 'Could not load translations', + translationUnableToJoin: 'Could not connect to collaboration server', +} export const LoadingPage: Story = { - render: args => ( - - - - ), + render: args => { + for (const [key, value] of Object.entries(errorMessages)) { + window.metaAttributesCache.set(`ol-${key}`, value) + } + return ( + + + + ) + }, } diff --git a/services/web/frontend/stylesheets/bootstrap-5/pages/editor/loading-screen.scss b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/loading-screen.scss index 456c89a91c..198eba0384 100644 --- a/services/web/frontend/stylesheets/bootstrap-5/pages/editor/loading-screen.scss +++ b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/loading-screen.scss @@ -1,3 +1,5 @@ +@use 'sass:math'; + @keyframes blink { 0% { opacity: 0.2; @@ -12,49 +14,62 @@ } } -.loading-screen-brand-container { - width: 15%; - min-width: 200px; - text-align: center; -} - -.loading-screen-brand { - position: relative; +.loading-screen { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; width: 100%; - padding-top: 115.44%; - height: 0; - background: url(../../../../../public/img/ol-brand/overleaf-o-grey.svg) - no-repeat bottom / 100%; + height: 100%; + background-color: #fff; - &::after { - content: ''; - position: absolute; - height: inherit; - right: 0; - bottom: 0; - left: 0; - background: url(../../../../../public/img/ol-brand/overleaf-o.svg) no-repeat - bottom / 100%; - transition: height 0.5s; - } -} - -.loading-screen-label { - margin: 0 !important; - padding-top: var(--spacing-09); - font-family: $font-family-serif; - font-size: var(--font-size-07) !important; - color: var(--content-secondary); -} - -.loading-screen-ellip { - animation: blink 1.4s both infinite; - - &:nth-child(2) { - animation-delay: 0.2s; + .loading-screen-brand-container { + min-width: 200px; } - &:nth-child(3) { - animation-delay: 0.4s; + .loading-screen-brand { + position: relative; + padding-top: math.percentage(math.div(150, 130)); // dimensions of the SVG + height: 0; + background: url(../../../../../public/img/ol-brand/overleaf-o-grey.svg) + no-repeat bottom / 100%; + + &::after { + content: ''; + position: absolute; + height: inherit; + right: 0; + bottom: 0; + left: 0; + background: url(../../../../../public/img/ol-brand/overleaf-o.svg) + no-repeat bottom / 100%; + transition: height 0.5s; + } + } + + .loading-screen-label { + margin: 0; + padding-top: var(--spacing-09); + font-family: $font-family-serif; + font-size: var(--font-size-07); + color: var(--content-secondary); + } + + .loading-screen-ellip { + animation: blink 1.4s both infinite; + + &:nth-child(2) { + animation-delay: 0.2s; + } + + &:nth-child(3) { + animation-delay: 0.4s; + } + } + + .loading-screen-error { + margin: 0; + padding-top: var(--spacing-06); + color: var(--content-danger); } }