Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 110 additions & 19 deletions src/accessibility/describe.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ function describe(p5, fn) {
*
* The first parameter, `text`, is the description of the canvas.
*
* The second parameter, `display`, is optional. It determines how the
* description is displayed. If `LABEL` is passed, as in
* `describe('A description.', LABEL)`, the description will be visible in
* a div element next to the canvas. If `FALLBACK` is passed, as in
* `describe('A description.', FALLBACK)`, the description will only be
* visible to screen readers. This is the default mode.
* The second parameter, `display`, is optional and can be `LABEL` or
* `FALLBACK`. The third parameter, `lang`, is optional and sets the language
* of the description for screen readers. However, if only the optional 'lang'
* parameter is passed, it will be treated as the second optional parameter and
* the default option 'FALLBACK' will be used as the display.
*
*
* Read
* <a href="/learn/accessible-labels.html">Writing accessible canvas descriptions</a>
Expand All @@ -33,6 +33,7 @@ function describe(p5, fn) {
* @method describe
* @param {String} text description of the canvas.
* @param {(FALLBACK|LABEL)} [display] either LABEL or FALLBACK.
* @param {String} [lang] valid lang attribute.
*
* @example
* function setup() {
Expand Down Expand Up @@ -110,7 +111,7 @@ function describe(p5, fn) {
* describe(`A green circle at (${x}, 50) moves from left to right on a gray square.`, LABEL);
* }
*/

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think here for describe() you need to add an overload in the JSDoc, since this function now has two signatures.
When I ran this sketch

function setup() {
  createCanvas(100, 100);
}

function draw() {
  background(200);
  fill(0, 255, 0);
  circle(50, 50, 40);

  describe('Un círculo verde sobre un fondo gris.', 'es');
}

it gives an FES error on every frame

🌸 p5.js says: [sketch.js, line 10] Expected constant (please refer to documentation
for allowed values) at the second parameter, but received es in describe().

After adding the overload, docs/parameterData.json also needs to be updated for it (you can simply run npm run docs for it)

I think you need to do the same thing with describeElement() too.

fn.describe = function (text, display) {
fn.describe = function (text, display, lang) {
// p5._validateParameters('describe', arguments);
if (typeof text !== 'string') {
return;
Expand All @@ -122,6 +123,7 @@ function describe(p5, fn) {
if (!this.dummyDOM) {
this.dummyDOM = document.getElementById(cnvId).parentNode;
}
//check if html structure for description is ready
if (!this.descriptions) {
this.descriptions = {};
}
Expand Down Expand Up @@ -150,6 +152,7 @@ function describe(p5, fn) {
this._describeHTML('label', text);
}
}
_setDescriptionLang(this, lang);
};

/**
Expand All @@ -161,15 +164,18 @@ function describe(p5, fn) {
* The first parameter, `name`, is the name of the element.
*
* The second parameter, `text`, is the description of the element.
*
* The third parameter, `display`, is optional. It determines how the
* description is displayed. If `LABEL` is passed, as in
* `describe('A description.', LABEL)`, the description will be visible in
* a div element next to the canvas. Using `LABEL` creates unhelpful
* duplicates for screen readers. Only use `LABEL` during development. If
* `FALLBACK` is passed, as in `describe('A description.', FALLBACK)`, the
* description will only be visible to screen readers. This is the default
* mode.
*
* The third parameter, `display`, is optional and can be `LABEL` or
* `FALLBACK`. The fourth parameter, `lang`, is optional and sets the language
* of the description for screen readers. However, if only the optional 'lang'
* parameter is passed, it will be treated as the third optional parameter and
* the default option 'FALLBACK' will be used as the display.
*
* If `LABEL` is passed, as in `describe('A description.', LABEL)`,
* the description will be visible in a div element next to the canvas. Using
* `LABEL` creates unhelpful duplicates for screen readers. Only use `LABEL`
* during development. If `FALLBACK` is passed, as in `describe('A description.', FALLBACK)`,
* the description will only be visible to screen readers. This is the default mode.
*
* Read
* <a href="/learn/accessible-labels.html">Writing accessible canvas descriptions</a>
Expand All @@ -179,7 +185,7 @@ function describe(p5, fn) {
* @param {String} name name of the element.
* @param {String} text description of the element.
* @param {(FALLBACK|LABEL)} [display] either LABEL or FALLBACK.
*
* @param {String} [lang] valid lang attribute.
* @example
* function setup() {
* background('pink');
Expand Down Expand Up @@ -229,7 +235,7 @@ function describe(p5, fn) {
* }
*/

fn.describeElement = function (name, text, display) {
fn.describeElement = function (name, text, display, lang) {
// p5._validateParameters('describeElement', arguments);
if (typeof text !== 'string' || typeof name !== 'string') {
return;
Expand All @@ -242,8 +248,11 @@ function describe(p5, fn) {
//remove any special characters from name to use it as html id
name = name.replace(/[^a-zA-Z0-9]/g, '');

// Inject lang attribute
let langAttr = typeof lang === 'string' ? ` lang="${lang}"` : '';

//store element description
let inner = `<th scope="row">${elementName}</th><td>${text}</td>`;
let inner = `<th scope="row"${langAttr}>${elementName}</th><td${langAttr}>${text}</td>`;
//if there is no dummyDOM
if (!this.dummyDOM) {
this.dummyDOM = document.getElementById(cnvId).parentNode;
Expand Down Expand Up @@ -281,14 +290,96 @@ function describe(p5, fn) {
this._describeElementHTML('label', name, inner);
}
}
_setDescriptionLang(this, lang);
};

p5.registerDecorator('p5.prototype.describe', function (target) {
return function (text, display, lang) {
// Checks for optional parameters and rearranges if needed
if (arguments.length === 2 && typeof display === 'string') {
// (text, display)
if (display === this.LABEL || display === this.FALLBACK) {
return target.call(this, text, display, undefined);
}
// (text, lang)
return target.call(this, text, undefined, display);
}
if (!_checkDescriptionOptions(this, 'describe', display, lang)) {
return;
}
// (text, display, lang) or just (text)
return target.call(this, text, display, lang);
};
});

p5.registerDecorator('p5.prototype.describeElement', function (target) {
return function (name, text, display, lang) {
// Checks for optional parameters and rearranges if needed
if (arguments.length === 3 && typeof display === 'string') {
// (name, text, display)
if (display === this.LABEL || display === this.FALLBACK) {
return target.call(this, name, text, display, undefined);
}
// (name, text, lang)
return target.call(this, name, text, undefined, display);
}
if (!_checkDescriptionOptions(this, 'describeElement', display, lang)) {
return;
}
// (name, text, display, lang) or just (name, text)
return target.call(this, name, text, display, lang);
};
});

/*
*
* Helper functions for describe() and describeElement().
*
*/

function _checkDescriptionOptions(pInst, methodName, display, lang) {
// Ensures display and lang are in the correct orders and are valid
const validDisplay = display === undefined || display === pInst.LABEL || display === pInst.FALLBACK;
const validLanguage = lang === undefined || typeof lang === 'string';

if (!validDisplay || !validLanguage) {
p5._friendlyError(
`${methodName}() expects display (LABEL or FALLBACK) before lang.`,
methodName
);
return false;
}
return true;
}

function _setDescriptionLang(pInst, lang) {
const canvas = pInst.canvas.elt || pInst.elt || pInst.canvas;
if (typeof lang === 'string') {
canvas.setAttribute('lang', lang);
}
else {
canvas.removeAttribute('lang');
}

if (pInst.descriptions.fallback) {
if (typeof lang === 'string') {
pInst.descriptions.fallback.setAttribute('lang', lang);
}
else {
pInst.descriptions.fallback.removeAttribute('lang');
}
}

if (pInst.descriptions.label) {
if (typeof lang === 'string') {
pInst.descriptions.label.setAttribute('lang', lang);
}
else {
pInst.descriptions.label.removeAttribute('lang');
}
}
}

// check that text is not LABEL or FALLBACK and ensure text ends with punctuation mark
function _descriptionText(text) {
if (text === 'label' || text === 'fallback') {
Expand Down
46 changes: 46 additions & 0 deletions test/manual-test-examples/accessibility/describe/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Accessibility: describe language</title>
<link rel="stylesheet" href="../../styles.css">

<script language="javascript" src="../../../../lib/p5.js"></script>
<script language="javascript" type="text/javascript" src="sketch.js"></script>
</head>

<body>
<header>
<p>Accessibility: describe() language test</p>
</header>

<main>
<p>Read each region with a screen reader. Compare regions without a lang attribute with those that have one.</p>

<div aria-label="English No Lang">
<p>this is a test</p>
</div>
<div aria-label="English With Lang" lang="en">
<p>this is a test</p>
</div>
<div aria-label="Spanish No Lang">
<p>esto es una prueba</p>
</div>
<div aria-label="Spanish With Lang" lang="es">
<p>esto es una prueba</p>
</div>
<div aria-label="Vietnamese No Lang">
<p>Cái này là bài thi</p>
</div>
<div aria-label="Vietnamese With Lang" lang="vi">
<p>Cái này là bài thi</p>
</div>

<h1>p5.js generated descriptions</h1>
<p>Read the descriptions generated by the canvases below. The visible LABEL descriptions are included for inspection.</p>
</main>
</body>

</html>
40 changes: 40 additions & 0 deletions test/manual-test-examples/accessibility/describe/sketch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
function setup() {
// Uncomment any of the following lines to test

createCanvas(400, 400);

// Text only (fallback is the default)
// describe('Cái này là bài thi');

// Text and display
// describe('Esto es una prueba', LABEL);

// Text and lang
//describe('Esto es una prueba', 'es');

// Text, display, and lang
// describe('Cái này là bài thi', LABEL, 'vi');

// Text and lang
// describe('यह टेस्ट है', 'hi');



// Name and text only (fallback is the default)
// describeElement('a', 'Cái này là bài thi');

// Name, text, and display
// describeElement('b', 'Esto es una prueba', LABEL);

// Name, text, and lang
// describeElement('c', 'Esto es una prueba', 'es');

// Name, text, display, and lang
// describeElement('d', 'Cái này là bài thi', LABEL, 'vi');

// Name, text, and lang
// describeElement('e', 'यह टेस्ट है', 'hi');

fill('blue');
circle(200, 150, 100);
}
Loading
Loading