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')
+ })
+ })
})