From dfd816801d70c91f5328806e7a0842406c76b79a Mon Sep 17 00:00:00 2001 From: Kristina <7614497+khjrtbrg@users.noreply.github.com> Date: Thu, 16 Jul 2026 12:08:25 +0200 Subject: [PATCH] [web] add disconnected state to offline indicator (#35378) GitOrigin-RevId: 692dc16cee738a8b05353717044a4063c3dd8e8a --- .../components/toolbar/offline-indicator.tsx | 44 ++++++++++++--- .../toolbar/offline-indicator.spec.tsx | 54 +++++++++++++++++-- 2 files changed, 87 insertions(+), 11 deletions(-) diff --git a/services/web/frontend/js/features/ide-react/components/toolbar/offline-indicator.tsx b/services/web/frontend/js/features/ide-react/components/toolbar/offline-indicator.tsx index f4e418f05d..818736ea09 100644 --- a/services/web/frontend/js/features/ide-react/components/toolbar/offline-indicator.tsx +++ b/services/web/frontend/js/features/ide-react/components/toolbar/offline-indicator.tsx @@ -1,6 +1,7 @@ import { useTranslation } from 'react-i18next' import MaterialIcon from '@/shared/components/material-icon' import OLTooltip from '@/shared/components/ol/ol-tooltip' +import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context' export function OfflineIndicatorContent() { const { t } = useTranslation() @@ -18,22 +19,49 @@ export function OfflineIndicatorContent() { ) } +export function DisconnectedIndicatorContent() { + const { t } = useTranslation() + return ( +
+ + {t('disconnected')} +
+ ) +} + +function OfflineIndicatorVisual({ outOfSync }: { outOfSync: boolean }) { + if (outOfSync) { + return + } + return +} + +function OfflineIndicatorStatus({ outOfSync }: { outOfSync: boolean }) { + const { t } = useTranslation() + if (outOfSync) { + return {t('disconnected')} + } + return ( + <> + {t('you_re_offline')} + {t('changes_saved_in_browser_sync_when_online')} + + ) +} + export default function OfflineIndicator({ isOffline, }: { isOffline: boolean }) { - const { t } = useTranslation() + const { outOfSync } = useIdeReactContext() + // The live region div below must stay mounted at all times so screen + // readers announce its contents when the offline state changes. return ( <> - {isOffline && } + {isOffline && }
- {isOffline && ( - <> - {t('you_re_offline')} - {t('changes_saved_in_browser_sync_when_online')} - - )} + {isOffline && }
) diff --git a/services/web/test/frontend/features/ide-react/components/toolbar/offline-indicator.spec.tsx b/services/web/test/frontend/features/ide-react/components/toolbar/offline-indicator.spec.tsx index 5f31d65e78..6a934d6fbc 100644 --- a/services/web/test/frontend/features/ide-react/components/toolbar/offline-indicator.spec.tsx +++ b/services/web/test/frontend/features/ide-react/components/toolbar/offline-indicator.spec.tsx @@ -1,19 +1,47 @@ import OfflineIndicator from '@/features/ide-react/components/toolbar/offline-indicator' +import { IdeReactContext } from '@/features/ide-react/context/ide-react-context' + +const mount = ({ + isOffline = false, + outOfSync = false, +}: { + isOffline?: boolean + outOfSync?: boolean +} = {}) => { + cy.mount( + {}, + reportError: () => {}, + projectJoined: true, + permissionsLevel: 'readAndWrite', + setPermissionsLevel: () => {}, + outOfSync, + setOutOfSync: () => {}, + }} + > + + + ) +} describe('', function () { it('renders an empty live region when online', function () { - cy.mount() + mount({ isOffline: false }) cy.findByText('You’re offline').should('not.exist') cy.findByRole('status').should('be.empty') }) it('shows the offline indicator when offline', function () { - cy.mount() + mount({ isOffline: true }) cy.findAllByText('You’re offline').first().should('be.visible') }) it('announces the offline status to screen readers via a live region', function () { - cy.mount() + mount({ isOffline: true }) cy.findByRole('status').within(() => { cy.findByText('You’re offline').should('exist') cy.findByText( @@ -21,4 +49,24 @@ describe('', function () { ).should('exist') }) }) + + it('shows "Disconnected" without a tooltip when out of sync', function () { + mount({ isOffline: true, outOfSync: true }) + cy.findAllByText('Disconnected') + .first() + .should('be.visible') + .trigger('mouseover') + cy.get('#tooltip-offline-indicator-tooltip').should('not.exist') + cy.findByText('You’re offline').should('not.exist') + }) + + it('announces "Disconnected" to screen readers when out of sync', function () { + mount({ isOffline: true, outOfSync: true }) + cy.findByRole('status').within(() => { + cy.findByText('Disconnected').should('exist') + cy.findByText( + 'Your changes are saved in the browser. We’ll sync your work when you’re back online.' + ).should('not.exist') + }) + }) })