summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarco Cecchetti <marco.cecchetti@collabora.com>2023-07-10 12:14:17 +0200
committerMarco Cecchetti <mrcekets@gmail.com>2023-08-10 09:29:09 +0200
commit96a7cfb25eeeeabad75d4dbd17a698bdd432335b (patch)
tree6bc8eae2082280eae708a2899a516ec2fe8dfb2c
parentRemove open_local_editor button in tablet view (diff)
downloadonline-96a7cfb25eeeeabad75d4dbd17a698bdd432335b.tar.gz
online-96a7cfb25eeeeabad75d4dbd17a698bdd432335b.zip
a11y: provide info about current table and cell to screen reader
When we get in one or more tables screen reader reports row and column count. When we get out one or more table screen report reports "out of table" for each table. When the fosused cell changes screen reader reports new row/col index. Screen reader reports cell paragraph content, too. Added also cypress tests for checking that the table/cell description string is correct. Signed-off-by: Marco Cecchetti <marco.cecchetti@collabora.com> Change-Id: I078b29a2f76f91df479f75db76113c80405f7169
-rw-r--r--browser/src/layer/marker/A11yTextInput.js69
-rw-r--r--browser/src/layer/tile/CanvasTileLayer.js10
-rw-r--r--cypress_test/data/desktop/writer/table_accessibility.odtbin0 -> 14065 bytes
-rw-r--r--cypress_test/integration_tests/desktop/writer/table_accessibility_spec.js95
-rw-r--r--kit/ChildSession.cpp5
5 files changed, 178 insertions, 1 deletions
diff --git a/browser/src/layer/marker/A11yTextInput.js b/browser/src/layer/marker/A11yTextInput.js
index 01d18c03d9..cd30bb03c6 100644
--- a/browser/src/layer/marker/A11yTextInput.js
+++ b/browser/src/layer/marker/A11yTextInput.js
@@ -8,7 +8,7 @@
* text area itself.
*/
-/* global app */
+/* global app _ */
L.A11yTextInput = L.TextInput.extend({
initialize: function() {
@@ -182,6 +182,73 @@ L.A11yTextInput = L.TextInput.extend({
}
},
+ _setDescription: function(text) {
+ window.app.console.log('setDescription: ' + text);
+ this._textArea.setAttribute('aria-description', text);
+ },
+
+ _updateTable: function(outCount, inList, row, col, rowSpan, colSpan) {
+ if (this._isDebugOn) {
+ window.app.console.log('_updateTable: '
+ + '\n outCount: ' + outCount
+ + '\n inList: ' + inList.toString()
+ + '\n row: ' + row + ', rowSpan: ' + rowSpan
+ + '\n col: ' + col + ', colSpan: ' + colSpan
+ );
+ }
+
+ if (this._timeoutForTableDescription)
+ clearTimeout(this._timeoutForTableDescription);
+
+ var eventDescription = '';
+ if (outCount > 0 || inList.length > 0) {
+ this._lastRowIndex = 0;
+ this._lastColIndex = 0;
+ this._lastRowSpan = 1;
+ this._lastColSpan = 1;
+ }
+ for (var i = 0; i < outCount; i++) {
+ eventDescription += _('Out of table') + '. ';
+ }
+ for (i = 0; i < inList.length; i++) {
+ eventDescription += _('Table with') + ' ' + inList[i].rowCount + ' ' + _('rows') + ' '
+ + _('and') + ' ' + inList[i].colCount + ' ' + _('columns') + '. ';
+ }
+ if (this._lastRowIndex !== row || this._lastRowSpan !== rowSpan) {
+ this._lastRowIndex = row;
+ eventDescription += _('Row') + ' ' + row;
+ if (this._lastRowSpan !== rowSpan) {
+ if (rowSpan > 1) {
+ eventDescription += ' ' + _('through') + ' ' + (row + rowSpan - 1) ;
+ }
+ this._lastRowSpan = rowSpan;
+ }
+ eventDescription += '. ';
+ }
+ if (this._lastColIndex !== col || this._lastColSpan !== colSpan) {
+ this._lastColIndex = col;
+ eventDescription += _('Column') + ' ' + col;
+ if (this._lastColSpan !== colSpan) {
+ if (colSpan > 1) {
+ eventDescription += ' ' + _('through') + ' ' + (col + colSpan - 1);
+ }
+ this._lastColSpan = colSpan;
+ }
+ eventDescription += '. ';
+ }
+ this._setDescription(eventDescription);
+
+ var that = this;
+ this._timeoutForTableDescription = setTimeout(function() {
+ that._setDescription('');
+ }, 1000);
+ },
+
+ onAccessibilityFocusedCellChanged: function(outCount, inList, row, col, rowSpan, colSpan, paragraph) {
+ this._setFocusedParagraph(paragraph.content, parseInt(paragraph.position), parseInt(paragraph.start), parseInt(paragraph.end));
+ this._updateTable(outCount, inList, row + 1, col + 1, rowSpan, colSpan);
+ },
+
// Check if a UTF-16 pair represents a Unicode code point
_isSurrogatePair: function(hi, lo) {
return hi >= 0xd800 && hi <= 0xdbff && lo >= 0xdc00 && lo <= 0xdfff;
diff --git a/browser/src/layer/tile/CanvasTileLayer.js b/browser/src/layer/tile/CanvasTileLayer.js
index 6202044ed1..feda5c2a45 100644
--- a/browser/src/layer/tile/CanvasTileLayer.js
+++ b/browser/src/layer/tile/CanvasTileLayer.js
@@ -1864,6 +1864,16 @@ L.CanvasTileLayer = L.Layer.extend({
obj = JSON.parse(textMsg.substring('a11ytextselectionchanged:'.length + 1));
this._map._textInput.onAccessibilityTextSelectionChanged(parseInt(obj.start), parseInt(obj.end));
}
+ else if (textMsg.startsWith('a11yfocusedcellchanged:')) {
+ obj = JSON.parse(textMsg.substring('a11yfocusedcellchanged:'.length + 1));
+ var outCount = obj.outCount !== undefined ? parseInt(obj.outCount) : 0;
+ var inList = obj.inList !== undefined ? obj.inList : [];
+ var row = parseInt(obj.row);
+ var col = parseInt(obj.col);
+ var rowSpan = obj.rowSpan !== undefined ? parseInt(obj.rowSpan) : 1;
+ var colSpan = obj.colSpan !== undefined ? parseInt(obj.colSpan) : 1;
+ this._map._textInput.onAccessibilityFocusedCellChanged(outCount, inList, row, col, rowSpan, colSpan, obj.paragraph);
+ }
else if (textMsg.startsWith('a11yfocusedparagraph:')) {
obj = JSON.parse(textMsg.substring('a11yfocusedparagraph:'.length + 1));
this._map._textInput.setA11yFocusedParagraph(obj.content, parseInt(obj.position), parseInt(obj.start), parseInt(obj.end));
diff --git a/cypress_test/data/desktop/writer/table_accessibility.odt b/cypress_test/data/desktop/writer/table_accessibility.odt
new file mode 100644
index 0000000000..aca7a9859b
--- /dev/null
+++ b/cypress_test/data/desktop/writer/table_accessibility.odt
Binary files differ
diff --git a/cypress_test/integration_tests/desktop/writer/table_accessibility_spec.js b/cypress_test/integration_tests/desktop/writer/table_accessibility_spec.js
new file mode 100644
index 0000000000..1538008166
--- /dev/null
+++ b/cypress_test/integration_tests/desktop/writer/table_accessibility_spec.js
@@ -0,0 +1,95 @@
+/* global describe it cy beforeEach require afterEach */
+
+var helper = require('../../common/helper');
+// var desktopHelper = require('../../common/desktop_helper');
+var ceHelper = require('../../common/contenteditable_helper');
+
+describe(['taga11yenabled'], 'Table accessibility', function() {
+ var testFileName = 'table_accessibility.odt';
+
+ beforeEach(function () {
+ helper.beforeAll(testFileName, 'writer');
+ cy.cGet('div.clipboard').as('clipboard');
+ });
+
+ afterEach(function () {
+ helper.afterAll(testFileName, this.currentTest.state);
+ });
+
+ function checkCellDescription(expectedDescription) {
+ cy.wait(400);
+ cy.get('@clipboard').should('have.attr', 'aria-description', expectedDescription);
+ }
+
+ it('Table content and cell address on navigation', function () {
+ ceHelper.checkPlainContent('Paragraph above.');
+ ceHelper.moveCaret('down');
+ checkCellDescription('Table with 4 rows and 3 columns. Row 1. Column 1. ');
+ ceHelper.checkPlainContent('Item 1.1');
+ ceHelper.moveCaret('down');
+ checkCellDescription('Row 2. ');
+ ceHelper.checkPlainContent('Item 2.1');
+ ceHelper.moveCaret('down');
+ checkCellDescription('Row 3 through 4. ');
+ ceHelper.checkPlainContent('Item 3.1');
+ ceHelper.moveCaret('end');
+ ceHelper.moveCaret('right');
+ checkCellDescription('Row 3. Column 2 through 3. ');
+ ceHelper.checkPlainContent('Item 3.2');
+ ceHelper.moveCaret('down');
+ checkCellDescription('Row 4. ');
+ ceHelper.checkPlainContent('Item 4.2');
+ ceHelper.moveCaret('down');
+ checkCellDescription('Out of table. ');
+ ceHelper.checkPlainContent('Paragraph below.');
+ ceHelper.moveCaret('up');
+ checkCellDescription('Table with 4 rows and 3 columns. Row 3 through 4. Column 1. ');
+ ceHelper.checkPlainContent('Item 3.1');
+ });
+
+ it('Nested table content and cell address on navigation', function () {
+ ceHelper.checkPlainContent('Paragraph above.');
+ ceHelper.moveCaret('down');
+ checkCellDescription('Table with 4 rows and 3 columns. Row 1. Column 1. ');
+ ceHelper.checkPlainContent('Item 1.1');
+ ceHelper.moveCaret('end');
+ ceHelper.moveCaret('right');
+ checkCellDescription('Column 2. ');
+ ceHelper.checkPlainContent('Item 1.2');
+ ceHelper.moveCaret('end');
+ ceHelper.moveCaret('right');
+ checkCellDescription('Column 3. ');
+ ceHelper.checkPlainContent('Item 1.3');
+ ceHelper.moveCaret('down');
+ checkCellDescription('Table with 2 rows and 2 columns. Row 1. Column 2. ');
+ ceHelper.checkPlainContent('Nested Cell 1.2');
+ ceHelper.moveCaret('left');
+ checkCellDescription('Column 1. ');
+ ceHelper.checkPlainContent('Nested Cell 1.1');
+ ceHelper.moveCaret('home');
+ ceHelper.moveCaret('left');
+ checkCellDescription('Out of table. Row 2. Column 2. ');
+ ceHelper.checkPlainContent('Item 2.2');
+ ceHelper.moveCaret('down');
+ checkCellDescription('Row 3. Column 2 through 3. ');
+ ceHelper.checkPlainContent('Item 3.2');
+ ceHelper.moveCaret('up');
+ checkCellDescription('Row 2. Column 3. ');
+ ceHelper.checkPlainContent('');
+ ceHelper.moveCaret('up');
+ checkCellDescription('Table with 2 rows and 2 columns. Row 2. Column 1. ');
+ ceHelper.checkPlainContent('Nested Cell 2.1');
+ ceHelper.moveCaret('end');
+ ceHelper.moveCaret('right');
+ checkCellDescription('Column 2. ');
+ ceHelper.checkPlainContent('Nested Cell 2.2');
+ ceHelper.moveCaret('end');
+ ceHelper.moveCaret('right');
+ checkCellDescription('Out of table. Row 2. Column 3. ');
+ ceHelper.checkPlainContent('');
+ ceHelper.moveCaret('end');
+ ceHelper.moveCaret('right');
+ checkCellDescription('Row 3 through 4. Column 1. ');
+ ceHelper.checkPlainContent('Item 3.1');
+ });
+});
diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
index acb8905ab6..8cdabd8752 100644
--- a/kit/ChildSession.cpp
+++ b/kit/ChildSession.cpp
@@ -3117,6 +3117,11 @@ void ChildSession::loKitCallback(const int type, const std::string& payload)
sendTextFrame("a11ytextselectionchanged: " + payload);
break;
}
+ case LOK_CALLBACK_A11Y_FOCUSED_CELL_CHANGED:
+ {
+ sendTextFrame("a11yfocusedcellchanged: " + payload);
+ break;
+ }
case LOK_CALLBACK_COLOR_PALETTES:
sendTextFrame("colorpalettes: " + payload);
break;