From 317d5d7eadc454db5d8956e0195c50518e522301 Mon Sep 17 00:00:00 2001
From: "me@jeffersonbledsoe.com"
Date: Sat, 7 Oct 2023 16:26:17 +0200
Subject: [PATCH 01/11] WIP: title change announcements using MutationObserver
---
src/components/theme/App/App.jsx | 2 +
.../theme/RouteAnnouncer/RouteAnnouncer.jsx | 60 +++++++++++++++++++
2 files changed, 62 insertions(+)
create mode 100644 src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx
diff --git a/src/components/theme/App/App.jsx b/src/components/theme/App/App.jsx
index cec83066a1..0ea0f15e6c 100644
--- a/src/components/theme/App/App.jsx
+++ b/src/components/theme/App/App.jsx
@@ -51,6 +51,7 @@ import clearSVG from '@plone/volto/icons/clear.svg';
import MultilingualRedirector from '@plone/volto/components/theme/MultilingualRedirector/MultilingualRedirector';
import WorkingCopyToastsFactory from '@plone/volto/components/manage/WorkingCopyToastsFactory/WorkingCopyToastsFactory';
import LockingToastsFactory from '@plone/volto/components/manage/LockingToastsFactory/LockingToastsFactory';
+import RouteAnnouncer from '@plone/volto/components/theme/RouteAnnouncer/RouteAnnouncer';
/**
* @export
@@ -186,6 +187,7 @@ export class App extends Component {
+
{
+ const observer = new MutationObserver((mutationList) => {
+ for (const mutation of mutationList) {
+ if (mutation.type === 'childList') {
+ for (const node of mutation.addedNodes) {
+ if (node.nodeType === Node.TEXT_NODE && node.textContent) {
+ updatePage(node.textContent);
+ }
+ }
+ }
+ }
+ });
+ observer.observe(document.querySelector('title'), {
+ characterData: true,
+ attributes: true,
+ childList: true,
+ subtree: true,
+ });
+
+ return () => {
+ observer.disconnect();
+ };
+ }, []);
+
+ return (
+
+ {pageTitle}
+
+ );
+}
+
+export default RouteAnnouncer;
From 959bf1a6dd8b4349b0215a7cc7183597dd9378d1 Mon Sep 17 00:00:00 2001
From: "me@jeffersonbledsoe.com"
Date: Tue, 2 Jul 2024 11:32:01 +0100
Subject: [PATCH 02/11] Move to packages location
---
.../volto/src}/components/theme/RouteAnnouncer/RouteAnnouncer.jsx | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename {src => packages/volto/src}/components/theme/RouteAnnouncer/RouteAnnouncer.jsx (100%)
diff --git a/src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx b/packages/volto/src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx
similarity index 100%
rename from src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx
rename to packages/volto/src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx
From 86d5b6b5abc9372f0e4ad0658637801d057bc70a Mon Sep 17 00:00:00 2001
From: "me@jeffersonbledsoe.com"
Date: Tue, 24 Sep 2024 16:17:22 +0200
Subject: [PATCH 03/11] Listen for back and forward events and handle those the
same way
---
.../src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/packages/volto/src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx b/packages/volto/src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx
index 942ef091cd..4079312460 100644
--- a/packages/volto/src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx
+++ b/packages/volto/src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx
@@ -11,6 +11,11 @@ function RouteAnnouncer() {
}
useEffect(() => {
+ function handlePop(event) {
+ const pageTitle = event.target.document.title;
+ updatePage(pageTitle);
+ }
+
const observer = new MutationObserver((mutationList) => {
for (const mutation of mutationList) {
if (mutation.type === 'childList') {
@@ -28,9 +33,11 @@ function RouteAnnouncer() {
childList: true,
subtree: true,
});
+ window.addEventListener('popstate', handlePop);
return () => {
observer.disconnect();
+ window.removeEventListener('popstate', handlePop);
};
}, []);
From 656312e7abe0ab5d1458e5058c4d787d4450b353 Mon Sep 17 00:00:00 2001
From: "me@jeffersonbledsoe.com"
Date: Thu, 26 Sep 2024 13:21:31 +0200
Subject: [PATCH 04/11] Changelog
---
packages/volto/news/5288.bugfix | 1 +
1 file changed, 1 insertion(+)
create mode 100644 packages/volto/news/5288.bugfix
diff --git a/packages/volto/news/5288.bugfix b/packages/volto/news/5288.bugfix
new file mode 100644
index 0000000000..e53a45483d
--- /dev/null
+++ b/packages/volto/news/5288.bugfix
@@ -0,0 +1 @@
+Fixed page changes not being announced to screen reader users. @JeffersonBledsoe
\ No newline at end of file
From c92a2605be141e3693ed77d399ed7dd2691b3874 Mon Sep 17 00:00:00 2001
From: "me@jeffersonbledsoe.com"
Date: Thu, 26 Sep 2024 13:28:17 +0200
Subject: [PATCH 05/11] Update snapshot
---
.../theme/App/__snapshots__/App.test.jsx.snap | 20 +++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/packages/volto/src/components/theme/App/__snapshots__/App.test.jsx.snap b/packages/volto/src/components/theme/App/__snapshots__/App.test.jsx.snap
index b4d714d40a..3a984aa49a 100644
--- a/packages/volto/src/components/theme/App/__snapshots__/App.test.jsx.snap
+++ b/packages/volto/src/components/theme/App/__snapshots__/App.test.jsx.snap
@@ -35,6 +35,26 @@ Array [
,
+
+ Route content change
+
,
,
From ff5b4dc5ae1cafc0297efb7b36656648d2e45941 Mon Sep 17 00:00:00 2001
From: "me@jeffersonbledsoe.com"
Date: Thu, 26 Sep 2024 13:29:05 +0200
Subject: [PATCH 06/11] Remove comment
---
.../src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx | 2 --
1 file changed, 2 deletions(-)
diff --git a/packages/volto/src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx b/packages/volto/src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx
index 4079312460..ff283ff404 100644
--- a/packages/volto/src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx
+++ b/packages/volto/src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx
@@ -1,7 +1,5 @@
import { useEffect, useState } from 'react';
-// TODO:
-// Don't announce on first page load (normal browser behaviour)
function RouteAnnouncer() {
const [pageTitle, setPageTitle] = useState('Route content change');
From 2a5cd4c25b5908a60e91a4a85f83a050bf648a5a Mon Sep 17 00:00:00 2001
From: "me@jeffersonbledsoe.com"
Date: Thu, 26 Sep 2024 13:39:46 +0200
Subject: [PATCH 07/11] Fix test failure
---
packages/volto/src/components/theme/App/App.test.jsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/volto/src/components/theme/App/App.test.jsx b/packages/volto/src/components/theme/App/App.test.jsx
index 2a34423864..86160abe70 100644
--- a/packages/volto/src/components/theme/App/App.test.jsx
+++ b/packages/volto/src/components/theme/App/App.test.jsx
@@ -64,5 +64,6 @@ describe('App', () => {
);
const json = component.toJSON();
expect(json).toMatchSnapshot();
+ component.unmount();
});
});
From 76ea10686b929508bea2414fb696bfc8a15c7648 Mon Sep 17 00:00:00 2001
From: "me@jeffersonbledsoe.com"
Date: Thu, 26 Sep 2024 13:58:58 +0200
Subject: [PATCH 08/11] Test fixes
---
.../volto/cypress/tests/core/basic/locking.js | 6 +++---
.../cypress/tests/core/basic/metadata.js | 4 ++--
.../volto/cypress/tests/workingCopy/create.js | 19 ++++++++++++++-----
.../src/components/theme/App/App.test.jsx | 6 +++---
4 files changed, 22 insertions(+), 13 deletions(-)
diff --git a/packages/volto/cypress/tests/core/basic/locking.js b/packages/volto/cypress/tests/core/basic/locking.js
index d414667777..bac79c3115 100644
--- a/packages/volto/cypress/tests/core/basic/locking.js
+++ b/packages/volto/cypress/tests/core/basic/locking.js
@@ -19,7 +19,7 @@ describe('Document locking', () => {
cy.removeUser('editor2', 'password');
});
- it('As editor, a page is locked for other users when I edit that page', function () {
+ it.only('As editor, a page is locked for other users when I edit that page', function () {
// As an editor I can add a document
cy.intercept('/**/@logout').as('logout');
cy.intercept('GET', '/**/Document').as('schema');
@@ -41,8 +41,8 @@ describe('Document locking', () => {
cy.visit('/document');
cy.wait('@content');
- cy.findByRole('alert')
- .get('.toast-inner-content')
+ cy.get('.Toastify')
+ .findByRole('alert')
.contains('This item was locked by Editor 1 on');
});
diff --git a/packages/volto/cypress/tests/core/basic/metadata.js b/packages/volto/cypress/tests/core/basic/metadata.js
index 70c30e31a9..6d5b414aad 100644
--- a/packages/volto/cypress/tests/core/basic/metadata.js
+++ b/packages/volto/cypress/tests/core/basic/metadata.js
@@ -34,8 +34,8 @@ describe('Add Content Tests', () => {
cy.get('.ui.basic.icon.button.image').contains('Image').click();
cy.get('#toolbar-save').click();
- cy.findByRole('alert')
- .get('.toast-inner-content')
+ cy.get('.Toastify')
+ .findByRole('alert')
.contains('Required input is missing');
cy.get('.sidebar-container .tabs-wrapper .active.item').contains('Page');
});
diff --git a/packages/volto/cypress/tests/workingCopy/create.js b/packages/volto/cypress/tests/workingCopy/create.js
index 9d72f4c2c5..55b737e07f 100644
--- a/packages/volto/cypress/tests/workingCopy/create.js
+++ b/packages/volto/cypress/tests/workingCopy/create.js
@@ -19,8 +19,11 @@ describe('Working Copy Tests - Create', () => {
it('Basic create operation', function () {
cy.get('#toolbar-more').click();
cy.findByLabelText('Create working copy').click();
- cy.findByRole('alert').contains('This is a working copy of');
- cy.findByRole('alert')
+ cy.get('.Toastify')
+ .findByRole('alert')
+ .contains('This is a working copy of');
+ cy.get('.Toastify')
+ .findByRole('alert')
.get('.toast-inner-content a')
.should('have.attr', 'href')
.and('include', '/document');
@@ -37,11 +40,17 @@ describe('Working Copy Tests - Create', () => {
it('Navigation through baseline-working copy', function () {
cy.get('#toolbar-more').click();
cy.findByLabelText('Create working copy').click();
- cy.findByRole('alert').get('.toast-inner-content a').click();
+ cy.get('.Toastify')
+ .findByRole('alert')
+ .get('.toast-inner-content a')
+ .click();
cy.url().should('eq', Cypress.config().baseUrl + '/document');
- cy.findByRole('alert').contains('This has an ongoing working copy in');
- cy.findByRole('alert')
+ cy.get('.Toastify')
+ .findByRole('alert')
+ .contains('This has an ongoing working copy in');
+ cy.get('.Toastify')
+ .findByRole('alert')
.get('.toast-inner-content a')
.should('have.attr', 'href')
.and('include', '/working_copy_of_document');
diff --git a/packages/volto/src/components/theme/App/App.test.jsx b/packages/volto/src/components/theme/App/App.test.jsx
index 86160abe70..e9284aac39 100644
--- a/packages/volto/src/components/theme/App/App.test.jsx
+++ b/packages/volto/src/components/theme/App/App.test.jsx
@@ -1,9 +1,9 @@
+import config from '@plone/volto/registry';
import React from 'react';
-import renderer from 'react-test-renderer';
-import configureStore from 'redux-mock-store';
import { Provider } from 'react-intl-redux';
import { MemoryRouter } from 'react-router-dom';
-import config from '@plone/volto/registry';
+import renderer from 'react-test-renderer';
+import configureStore from 'redux-mock-store';
import { __test__ as App } from './App';
From 76a99c05849fea889549db0544e20d0f24dd6457 Mon Sep 17 00:00:00 2001
From: "me@jeffersonbledsoe.com"
Date: Thu, 26 Sep 2024 14:53:35 +0200
Subject: [PATCH 09/11] Fix unwanted test change
---
packages/volto/cypress/tests/core/basic/locking.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/volto/cypress/tests/core/basic/locking.js b/packages/volto/cypress/tests/core/basic/locking.js
index bac79c3115..63e47823eb 100644
--- a/packages/volto/cypress/tests/core/basic/locking.js
+++ b/packages/volto/cypress/tests/core/basic/locking.js
@@ -19,7 +19,7 @@ describe('Document locking', () => {
cy.removeUser('editor2', 'password');
});
- it.only('As editor, a page is locked for other users when I edit that page', function () {
+ it('As editor, a page is locked for other users when I edit that page', function () {
// As an editor I can add a document
cy.intercept('/**/@logout').as('logout');
cy.intercept('GET', '/**/Document').as('schema');
From 7bacf1759391e90de65c646e39b325e203182dc4 Mon Sep 17 00:00:00 2001
From: "me@jeffersonbledsoe.com"
Date: Fri, 27 Sep 2024 11:49:40 +0200
Subject: [PATCH 10/11] Remove initial "Route content change" contents incase a
user somehow ends up focused on this
---
.../src/components/theme/App/__snapshots__/App.test.jsx.snap | 4 +---
.../src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx | 2 +-
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/packages/volto/src/components/theme/App/__snapshots__/App.test.jsx.snap b/packages/volto/src/components/theme/App/__snapshots__/App.test.jsx.snap
index 3a984aa49a..4ed6bbfcb0 100644
--- a/packages/volto/src/components/theme/App/__snapshots__/App.test.jsx.snap
+++ b/packages/volto/src/components/theme/App/__snapshots__/App.test.jsx.snap
@@ -52,9 +52,7 @@ Array [
"wordWrap": "normal",
}
}
- >
- Route content change
-
,
+ />,
,
diff --git a/packages/volto/src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx b/packages/volto/src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx
index ff283ff404..7505a723f1 100644
--- a/packages/volto/src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx
+++ b/packages/volto/src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
function RouteAnnouncer() {
- const [pageTitle, setPageTitle] = useState('Route content change');
+ const [pageTitle, setPageTitle] = useState('');
function updatePage(title) {
setPageTitle(title);
From 3acd44badb449e203080a560112be8566d25b910 Mon Sep 17 00:00:00 2001
From: "me@jeffersonbledsoe.com"
Date: Mon, 30 Sep 2024 14:40:53 +0100
Subject: [PATCH 11/11] Move updatePage inside the useEffect
---
.../components/theme/RouteAnnouncer/RouteAnnouncer.jsx | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/packages/volto/src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx b/packages/volto/src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx
index 7505a723f1..91a746c626 100644
--- a/packages/volto/src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx
+++ b/packages/volto/src/components/theme/RouteAnnouncer/RouteAnnouncer.jsx
@@ -3,12 +3,11 @@ import { useEffect, useState } from 'react';
function RouteAnnouncer() {
const [pageTitle, setPageTitle] = useState('');
- function updatePage(title) {
- setPageTitle(title);
- document.activeElement.blur();
- }
-
useEffect(() => {
+ function updatePage(title) {
+ setPageTitle(title);
+ document.activeElement.blur();
+ }
function handlePop(event) {
const pageTitle = event.target.document.title;
updatePage(pageTitle);