فهرست منبع

Improve landmarks on the Project dashboard and Editor pages (#26751)

* Improve landmarks for the Project Dasbhboard

* Improve landmarks for the IDE page

* Improve landmarks for the new redesigned IDE page

* Sort locales

* Fix typo OlButtonToolbar -> OLButtonToolbar

* Update project navbar translation

* Update labels

* Redundant main landmark

* Fix failing test

* Descriptive name for the rails tab

* Header should not be in a button

* Update translation to Account and help

* Update translation to Project categories and tags

* Add explanations

GitOrigin-RevId: 96712b37bb1306b552de510d9fd8ae890f24309f
Rebeka Dekany 1 سال پیش
والد
کامیت
1b4cbd4efb
41فایلهای تغییر یافته به همراه331 افزوده شده و 166 حذف شده
  1. 20 4
      server-ce/test/git-bridge.spec.ts
  2. 15 3
      server-ce/test/sandboxed-compiles.spec.ts
  3. 30 6
      server-ce/test/templates.spec.ts
  4. 8 2
      server-ce/test/upgrading.spec.ts
  5. 1 4
      services/web/app/views/layout/fat-footer-website-redesign.pug
  6. 1 4
      services/web/app/views/layout/fat-footer.pug
  7. 1 0
      services/web/app/views/layout/navbar-marketing-bootstrap-5.pug
  8. 1 0
      services/web/app/views/layout/navbar-marketing.pug
  9. 3 1
      services/web/app/views/layout/navbar-website-redesign.pug
  10. 1 1
      services/web/app/views/project/ide-react.pug
  11. 13 4
      services/web/frontend/extracted-translations.json
  12. 1 1
      services/web/frontend/js/features/chat/components/chat-pane.tsx
  13. 2 6
      services/web/frontend/js/features/editor-navigation-toolbar/components/toolbar-header.tsx
  14. 1 1
      services/web/frontend/js/features/history/components/change-list/change-list.tsx
  15. 4 0
      services/web/frontend/js/features/ide-react/components/editor-and-pdf.tsx
  16. 4 2
      services/web/frontend/js/features/ide-react/components/editor-sidebar.tsx
  17. 4 1
      services/web/frontend/js/features/ide-react/components/history-sidebar.tsx
  18. 1 1
      services/web/frontend/js/features/ide-redesign/components/chat/chat.tsx
  19. 2 2
      services/web/frontend/js/features/ide-redesign/components/integrations-panel/integration-card.tsx
  20. 4 0
      services/web/frontend/js/features/ide-redesign/components/main-layout.tsx
  21. 8 3
      services/web/frontend/js/features/ide-redesign/components/pdf-preview/pdf-preview-hybrid-toolbar.tsx
  22. 2 2
      services/web/frontend/js/features/ide-redesign/components/rail-panel-header.tsx
  23. 13 5
      services/web/frontend/js/features/ide-redesign/components/rail.tsx
  24. 6 4
      services/web/frontend/js/features/ide-redesign/components/toolbar/toolbar.tsx
  25. 2 2
      services/web/frontend/js/features/outline/components/outline-pane.tsx
  26. 15 0
      services/web/frontend/js/features/pdf-preview/components/pdf-js-viewer.tsx
  27. 7 4
      services/web/frontend/js/features/pdf-preview/components/pdf-preview-hybrid-toolbar.tsx
  28. 2 2
      services/web/frontend/js/features/preview/components/preview-log-entry-header.tsx
  29. 7 2
      services/web/frontend/js/features/project-list/components/notifications/user-notifications.tsx
  30. 53 51
      services/web/frontend/js/features/project-list/components/project-list-ds-nav.tsx
  31. 1 1
      services/web/frontend/js/features/project-list/components/project-list-root.tsx
  32. 23 18
      services/web/frontend/js/features/project-list/components/sidebar/sidebar-ds-nav.tsx
  33. 0 0
      services/web/frontend/js/features/project-list/components/sidebar/sidebar.tsx
  34. 4 2
      services/web/frontend/js/features/project-list/components/title/project-list-title.tsx
  35. 2 2
      services/web/frontend/js/features/ui/components/bootstrap-5/footer/fat-footer-base.tsx
  36. 1 5
      services/web/frontend/js/features/ui/components/bootstrap-5/footer/fat-footer.tsx
  37. 2 1
      services/web/frontend/js/features/ui/components/bootstrap-5/navbar/default-navbar.tsx
  38. 6 7
      services/web/frontend/stylesheets/bootstrap-5/pages/project-list.scss
  39. 12 4
      services/web/locales/en.json
  40. 9 5
      services/web/test/frontend/components/pdf-preview/pdf-js-viewer.spec.tsx
  41. 39 3
      services/web/test/frontend/features/history/components/change-list.spec.tsx

+ 20 - 4
server-ce/test/git-bridge.spec.ts

@@ -85,7 +85,11 @@ describe('git-bridge', function () {
     it('should render the git-bridge UI in the editor', function () {
       maybeClearAllTokens()
       createProject('git').as('projectId')
-      cy.get('header').findByText('Menu').click()
+      cy.findByRole('navigation', {
+        name: /Project actions/i,
+      })
+        .findByRole('button', { name: /Menu/i })
+        .click()
       cy.findByText('Sync')
       cy.findByText('Git').click()
       cy.findByTestId('git-bridge-modal').within(() => {
@@ -100,7 +104,11 @@ describe('git-bridge', function () {
 
       // Re-open
       cy.url().then(url => cy.visit(url))
-      cy.get('header').findByText('Menu').click()
+      cy.findByRole('navigation', {
+        name: /Project actions/i,
+      })
+        .findByRole('button', { name: /Menu/i })
+        .click()
       cy.findByText('Git').click()
       cy.findByTestId('git-bridge-modal').within(() => {
         cy.get('@projectId').then(id => {
@@ -188,7 +196,11 @@ describe('git-bridge', function () {
     function checkGitAccess(access: 'readOnly' | 'readAndWrite') {
       const recompile = throttledRecompile()
 
-      cy.get('header').findByText('Menu').click()
+      cy.findByRole('navigation', {
+        name: /Project actions/i,
+      })
+        .findByRole('button', { name: /Menu/i })
+        .click()
       cy.findByText('Sync')
       cy.findByText('Git').click()
       cy.get('@projectId').then(projectId => {
@@ -370,7 +382,11 @@ Hello world
     it('should not render the git-bridge UI in the editor', function () {
       login('user@example.com')
       createProject('maybe git')
-      cy.get('header').findByText('Menu').click()
+      cy.findByRole('navigation', {
+        name: /Project actions/i,
+      })
+        .findByRole('button', { name: /Menu/i })
+        .click()
       cy.findByText('Word Count') // wait for lazy loading
       cy.findByText('Sync').should('not.exist')
       cy.findByText('Git').should('not.exist')

+ 15 - 3
server-ce/test/sandboxed-compiles.spec.ts

@@ -37,7 +37,11 @@ describe('SandboxedCompiles', function () {
       cy.findByText(/This is pdfTeX, Version .+ \(TeX Live 2023\) /)
 
       cy.log('Switch TeXLive version from 2023 to 2022')
-      cy.get('header').findByText('Menu').click()
+      cy.findByRole('navigation', {
+        name: /Project actions/i,
+      })
+        .findByRole('button', { name: /Menu/i })
+        .click()
       cy.findByText(LABEL_TEX_LIVE_VERSION)
         .parent()
         .findByText('2023')
@@ -213,7 +217,11 @@ describe('SandboxedCompiles', function () {
       cy.findByText(/This is pdfTeX/)
 
       cy.log('Switch compiler to from pdfLaTeX to XeLaTeX')
-      cy.get('header').findByText('Menu').click()
+      cy.findByRole('navigation', {
+        name: /Project actions/i,
+      })
+        .findByRole('button', { name: /Menu/i })
+        .click()
       cy.findByText('Compiler')
         .parent()
         .findByText('pdfLaTeX')
@@ -245,7 +253,11 @@ describe('SandboxedCompiles', function () {
       cy.findByText(/This is pdfTeX, Version .+ \(TeX Live 2025\) /)
 
       cy.log('Check that there is no TeX Live version toggle')
-      cy.get('header').findByText('Menu').click()
+      cy.findByRole('navigation', {
+        name: /Project actions/i,
+      })
+        .findByRole('button', { name: /Menu/i })
+        .click()
       cy.findByText('Word Count') // wait for lazy loading
       cy.findByText(LABEL_TEX_LIVE_VERSION).should('not.exist')
     })

+ 30 - 6
server-ce/test/templates.spec.ts

@@ -57,7 +57,11 @@ describe('Templates', () => {
       cy.visit('/')
       createProject(name).as('templateProjectId')
 
-      cy.get('header').findByText('Menu').click()
+      cy.findByRole('navigation', {
+        name: /Project actions/i,
+      })
+        .findByRole('button', { name: /Menu/i })
+        .click()
       cy.findByText('Manage Template').click()
 
       cy.findByText('Template Description')
@@ -136,7 +140,11 @@ describe('Templates', () => {
       cy.get('@templateProjectId').then(projectId =>
         cy.visit(`/project/${projectId}`)
       )
-      cy.get('header').findByText('Menu').click()
+      cy.findByRole('navigation', {
+        name: /Project actions/i,
+      })
+        .findByRole('button', { name: /Menu/i })
+        .click()
       cy.findByText('Manage Template').click()
       cy.findByText('Publish').click()
       cy.findByText('Unpublish', { timeout: 10_000 })
@@ -161,7 +169,11 @@ describe('Templates', () => {
       cy.findByText('Open as Template').click()
       cy.url().should('match', /\/project\/[a-f0-9]{24}$/)
       cy.get('.project-name').findByText(name)
-      cy.get('header').findByText('Menu').click()
+      cy.findByRole('navigation', {
+        name: /Project actions/i,
+      })
+        .findByRole('button', { name: /Menu/i })
+        .click()
       cy.findByText('Word Count') // wait for lazy loading
       cy.findByText('Manage Template').should('not.exist')
 
@@ -180,7 +192,11 @@ describe('Templates', () => {
       cy.get('@templateProjectId').then(projectId =>
         cy.visit(`/project/${projectId}`)
       )
-      cy.get('header').findByText('Menu').click()
+      cy.findByRole('navigation', {
+        name: /Project actions/i,
+      })
+        .findByRole('button', { name: /Menu/i })
+        .click()
       cy.findByText('Manage Template').click()
       cy.findByText('Unpublish')
 
@@ -191,7 +207,11 @@ describe('Templates', () => {
       cy.get('@templateProjectId').then(projectId =>
         cy.visit(`/project/${projectId}`)
       )
-      cy.get('header').findByText('Menu').click()
+      cy.findByRole('navigation', {
+        name: /Project actions/i,
+      })
+        .findByRole('button', { name: /Menu/i })
+        .click()
       cy.findByText('Manage Template').click()
       cy.findByText('Unpublish').click()
       cy.findByText('Publish')
@@ -217,7 +237,11 @@ describe('Templates', () => {
       cy.visit('/')
       createProject('maybe templates')
 
-      cy.get('header').findByText('Menu').click()
+      cy.findByRole('navigation', {
+        name: /Project actions/i,
+      })
+        .findByRole('button', { name: /Menu/i })
+        .click()
       cy.findByText('Word Count') // wait for lazy loading
       cy.findByText('Manage Template').should('not.exist')
 

+ 8 - 2
server-ce/test/upgrading.spec.ts

@@ -55,7 +55,11 @@ describe('Upgrading', function () {
 
         cy.log('Trigger full flush')
         recompile()
-        cy.get('header').findByText('Menu').click()
+        cy.findByRole('navigation', {
+          name: /Project Layout, Sharing, and Submission/i,
+        })
+          .findByRole('button', { name: /Menu/i })
+          .click()
         cy.findByText('Source').click()
         cy.get('.left-menu-modal-backdrop').click({ force: true })
       }
@@ -116,7 +120,9 @@ describe('Upgrading', function () {
       openProjectByName(PROJECT_NAME)
 
       cy.url().should('match', /\/project\/[a-fA-F0-9]{24}/)
-      cy.findByRole('navigation').within(() => {
+      cy.findByRole('navigation', {
+        name: /Project actions/i,
+      }).within(() => {
         cy.findByText(PROJECT_NAME)
       })
       const recompile = throttledRecompile()

+ 1 - 4
services/web/app/views/layout/fat-footer-website-redesign.pug

@@ -1,8 +1,5 @@
 footer.fat-footer.hidden-print.website-redesign-fat-footer
-	.fat-footer-container(
-		role='navigation'
-		aria-label=translate('footer_navigation')
-	)
+	.fat-footer-container
 		.fat-footer-sections(class={hidden: hideFatFooter})
 			#footer-brand.footer-section
 				a.footer-brand(href='/' aria-label=settings.appName)

+ 1 - 4
services/web/app/views/layout/fat-footer.pug

@@ -1,8 +1,5 @@
 footer.fat-footer.hidden-print
-	.fat-footer-container(
-		role='navigation'
-		aria-label=translate('footer_navigation')
-	)
+	.fat-footer-container
 		.fat-footer-sections(class={hidden: hideFatFooter})
 			#footer-brand.footer-section
 				a.footer-brand(href='/' aria-label=settings.appName)

+ 1 - 0
services/web/app/views/layout/navbar-marketing-bootstrap-5.pug

@@ -5,6 +5,7 @@ nav.navbar.navbar-default.navbar-main.navbar-expand-lg(
 	class={
 		'website-redesign-navbar': isWebsiteRedesign,
 	}
+	aria-label=translate('primary')
 )
 	.container-fluid.navbar-container
 		.navbar-header

+ 1 - 0
services/web/app/views/layout/navbar-marketing.pug

@@ -2,6 +2,7 @@ nav.navbar.navbar-default.navbar-main(
 	class={
 		'website-redesign-navbar': isWebsiteRedesign,
 	}
+	aria-label=translate('primary')
 )
 	.container-fluid
 		.navbar-header

+ 3 - 1
services/web/app/views/layout/navbar-website-redesign.pug

@@ -1,4 +1,6 @@
-nav.navbar.navbar-default.navbar-main.website-redesign-navbar
+nav.navbar.navbar-default.navbar-main.website-redesign-navbar(
+	aria-label=translate('primary')
+)
 	.container-fluid
 		.navbar-header
 			if typeof suppressNavbarRight == 'undefined'

+ 1 - 1
services/web/app/views/project/ide-react.pug

@@ -12,7 +12,7 @@ block entrypointVar
 	- entrypoint = 'pages/ide'
 
 block content
-	main#ide-root
+	#ide-root
 		.loading-screen
 			.loading-screen-brand-container
 				.loading-screen-brand(style='height: 20%')

+ 13 - 4
services/web/frontend/extracted-translations.json

@@ -53,6 +53,7 @@
   "account_billed_manually": "",
   "account_has_been_link_to_institution_account": "",
   "account_has_past_due_invoice_change_plan_warning": "",
+  "account_help": "",
   "account_managed_by_group_administrator": "",
   "account_not_linked_to_dropbox": "",
   "account_settings": "",
@@ -566,6 +567,7 @@
   "fast": "",
   "fast_draft": "",
   "features_like_track_changes": "",
+  "feedback": "",
   "figure": "",
   "file": "",
   "file_action_created": "",
@@ -585,6 +587,7 @@
   "file_size": "",
   "file_tree": "",
   "files_cannot_include_invalid_characters": "",
+  "files_collaboration_integrations_logs": "",
   "files_selected": "",
   "filter_projects": "",
   "find": "",
@@ -605,7 +608,6 @@
   "font_size": "",
   "footer_about_us": "",
   "footer_contact_us": "",
-  "footer_navigation": "",
   "footnotes": "",
   "for_business": "",
   "for_government": "",
@@ -729,6 +731,7 @@
   "headers": "",
   "help": "",
   "help_articles_matching": "",
+  "help_editor_settings": "",
   "help_improve_overleaf_fill_out_this_survey": "",
   "help_improve_screen_reader_fill_out_this_survey": "",
   "help_shape_the_future_of_overleaf": "",
@@ -997,7 +1000,6 @@
   "main_bibliography_file_for_this_project": "",
   "main_document": "",
   "main_file_not_found": "",
-  "main_navigation": "",
   "make_a_copy": "",
   "make_email_primary_description": "",
   "make_owner": "",
@@ -1136,6 +1138,7 @@
   "not_a_student": "",
   "not_managed": "",
   "not_now": "",
+  "notification": "",
   "notification_personal_and_group_subscriptions": "",
   "notification_project_invite_accepted_message": "",
   "notification_project_invite_message": "",
@@ -1220,6 +1223,7 @@
   "payment_error_update_payment_method": "",
   "payment_provider_unreachable_error": "",
   "payment_summary": "",
+  "pdf": "",
   "pdf_compile_in_progress_error": "",
   "pdf_compile_rate_limit_hit": "",
   "pdf_compile_try_again": "",
@@ -1229,6 +1233,7 @@
   "pdf_only_hide_editor": "",
   "pdf_preview": "",
   "pdf_preview_error": "",
+  "pdf_preview_logs": "",
   "pdf_rendering_error": "",
   "pdf_unavailable_for_download": "",
   "pdf_viewer": "",
@@ -1290,6 +1295,7 @@
   "primarily_work_study_question_nonprofit_ngo": "",
   "primarily_work_study_question_other": "",
   "primarily_work_study_question_university_school": "",
+  "primary": "",
   "primary_certificate": "",
   "priority_support": "",
   "privacy_and_terms": "",
@@ -1302,14 +1308,17 @@
   "processing_uppercase": "",
   "professional": "",
   "progress_bar_percentage": "",
+  "project_actions": "",
   "project_approaching_file_limit": "",
+  "project_categories_tags": "",
   "project_figure_modal": "",
   "project_files": "",
+  "project_files_history": "",
+  "project_files_outline": "",
   "project_flagged_too_many_compiles": "",
   "project_has_too_many_files": "",
-  "project_history_list": "",
+  "project_history_labels": "",
   "project_last_published_at": "",
-  "project_layout_sharing_submission": "",
   "project_linked_to": "",
   "project_name": "",
   "project_not_linked_to_github": "",

+ 1 - 1
services/web/frontend/js/features/chat/components/chat-pane.tsx

@@ -72,7 +72,7 @@ const ChatPane = React.memo(function ChatPane() {
   }
 
   return (
-    <aside className="chat">
+    <aside className="chat" aria-label={t('chat')}>
       <InfiniteScroll
         atEnd={atEnd}
         className="messages"

+ 2 - 6
services/web/frontend/js/features/editor-navigation-toolbar/components/toolbar-header.tsx

@@ -88,11 +88,7 @@ const ToolbarHeader = React.memo(function ToolbarHeader({
   const shouldDisplayPublishButton = hasPublishPermissions && PublishButton
 
   return (
-    <header
-      className="toolbar toolbar-header"
-      role="navigation"
-      aria-label={t('project_layout_sharing_submission')}
-    >
+    <nav className="toolbar toolbar-header" aria-label={t('project_actions')}>
       <div className="toolbar-left">
         <MenuButton onClick={onShowLeftMenuClick} />
         {cobranding && cobranding.logoImgUrl && (
@@ -158,7 +154,7 @@ const ToolbarHeader = React.memo(function ToolbarHeader({
           </>
         )}
       </div>
-    </header>
+    </nav>
   )
 })
 

+ 1 - 1
services/web/frontend/js/features/history/components/change-list/change-list.tsx

@@ -9,7 +9,7 @@ function ChangeList() {
   const { t } = useTranslation()
 
   return (
-    <aside className="change-list" aria-label={t('project_history_list')}>
+    <aside className="change-list" aria-label={t('project_history_labels')}>
       <div className="history-header history-toggle-switch-container">
         <ToggleSwitch labelsOnly={labelsOnly} setLabelsOnly={setLabelsOnly} />
       </div>

+ 4 - 0
services/web/frontend/js/features/ide-react/components/editor-and-pdf.tsx

@@ -55,6 +55,8 @@ export const EditorAndPdf: FC = () => {
           'ide-panel-group-resizing': resizing,
           hidden: !editorIsOpen,
         })}
+        tagName="section"
+        aria-label={t('editor')}
       >
         {selectedEntityCount === 0 && <NoSelectionPane />}
         {selectedEntityCount === 1 && openEntity?.type === 'fileRef' && (
@@ -105,6 +107,8 @@ export const EditorAndPdf: FC = () => {
         onCollapse={handlePdfPaneCollapse}
         onExpand={handlePdfPaneExpand}
         className="ide-react-panel"
+        tagName="section"
+        aria-label={t('pdf_preview_logs')}
       >
         <PdfPreview />
         {/* ensure that "sync to code" is available in PDF only layout */}

+ 4 - 2
services/web/frontend/js/features/ide-react/components/editor-sidebar.tsx

@@ -7,6 +7,7 @@ import { OutlineContainer } from '@/features/outline/components/outline-containe
 import { useOutlinePane } from '@/features/ide-react/hooks/use-outline-pane'
 import React, { ElementType } from 'react'
 import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
+import { t } from 'i18next'
 
 const editorSidebarComponents = importOverleafModules(
   'editorSidebarComponents'
@@ -18,10 +19,11 @@ export default function EditorSidebar() {
   const { outlineEnabled, outlinePanelRef } = useOutlinePane()
 
   return (
-    <aside
+    <nav
       className={classNames('ide-react-editor-sidebar', {
         hidden: view === 'history',
       })}
+      aria-label={t('project_files_outline')}
     >
       {editorSidebarComponents.map(
         ({ import: { default: Component }, path }) => (
@@ -53,6 +55,6 @@ export default function EditorSidebar() {
           <OutlineContainer />
         </Panel>
       </PanelGroup>
-    </aside>
+    </nav>
   )
 }

+ 4 - 1
services/web/frontend/js/features/ide-react/components/history-sidebar.tsx

@@ -1,10 +1,13 @@
 import React from 'react'
+import { useTranslation } from 'react-i18next'
 
 export function HistorySidebar() {
+  const { t } = useTranslation()
   return (
-    <aside
+    <nav
       id="history-file-tree"
       className="ide-react-editor-sidebar history-file-tree"
+      aria-label={t('project_files_history')}
     />
   )
 }

+ 1 - 1
services/web/frontend/js/features/ide-redesign/components/chat/chat.tsx

@@ -69,7 +69,7 @@ export const ChatPane = () => {
     <div className="chat-panel">
       <RailPanelHeader title={t('collaborator_chat')} />
       <div className="chat-wrapper">
-        <aside className="chat">
+        <aside className="chat" aria-label={t('chat')}>
           <InfiniteScroll
             atEnd={atEnd}
             className="messages"

+ 2 - 2
services/web/frontend/js/features/ide-redesign/components/integrations-panel/integration-card.tsx

@@ -22,7 +22,7 @@ export default function IntegrationCard({
       <div className="integrations-panel-card-contents">
         {icon}
         <div className="integrations-panel-card-inner">
-          <header className="integrations-panel-card-header">
+          <div className="integrations-panel-card-header">
             <div className="integrations-panel-card-title">{title}</div>
             {showPaywallBadge && (
               <OLBadge
@@ -33,7 +33,7 @@ export default function IntegrationCard({
                 {t('premium')}
               </OLBadge>
             )}
-          </header>
+          </div>
           <p className="integrations-panel-card-description">{description}</p>
         </div>
       </div>

+ 4 - 0
services/web/frontend/js/features/ide-redesign/components/main-layout.tsx

@@ -68,6 +68,8 @@ export default function MainLayout() {
                 })}
                 minSize={5}
                 defaultSize={50}
+                tagName="section"
+                aria-label={t('editor')}
               >
                 <div className="ide-redesign-editor-container">
                   <EditorPanel />
@@ -108,6 +110,8 @@ export default function MainLayout() {
                 ref={pdfPanelRef}
                 onExpand={handlePdfPaneExpand}
                 onCollapse={handlePdfPaneCollapse}
+                tagName="section"
+                aria-label={t('pdf_preview')}
               >
                 <PdfPreview />
                 {pdfLayout === 'flat' && view === 'pdf' && (

+ 8 - 3
services/web/frontend/js/features/ide-redesign/components/pdf-preview/pdf-preview-hybrid-toolbar.tsx

@@ -1,14 +1,19 @@
 import { memo } from 'react'
-import OlButtonToolbar from '@/features/ui/components/ol/ol-button-toolbar'
+import { useTranslation } from 'react-i18next'
+import OLButtonToolbar from '@/features/ui/components/ol/ol-button-toolbar'
 import PdfCompileButton from '@/features/pdf-preview/components/pdf-compile-button'
 import PdfHybridDownloadButton from '@/features/pdf-preview/components/pdf-hybrid-download-button'
 import { DetachedSynctexControl } from '@/features/pdf-preview/components/detach-synctex-control'
 import SwitchToEditorButton from '@/features/pdf-preview/components/switch-to-editor-button'
 
 function PdfPreviewHybridToolbar() {
+  const { t } = useTranslation()
   // TODO: add detached pdf logic
   return (
-    <OlButtonToolbar className="toolbar toolbar-pdf toolbar-pdf-hybrid">
+    <OLButtonToolbar
+      className="toolbar toolbar-pdf toolbar-pdf-hybrid"
+      aria-label={t('pdf')}
+    >
       <div className="toolbar-pdf-left">
         <PdfCompileButton />
         <PdfHybridDownloadButton />
@@ -19,7 +24,7 @@ function PdfPreviewHybridToolbar() {
         <DetachedSynctexControl />
         {/* TODO: should we have code check? */}
       </div>
-    </OlButtonToolbar>
+    </OLButtonToolbar>
   )
 }
 

+ 2 - 2
services/web/frontend/js/features/ide-redesign/components/rail-panel-header.tsx

@@ -14,7 +14,7 @@ export default function RailPanelHeader({
   const { t } = useTranslation()
   const { handlePaneCollapse } = useRailContext()
   return (
-    <header className="rail-panel-header">
+    <div className="rail-panel-header">
       <h4 className="rail-panel-title">{title}</h4>
 
       <div className="rail-panel-header-actions">
@@ -33,6 +33,6 @@ export default function RailPanelHeader({
           />
         </OLTooltip>
       </div>
-    </header>
+    </div>
   )
 }

+ 13 - 5
services/web/frontend/js/features/ide-redesign/components/rail.tsx

@@ -258,7 +258,13 @@ export const RailLayout = () => {
       onSelect={onTabSelect}
       id="ide-rail-tabs"
     >
-      <div className={classNames('ide-rail', { hidden: isHistoryView })}>
+      {/* The <Nav> element is a "div" and has a "role="tablist"".
+          But it should be identified as a navigation landmark.
+          Therefore, we nest them: the parent <nav> is the landmark, and its child gets the "role="tablist"". */}
+      <nav
+        className={classNames('ide-rail', { hidden: isHistoryView })}
+        aria-label={t('files_collaboration_integrations_logs')}
+      >
         <Nav activeKey={selectedTab} className="ide-rail-tabs-nav">
           {railTabs
             .filter(({ hide }) => !hide)
@@ -274,11 +280,13 @@ export const RailLayout = () => {
               />
             ))}
           <div className="flex-grow-1" />
-          {railActions?.map(action => (
-            <RailActionElement key={action.key} action={action} />
-          ))}
+          <nav aria-label={t('help_editor_settings')}>
+            {railActions?.map(action => (
+              <RailActionElement key={action.key} action={action} />
+            ))}
+          </nav>
         </Nav>
-      </div>
+      </nav>
       <Panel
         id={
           newErrorlogs

+ 6 - 4
services/web/frontend/js/features/ide-redesign/components/toolbar/toolbar.tsx

@@ -1,3 +1,4 @@
+import { useTranslation } from 'react-i18next'
 import { ToolbarMenuBar } from './menu-bar'
 import { ToolbarProjectTitle } from './project-title'
 import { OnlineUsers } from './online-users'
@@ -23,6 +24,7 @@ export const Toolbar = () => {
   const { view, restoreView } = useLayoutContext()
   const { cobranding } = useEditorContext()
   const { permissionsLevel } = useIdeReactContext()
+  const { t } = useTranslation()
   const shouldDisplaySubmitButton =
     (permissionsLevel === 'owner' || permissionsLevel === 'readAndWrite') &&
     SubmitProjectButton
@@ -34,18 +36,18 @@ export const Toolbar = () => {
 
   if (view === 'history') {
     return (
-      <div className="ide-redesign-toolbar">
+      <nav className="ide-redesign-toolbar" aria-label={t('project_actions')}>
         <div className="d-flex align-items-center">
           <BackToEditorButton onClick={handleBackToEditorClick} />
         </div>
         <ToolbarProjectTitle />
         <div /> {/* Empty div used for spacing */}
-      </div>
+      </nav>
     )
   }
 
   return (
-    <div className="ide-redesign-toolbar">
+    <nav className="ide-redesign-toolbar" aria-label={t('project_actions')}>
       <div className="ide-redesign-toolbar-menu">
         <ToolbarLogos cobranding={cobranding} />
         <ToolbarMenuBar />
@@ -62,6 +64,6 @@ export const Toolbar = () => {
         <ShareProjectButton />
         {getMeta('ol-showUpgradePrompt') && <UpgradeButton />}
       </div>
-    </div>
+    </nav>
   )
 }

+ 2 - 2
services/web/frontend/js/features/outline/components/outline-pane.tsx

@@ -39,7 +39,7 @@ const OutlinePane = React.memo<{
 
   return (
     <div className={headerClasses}>
-      <header className="outline-header">
+      <div className="outline-header">
         <OutlineToggleButton
           toggleExpanded={toggleExpanded}
           expanded={expanded}
@@ -47,7 +47,7 @@ const OutlinePane = React.memo<{
           isPartial={isPartial}
           isTexFile={isTexFile}
         />
-      </header>
+      </div>
       {isOpen && (
         <div className="outline-body">
           <OutlineRoot

+ 15 - 0
services/web/frontend/js/features/pdf-preview/components/pdf-js-viewer.tsx

@@ -487,6 +487,21 @@ function PdfJsViewer({ url, pdfFile }: PdfJsViewerProps) {
   const toolbarInfoLoaded =
     rawScale !== null && page !== null && totalPages !== null
 
+  // Remove the 'region' role from each PDF page container.
+  // This prevents polluting the landmark navigation menu for every page,
+  // which creates a poor screen reader experience. Page navigation should be handled
+  // by the toolbar controls.
+  useEffect(() => {
+    if (!initialised || !pdfJsWrapper) return
+
+    const pageElements = pdfJsWrapper.container.querySelectorAll(
+      'div[data-page-number][role="region"]'
+    )
+    pageElements.forEach(element => {
+      element.removeAttribute('role')
+    })
+  }, [initialised, pdfJsWrapper])
+
   /* eslint-disable jsx-a11y/no-noninteractive-tabindex */
   /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
   return (

+ 7 - 4
services/web/frontend/js/features/pdf-preview/components/pdf-preview-hybrid-toolbar.tsx

@@ -1,6 +1,6 @@
 import { memo, useState, useEffect, useRef } from 'react'
-import OlButtonToolbar from '@/features/ui/components/ol/ol-button-toolbar'
 import { useTranslation } from 'react-i18next'
+import OLButtonToolbar from '@/features/ui/components/ol/ol-button-toolbar'
 import { useLayoutContext } from '@/shared/context/layout-context'
 import PdfCompileButton from './pdf-compile-button'
 import SwitchToEditorButton from './switch-to-editor-button'
@@ -15,7 +15,7 @@ const ORPHAN_UI_TIMEOUT_MS = 5000
 
 function PdfPreviewHybridToolbar() {
   const { detachRole, detachIsLinked } = useLayoutContext()
-
+  const { t } = useTranslation()
   const uiTimeoutRef = useRef<number>()
   const [orphanPdfTabAfterDelay, setOrphanPdfTabAfterDelay] = useState(false)
 
@@ -47,9 +47,12 @@ function PdfPreviewHybridToolbar() {
   }
 
   return (
-    <OlButtonToolbar className="toolbar toolbar-pdf toolbar-pdf-hybrid">
+    <OLButtonToolbar
+      className="toolbar toolbar-pdf toolbar-pdf-hybrid"
+      aria-label={t('pdf')}
+    >
       {ToolbarInner}
-    </OlButtonToolbar>
+    </OLButtonToolbar>
   )
 }
 

+ 2 - 2
services/web/frontend/js/features/preview/components/preview-log-entry-header.tsx

@@ -99,7 +99,7 @@ function PreviewLogEntryHeader({
   const headerTitleText = logType ? `${logType} ${headerTitle}` : headerTitle
 
   return (
-    <header className={logEntryHeaderClasses}>
+    <div className={logEntryHeaderClasses}>
       {headerIcon ? (
         <div className="log-entry-header-icon-container">{headerIcon}</div>
       ) : null}
@@ -116,7 +116,7 @@ function PreviewLogEntryHeader({
       ) : (
         locationLink
       )}
-    </header>
+    </div>
   )
 }
 

+ 7 - 2
services/web/frontend/js/features/project-list/components/notifications/user-notifications.tsx

@@ -1,4 +1,5 @@
 import { JSXElementConstructor } from 'react'
+import { useTranslation } from 'react-i18next'
 import Common from './groups/common'
 import Institution from './groups/institution'
 import ConfirmEmail from './groups/confirm-email'
@@ -32,9 +33,13 @@ const USGovBanner: JSXElementConstructor<Record<string, never>> =
 function UserNotifications() {
   const groupSubscriptionsPendingEnrollment =
     getMeta('ol-groupSubscriptionsPendingEnrollment') || []
+  const { t } = useTranslation()
 
   return (
-    <div className="user-notifications notification-list">
+    <section
+      className="user-notifications notification-list"
+      aria-label={t('notification')}
+    >
       <ul className="list-unstyled">
         {EnrollmentNotification &&
           groupSubscriptionsPendingEnrollment.map(subscription => (
@@ -58,7 +63,7 @@ function UserNotifications() {
 
         {isDeprecatedBrowser() && <DeprecatedBrowser />}
       </ul>
-    </div>
+    </section>
   )
 }
 

+ 53 - 51
services/web/frontend/js/features/project-list/components/project-list-ds-nav.tsx

@@ -62,73 +62,75 @@ export function ProjectListDsNav() {
         overleafLogo={overleafLogo}
         showCloseIcon
       />
-      <main className="project-list-wrapper">
+      <div className="project-list-wrapper">
         <SidebarDsNav />
         <div className="project-ds-nav-content-and-messages">
           <div className="project-ds-nav-content">
             <div className="project-ds-nav-main">
               {error ? <DashApiError /> : ''}
               <UserNotifications />
-              <div className="project-list-header-row">
-                <ProjectListTitle
-                  filter={filter}
-                  selectedTag={selectedTag}
-                  selectedTagId={selectedTagId}
-                  className="text-truncate d-none d-md-block"
-                />
-                <div className="project-tools">
-                  <div className="d-none d-md-block">
-                    {selectedProjects.length === 0 ? (
+              <main aria-labelledby="main-content">
+                <div className="project-list-header-row">
+                  <ProjectListTitle
+                    filter={filter}
+                    selectedTag={selectedTag}
+                    selectedTagId={selectedTagId}
+                    className="text-truncate d-none d-md-block"
+                  />
+                  <div className="project-tools">
+                    <div className="d-none d-md-block">
+                      {selectedProjects.length === 0 ? (
+                        <CurrentPlanWidget />
+                      ) : (
+                        <ProjectTools />
+                      )}
+                    </div>
+                    <div className="d-md-none">
                       <CurrentPlanWidget />
-                    ) : (
-                      <ProjectTools />
-                    )}
+                    </div>
                   </div>
-                  <div className="d-md-none">
-                    <CurrentPlanWidget />
-                  </div>
-                </div>
-              </div>
-              <div className="project-ds-nav-project-list">
-                <OLRow className="d-none d-md-block">
-                  <OLCol lg={7}>
-                    <SearchForm
-                      inputValue={searchText}
-                      setInputValue={setSearchText}
-                      filter={filter}
-                      selectedTag={selectedTag}
-                    />
-                  </OLCol>
-                </OLRow>
-                <div className="project-list-sidebar-survey-wrapper d-md-none">
-                  {/* Omit the survey card in mobile view for now */}
                 </div>
-                <div className="mt-1 d-md-none">
-                  <div
-                    role="toolbar"
-                    className="projects-toolbar"
-                    aria-label={t('projects')}
-                  >
-                    <ProjectsDropdown />
-                    <SortByDropdown />
+                <div className="project-ds-nav-project-list">
+                  <OLRow className="d-none d-md-block">
+                    <OLCol lg={7}>
+                      <SearchForm
+                        inputValue={searchText}
+                        setInputValue={setSearchText}
+                        filter={filter}
+                        selectedTag={selectedTag}
+                      />
+                    </OLCol>
+                  </OLRow>
+                  <div className="project-list-sidebar-survey-wrapper d-md-none">
+                    {/* Omit the survey card in mobile view for now */}
+                  </div>
+                  <div className="mt-1 d-md-none">
+                    <div
+                      role="toolbar"
+                      className="projects-toolbar"
+                      aria-label={t('projects')}
+                    >
+                      <ProjectsDropdown />
+                      <SortByDropdown />
+                    </div>
+                  </div>
+                  <div className="mt-3">
+                    <TableContainer bordered>
+                      {tableTopArea}
+                      <ProjectListTable />
+                    </TableContainer>
+                  </div>
+                  <div className="mt-3">
+                    <LoadMore />
                   </div>
                 </div>
-                <div className="mt-3">
-                  <TableContainer bordered>
-                    {tableTopArea}
-                    <ProjectListTable />
-                  </TableContainer>
-                </div>
-                <div className="mt-3">
-                  <LoadMore />
-                </div>
-              </div>
+              </main>
             </div>
             <Footer {...footerProps} />
           </div>
           <CookieBanner />
         </div>
-      </main>
+      </div>
     </div>
   )
 }

+ 1 - 1
services/web/frontend/js/features/project-list/components/project-list-root.tsx

@@ -50,8 +50,8 @@ function DefaultNavbarAndFooter({ children }: { children: ReactNode }) {
     <>
       <DefaultNavbar {...navbarProps} />
       <main
-        id="main-content"
         className="content content-alt project-list-react"
+        aria-labelledby="main-content"
       >
         {children}
       </main>

+ 23 - 18
services/web/frontend/js/features/project-list/components/sidebar/sidebar-ds-nav.tsx

@@ -1,4 +1,5 @@
 import { useState } from 'react'
+import { useTranslation } from 'react-i18next'
 import classnames from 'classnames'
 import { Question, User } from '@phosphor-icons/react'
 import NewProjectButton from '../new-project-button'
@@ -8,7 +9,6 @@ import { usePersistedResize } from '@/shared/hooks/use-resize'
 import { Dropdown } from 'react-bootstrap'
 import getMeta from '@/utils/meta'
 import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
-import { useTranslation } from 'react-i18next'
 import { NavDropdownMenuItems } from '@/features/ui/components/bootstrap-5/navbar/nav-dropdown-from-data'
 import { NavbarDropdownItemData } from '@/features/ui/components/types/navbar'
 import { useContactUsModal } from '@/shared/hooks/use-contact-us-modal'
@@ -45,29 +45,34 @@ function SidebarDsNav() {
         },
       })}
     >
-      <NewProjectButton
-        id="new-project-button-sidebar"
-        className={scrolledDown ? 'show-shadow' : undefined}
-      />
-      <div
-        className="project-list-sidebar-scroll"
-        ref={containerRef}
-        data-testid="project-list-sidebar-scroll"
-      >
-        <SidebarFilters />
-        {showAddAffiliationWidget && <hr />}
-        <AddAffiliation />
-      </div>
+      <nav aria-label={t('project_categories_tags')}>
+        <NewProjectButton
+          id="new-project-button-sidebar"
+          className={scrolledDown ? 'show-shadow' : undefined}
+        />
+        <div
+          className="project-list-sidebar-scroll"
+          ref={containerRef}
+          data-testid="project-list-sidebar-scroll"
+        >
+          <SidebarFilters />
+          {showAddAffiliationWidget && <hr />}
+          <AddAffiliation />
+        </div>
+      </nav>
       <div
         className={classnames(
           'ds-nav-sidebar-lower',
           scrolledUp && 'show-shadow'
         )}
       >
-        <div className="project-list-sidebar-survey-wrapper">
+        <aside
+          className="project-list-sidebar-survey-wrapper"
+          aria-label={t('feedback')}
+        >
           <SurveyWidgetDsNav />
-        </div>
-        <div className="d-flex gap-3 mb-2">
+        </aside>
+        <nav className="d-flex gap-3 mb-2" aria-label={t('account_help')}>
           {helpItem && (
             <Dropdown
               className="ds-nav-icon-dropdown"
@@ -157,7 +162,7 @@ function SidebarDsNav() {
               <UserProvider>{contactUsModal}</UserProvider>
             </>
           )}
-        </div>
+        </nav>
         <div className="ds-nav-ds-name" translate="no">
           <span>Digital Science</span>
         </div>

+ 0 - 0
services/web/frontend/js/features/project-list/components/sidebar/sidebar.tsx


+ 4 - 2
services/web/frontend/js/features/project-list/components/title/project-list-title.tsx

@@ -44,12 +44,14 @@ function ProjectListTitle({
   }
 
   return (
-    <div
+    <h1
+      id="main-content"
+      tabIndex={-1}
       className={classnames('project-list-title', className)}
       {...extraProps}
     >
       {message}
-    </div>
+    </h1>
   )
 }
 

+ 2 - 2
services/web/frontend/js/features/ui/components/bootstrap-5/footer/fat-footer-base.tsx

@@ -23,7 +23,7 @@ function FatFooterBase() {
   const currentYear = new Date().getFullYear()
 
   return (
-    <footer className="fat-footer-base">
+    <div className="fat-footer-base">
       <div className="fat-footer-base-section fat-footer-base-meta">
         <div className="fat-footer-base-item">
           <div className="fat-footer-base-copyright" translate="no">
@@ -62,7 +62,7 @@ function FatFooterBase() {
           />
         </div>
       </div>
-    </footer>
+    </div>
   )
 }
 

+ 1 - 5
services/web/frontend/js/features/ui/components/bootstrap-5/footer/fat-footer.tsx

@@ -91,11 +91,7 @@ function FatFooter() {
 
   return (
     <footer className="fat-footer hidden-print">
-      <div
-        role="navigation"
-        aria-label={t('footer_navigation')}
-        className="fat-footer-container"
-      >
+      <div className="fat-footer-container">
         <div className={`fat-footer-sections ${hideFatFooter ? 'hidden' : ''}`}>
           <div className="footer-section" id="footer-brand">
             <a href="/" aria-label={t('overleaf')} className="footer-brand">

+ 2 - 1
services/web/frontend/js/features/ui/components/bootstrap-5/navbar/default-navbar.tsx

@@ -61,6 +61,7 @@ function DefaultNavbar(
             '--navbar-brand-image-redesign-url': `url("${overleafBlackLogo}")`,
           } as CSSPropertiesWithVariables
         }
+        aria-label={t('primary')}
       >
         <Container className="navbar-container" fluid>
           <div className="navbar-header">
@@ -92,7 +93,7 @@ function DefaultNavbar(
               <Navbar.Toggle
                 aria-controls="navbar-main-collapse"
                 aria-expanded="false"
-                aria-label={t('main_navigation')}
+                aria-label={t('primary')}
               >
                 {showCloseIcon && expanded ? (
                   <X />

+ 6 - 7
services/web/frontend/stylesheets/bootstrap-5/pages/project-list.scss

@@ -140,15 +140,14 @@
   align-items: center;
   margin-bottom: var(--spacing-05);
   min-height: 36px;
-}
-
-.project-list-title {
-  min-width: 0;
-  color: $content-secondary;
 
-  @include heading-sm;
+  .project-list-title {
+    @include heading-sm;
 
-  font-weight: bold;
+    color: $content-secondary;
+    font-weight: bold;
+    min-width: 0;
+  }
 }
 
 .project-tools {

+ 12 - 4
services/web/locales/en.json

@@ -59,6 +59,7 @@
   "account_billed_manually": "Account billed manually",
   "account_has_been_link_to_institution_account": "Your __appName__ account on <b>__email__</b> has been linked to your <b>__institutionName__</b> institutional account.",
   "account_has_past_due_invoice_change_plan_warning": "Your account currently has a past due invoice. You will not be able to change your plan until this is resolved.",
+  "account_help": "Account and help",
   "account_linking": "Account linking",
   "account_managed_by_group_administrator": "Your account is managed by your group administrator (__admin__)",
   "account_not_linked_to_dropbox": "Your account is not linked to Dropbox",
@@ -744,6 +745,7 @@
   "features": "Features",
   "features_like_track_changes": "Features like real-time track changes",
   "february": "February",
+  "feedback": "Feedback",
   "figure": "Figure",
   "file": "File",
   "file_action_created": "Created",
@@ -764,6 +766,7 @@
   "file_too_large": "File too large",
   "file_tree": "File tree",
   "files_cannot_include_invalid_characters": "File name is empty or contains invalid characters",
+  "files_collaboration_integrations_logs": "Files, collaboration, integrations, error logs",
   "files_selected": "files selected.",
   "filter_projects": "Filter projects",
   "filters": "Filters",
@@ -787,7 +790,6 @@
   "font_size": "Font Size",
   "footer_about_us": "About us",
   "footer_contact_us": "Contact us",
-  "footer_navigation": "Footer navigation",
   "footnotes": "Footnotes",
   "for_business": "For business",
   "for_government": "For government",
@@ -949,6 +951,7 @@
   "help": "Help",
   "help_and_resources": "Help & resources",
   "help_articles_matching": "Help articles matching your subject",
+  "help_editor_settings": "Help and editor settings",
   "help_guides": "Help guides",
   "help_improve_overleaf_fill_out_this_survey": "If you would like to help us improve Overleaf, please take a moment to fill out <0>this survey</0>.",
   "help_improve_screen_reader_fill_out_this_survey": "Help us improve your experience using a screen reader with __appName__ by filling out this quick survey.",
@@ -1303,7 +1306,6 @@
   "main_bibliography_file_for_this_project": "Main bibliography file for this project",
   "main_document": "Main document",
   "main_file_not_found": "Unknown main document",
-  "main_navigation": "Main navigation",
   "maintenance": "Maintenance",
   "make_a_copy": "Make a copy",
   "make_email_primary_description": "Make this the primary email, used to log in",
@@ -1479,6 +1481,7 @@
   "not_now": "Not now",
   "not_registered": "Not registered",
   "note_features_under_development": "<0>Please note</0> that features in this program are still being tested and actively developed. This means that they might <0>change</0>, be <0>removed</0> or <0>become part of a premium plan</0>",
+  "notification": "Notification",
   "notification_features_upgraded_by_affiliation": "Good news! Your affiliated organization __institutionName__ has an Overleaf subscription, and you now have access to all of Overleaf’s Professional features.",
   "notification_personal_and_group_subscriptions": "We’ve spotted that you’ve got <0>more than one active __appName__ subscription</0>. To avoid paying more than you need to, <1>review your subscriptions</1>.",
   "notification_personal_subscription_not_required_due_to_affiliation": " Good news! Your affiliated organization __institutionName__ has an Overleaf subscription, and you now have access to Overleaf’s Professional features through your affiliation. You can cancel your individual subscription without losing access to any features.",
@@ -1615,6 +1618,7 @@
   "pdf_only_hide_editor": "PDF only <0>(hide editor)</0>",
   "pdf_preview": "PDF preview",
   "pdf_preview_error": "There was a problem displaying the compilation results for this project.",
+  "pdf_preview_logs": "PDF preview and logs",
   "pdf_rendering_error": "PDF Rendering Error",
   "pdf_unavailable_for_download": "PDF unavailable for download",
   "pdf_viewer": "PDF Viewer",
@@ -1695,6 +1699,7 @@
   "primarily_work_study_question_nonprofit_ngo": "Nonprofit or NGO",
   "primarily_work_study_question_other": "Other",
   "primarily_work_study_question_university_school": "University or school",
+  "primary": "Primary",
   "primary_certificate": "Primary certificate",
   "primary_email_check_question": "Is <0>__email__</0> still your email address?",
   "priority_support": "Priority support",
@@ -1714,14 +1719,17 @@
   "professional": "Professional",
   "progress_bar_percentage": "Progress bar from 0 to 100%",
   "project": "project",
+  "project_actions": "Project actions",
   "project_approaching_file_limit": "This project is approaching the file limit",
+  "project_categories_tags": "Project categories and tags",
   "project_figure_modal": "Project",
   "project_files": "Project files",
+  "project_files_history": "Project files history",
+  "project_files_outline": "Project files and outline",
   "project_flagged_too_many_compiles": "This project has been flagged for compiling too often. The limit will be lifted shortly.",
   "project_has_too_many_files": "This project has reached the 2000 file limit",
-  "project_history_list": "Project history list",
+  "project_history_labels": "Project history and labels",
   "project_last_published_at": "Your project was last published at",
-  "project_layout_sharing_submission": "Project Layout, Sharing, and Submission",
   "project_linked_to": "This project is linked to",
   "project_name": "Project Name",
   "project_not_linked_to_github": "This project is not linked to a GitHub repository. You can create a repository for it in GitHub:",

+ 9 - 5
services/web/test/frontend/components/pdf-preview/pdf-js-viewer.spec.tsx

@@ -31,10 +31,12 @@ describe('<PdfJSViewer/>', function () {
 
     cy.waitForCompile({ pdf: true })
 
-    cy.findByRole('region', { name: `Page ${FSI}1${PDI}` })
-    cy.findByRole('region', { name: `Page ${FSI}2${PDI}` })
-    cy.findByRole('region', { name: `Page ${FSI}3${PDI}` })
-    cy.findByRole('region', { name: `Page ${FSI}4${PDI}` }).should('not.exist')
+    cy.findByTestId('pdfjs-viewer-inner').within(() => {
+      cy.findByLabelText(`Page ${FSI}1${PDI}`)
+      cy.findByLabelText(`Page ${FSI}2${PDI}`)
+      cy.findByLabelText(`Page ${FSI}3${PDI}`)
+      cy.findByLabelText(`Page ${FSI}4${PDI}`).should('not.exist')
+    })
 
     cy.contains('Your Paper')
   })
@@ -96,7 +98,9 @@ describe('<PdfJSViewer/>', function () {
 
     cy.waitForCompile({ pdf: true })
 
-    cy.findByRole('region', { name: `Page ${FSI}1${PDI}` })
+    cy.findByTestId('pdfjs-viewer-inner').within(() => {
+      cy.findByLabelText(`Page ${FSI}1${PDI}`)
+    })
 
     cy.then(() => unmountComponentAtNode(getContainerEl()))
   })

+ 39 - 3
services/web/test/frontend/features/history/components/change-list.spec.tsx

@@ -144,7 +144,19 @@ describe('change list (Bootstrap 5)', function () {
           cy.findByRole('button', { name: /delete/i }).should('not.exist')
         })
       )
-      cy.findByLabelText(/labels/i).click({ force: true })
+      cy.findByRole('complementary', {
+        name: /Project history and labels/i,
+      }).within(() => {
+        cy.findByRole('group', {
+          name: /Show all of the project history/i,
+        }).within(() => {
+          cy.findByText(/Labels/i).click()
+        })
+        cy.findByRole('radio', { name: /Labels/i }).should('be.checked')
+        cy.findByRole('radio', { name: /All history/i }).should(
+          'not.be.checked'
+        )
+      })
       cy.findAllByTestId('history-version-details').as('details')
       // first details on labels is always "current version", start testing on second
       cy.get('@details').should('have.length', 3)
@@ -337,7 +349,19 @@ describe('change list (Bootstrap 5)', function () {
         }
       )
       waitForData()
-      cy.findByLabelText(/labels/i).click({ force: true })
+      cy.findByRole('complementary', {
+        name: /Project history and labels/i,
+      }).within(() => {
+        cy.findByRole('group', {
+          name: /Show all of the project history/i,
+        }).within(() => {
+          cy.findByText(/Labels/i).click()
+        })
+        cy.findByRole('radio', { name: /Labels/i }).should('be.checked')
+        cy.findByRole('radio', { name: /All history/i }).should(
+          'not.be.checked'
+        )
+      })
     })
 
     it('shows the dropdown menu item for adding new labels', function () {
@@ -688,7 +712,19 @@ describe('change list (Bootstrap 5)', function () {
 
       waitForData()
 
-      cy.findByLabelText(/labels/i).click({ force: true })
+      cy.findByRole('complementary', {
+        name: /Project history and labels/i,
+      }).within(() => {
+        cy.findByRole('group', {
+          name: /Show all of the project history/i,
+        }).within(() => {
+          cy.findByText(/Labels/i).click()
+        })
+        cy.findByRole('radio', { name: /Labels/i }).should('be.checked')
+        cy.findByRole('radio', { name: /All history/i }).should(
+          'not.be.checked'
+        )
+      })
 
       // One pseudo-label for the current state, one for our label
       cy.get('.history-version-label').should('have.length', 2)