-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Add describe() and describeElement() #4654
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 35 commits
Commits
Show all changes
42 commits
Select commit
Hold shift + click to select a range
c2ce19e
add accessibility folder and describe()/describeElement()
a684c01
add accessibility folder and describe()/describeElement()
6bf8657
add constants for describe() and describeElement()
a1980ed
accessibility module)
2565a0f
fix app.js to add accessibility module
b62d262
improved examples for describe()
412985e
update element Ids
be77921
update styling
50ee933
delete extra space
d40b7db
better descriptions
52e4d6f
add regex
8bee297
changes in description of functions
6c03031
add class for labels, improve descriptions
9748310
round numbers in example
cfa50bc
use global vars
ea72150
comments on regx
ef8f786
use consts for element ids
397c9a6
first tests
9bdbf60
updates tests
b8f4065
change elements ids
b992cc9
update const names
c0e97e6
fix errors when parameter not string
d28392c
add tests/change equal for deepEqual
156b0f6
update description of functions
e1fb044
fix typo in comment
af377be
add regex cases + fix regex
49691a3
add tests
95631a3
simplify false
d64db75
verbose variable names
144b6dc
optimize regex
7bd6ea8
fix comparing to null
7d29c42
use template literals
4419259
store DOM elements
16a56e2
store DOM elements
538abf0
no caps in regex
311f7a1
reorder functions
096cbc5
reorder functions
b83d6e3
skip tests
ba87ef6
fix test issues
cb24906
:hibiscus:fix tests and move to dummy DOM
6464da3
remove special characters of element IDs
2ca1f72
add comments to code
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,286 @@ | ||
/** | ||
* @module Environment | ||
* @submodule Environment | ||
* @for p5 | ||
* @requires core | ||
*/ | ||
|
||
import p5 from '../core/main'; | ||
const descContainer = '_Description'; //Fallback container | ||
const fallbackDescId = '_fallbackDesc'; //Fallback description | ||
const fallbackTableId = '_fallbackTable'; //Fallback Table | ||
const fallbackTableElId = '_fte_'; //Fallback Table Element | ||
const labelContainer = '_Label'; //Label container | ||
const labelDescId = '_labelDesc'; //Label description | ||
const labelTableId = '_labelTable'; //Label Table | ||
const labelTableElId = '_lte_'; //Label Table Element | ||
let fallbackDesc = ''; //stores latest fallback description | ||
let labelDesc = ''; //stores latest label description | ||
let fallbackElements = {}; //stores elements by name | ||
let labelElements = {}; //stores elements by name | ||
|
||
/** | ||
* Creates a screen-reader accessible description for the canvas. | ||
* The first parameter should be a string with a description of the canvas. | ||
* The second parameter is optional. If specified, it determines how the | ||
* description is displayed. | ||
* | ||
* <code class="language-javascript">describe(text, LABEL)</code> displays | ||
* the description to all users as a <a | ||
* href="https://en.wikipedia.org/wiki/Museum_label" target="_blank"> | ||
* tombstone or exhibit label/caption</a> in a | ||
* <code class="language-javascript"><div class="p5Label"></div></code> | ||
* adjacent to the canvas. You can style it as you wish in your CSS. | ||
* | ||
* <code class="language-javascript">describe(text, FALLBACK)</code> makes the | ||
* description accessible to screen-reader users only, in | ||
* <a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Hit_regions_and_accessibility" target="_blank"> | ||
* a sub DOM inside the canvas element</a>. If a second parameter is not | ||
* specified, by default, the description will only be available to | ||
* screen-reader users. | ||
* | ||
* @method describe | ||
* @param {String} text description of the canvas | ||
* @param {Constant} [display] either LABEL or FALLBACK (Optional) | ||
* | ||
* @example | ||
* <div> | ||
* <code> | ||
* describe('pink square with red heart in the bottom right corner', LABEL); | ||
* background('pink'); | ||
* fill('red'); | ||
* noStroke(); | ||
* ellipse(67, 67, 20, 20); | ||
* ellipse(83, 67, 20, 20); | ||
* triangle(91, 73, 75, 95, 59, 73); | ||
* </code> | ||
* </div> | ||
* | ||
* <div> | ||
* <code> | ||
* let x = 0; | ||
* function draw() { | ||
* if (x > 100) { | ||
* x = 0; | ||
* } | ||
* background(220); | ||
* fill(0, 255, 0); | ||
* ellipse(x, 50, 40, 40); | ||
* x = x + 0.1; | ||
* describe('green circle at x pos ' + round(x) + ' moving to the right'); | ||
* } | ||
* </code> | ||
* </div> | ||
* | ||
*/ | ||
|
||
p5.prototype.describe = function(text, display) { | ||
const cnvId = this.canvas.id; | ||
p5._validateParameters('describe', arguments); | ||
//Creates a sub DOM inside of the canvas element and populates | ||
//it with description text. | ||
if (typeof text !== 'string') { | ||
return; | ||
} | ||
text = _descriptionText(text); | ||
if (!document.getElementById(cnvId + descContainer)) { | ||
document.getElementById(cnvId).innerHTML = `<div id="${cnvId + | ||
descContainer}" role="region" aria-label="Canvas Description"><p id="${cnvId + | ||
fallbackDescId}"></p></div>`; | ||
} else if (!document.getElementById(cnvId + fallbackDescId)) { | ||
document | ||
.getElementById(cnvId + fallbackTableId) | ||
.insertAdjacentHTML( | ||
'beforebegin', | ||
`<p id="${cnvId + fallbackDescId}"></p>` | ||
); | ||
} | ||
//updates description | ||
if (fallbackDesc !== text) { | ||
document.getElementById(cnvId + fallbackDescId).innerHTML = text; | ||
fallbackDesc = text; | ||
} | ||
//If display is LABEL creates a div adjacent to the canvas element with | ||
//description text. | ||
if (display === this.LABEL) { | ||
if (!document.getElementById(cnvId + labelContainer)) { | ||
document | ||
.getElementById(cnvId) | ||
.insertAdjacentHTML( | ||
'afterend', | ||
`<div id="${cnvId + labelContainer}" class="p5Label"><p id="${cnvId + | ||
labelDescId}"></p></div>` | ||
); | ||
} else if (!document.getElementById(cnvId + labelDescId)) { | ||
document | ||
.getElementById(cnvId + labelTableId) | ||
.insertAdjacentHTML( | ||
'beforebegin', | ||
`<p id="${cnvId}${labelDescId}"></p>` | ||
); | ||
} | ||
if (labelDesc !== text) { | ||
document.getElementById(cnvId + labelDescId).innerHTML = text; | ||
labelDesc = text; | ||
} | ||
} | ||
}; | ||
|
||
/** | ||
* Helper function for describe() and describeElement(). | ||
*/ | ||
function _descriptionText(text) { | ||
if (text === 'label' || text === 'fallback') { | ||
throw new Error('description should not be LABEL or FALLBACK'); | ||
} | ||
//if string does not end with '.' | ||
if (!text.endsWith('.') && !text.endsWith('?') && !text.endsWith('!')) { | ||
//add '.' to the end of string | ||
text = text + '.'; | ||
} | ||
return text; | ||
} | ||
|
||
/** | ||
* This function creates a screen-reader accessible | ||
* description for elements —shapes or groups of shapes that create | ||
* meaning together— in the canvas. The first paramater should | ||
* be the name of the element. The second parameter should be a string | ||
* with a description of the element. The third parameter is optional. | ||
* If specified, it determines how the element description is displayed. | ||
* | ||
* <code class="language-javascript">describeElement(name, text, LABEL)</code> | ||
* displays the element description to all users as a | ||
* <a href="https://en.wikipedia.org/wiki/Museum_label" target="_blank"> | ||
* tombstone or exhibit label/caption</a> in a | ||
* <code class="language-javascript"><div class="p5Label"></div></code> | ||
* adjacent to the canvas. You can style it as you wish in your CSS. | ||
* | ||
* <code class="language-javascript">describeElement(name, text, FALLBACK)</code> | ||
* makes the element description accessible to screen-reader users | ||
* only, in <a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Hit_regions_and_accessibility" target="_blank"> | ||
* a sub DOM inside the canvas element</a>. If a second parameter is not | ||
* specified, by default, the element description will only be available | ||
* to screen-reader users. | ||
* | ||
* @method describeElement | ||
* @param {String} name name of the element | ||
* @param {String} text description of the element | ||
* @param {Constant} [display] either LABEL or FALLBACK (Optional) | ||
* | ||
* @example | ||
* <div> | ||
* <code> | ||
* describe('Heart and yellow circle over pink background', LABEL); | ||
* noStroke(); | ||
* background('pink'); | ||
* describeElement('Circle', 'Yellow circle in the top left corner', LABEL); | ||
* fill('yellow'); | ||
* ellipse(25, 25, 40, 40); | ||
* describeElement('Heart', 'red heart in the bottom right corner', LABEL); | ||
* fill('red'); | ||
* ellipse(66.6, 66.6, 20, 20); | ||
* ellipse(83.2, 66.6, 20, 20); | ||
* triangle(91.2, 72.6, 75, 95, 58.6, 72.6); | ||
* </code> | ||
* </div> | ||
*/ | ||
|
||
p5.prototype.describeElement = function(name, text, display) { | ||
p5._validateParameters('describeElement', arguments); | ||
if (typeof text !== 'string' || typeof name !== 'string') { | ||
return; | ||
} | ||
const cnvId = this.canvas.id; | ||
text = _descriptionText(text); | ||
let elementName = _elementName(name); | ||
//Creates a sub DOM inside of the canvas with a table, populates | ||
//a row header cell with the name of the elements and adds the description | ||
//of the element in adjecent cell. | ||
if (!document.getElementById(cnvId + descContainer)) { | ||
document.getElementById(cnvId).innerHTML = `<div id="${cnvId + | ||
descContainer}" role="region" aria-label="Canvas Description"><table id="${cnvId + | ||
fallbackTableId}"><caption>Canvas elements and their descriptions</caption></table></div>`; | ||
} else if (!document.getElementById(cnvId + fallbackTableId)) { | ||
document | ||
.getElementById(cnvId + fallbackDescId) | ||
.insertAdjacentHTML( | ||
'afterend', | ||
`<table id="${cnvId + | ||
fallbackTableId}"><caption>Canvas elements and their descriptions</caption></table>` | ||
); | ||
} | ||
if (!document.getElementById(cnvId + fallbackTableElId + name)) { | ||
let tableRow = document.createElement('tr'); | ||
tableRow.id = cnvId + fallbackTableElId + name; | ||
document.getElementById(cnvId + fallbackTableId).appendChild(tableRow); | ||
} | ||
if ( | ||
fallbackElements[name] !== | ||
`<th scope="row">${elementName}</th><td>${text}</td>` | ||
) { | ||
fallbackElements[ | ||
name | ||
] = `<th scope="row">${elementName}</th><td>${text}</td>`; | ||
document.getElementById(cnvId + fallbackTableElId + name).innerHTML = | ||
fallbackElements[name]; | ||
} | ||
//If display is LABEL creates a div adjacent to the canvas element with | ||
//a table, a row header cell with the name of the elements, | ||
//and adds the description of the element in adjecent cell. | ||
if (display === this.LABEL) { | ||
if (!document.getElementById(cnvId + labelContainer)) { | ||
document | ||
.getElementById(cnvId) | ||
.insertAdjacentHTML( | ||
'afterend', | ||
`<div id="${cnvId + | ||
labelContainer}" class="p5Label"><table id="${cnvId + | ||
labelTableId}"></table></div>` | ||
); | ||
} else if (!document.getElementById(cnvId + labelTableId)) { | ||
document | ||
.getElementById(cnvId + labelDescId) | ||
.insertAdjacentHTML( | ||
'afterend', | ||
`<table id="${cnvId + labelTableId}"></table>` | ||
); | ||
} | ||
if (!document.getElementById(cnvId + labelTableElId + name)) { | ||
let tableRow = document.createElement('tr'); | ||
tableRow.id = cnvId + labelTableElId + name; | ||
document.getElementById(cnvId + labelTableId).appendChild(tableRow); | ||
} | ||
|
||
if ( | ||
labelElements[name] !== | ||
`<th scope="row">${elementName}</th><td>${text}</td>` | ||
) { | ||
labelElements[ | ||
name | ||
] = `<th scope="row">${elementName}</th><td>${text}</td>`; | ||
document.getElementById(cnvId + labelTableElId + name).innerHTML = | ||
labelElements[name]; | ||
} | ||
} | ||
}; | ||
/** | ||
* Helper function for describeElement(). | ||
lm-n marked this conversation as resolved.
Show resolved
Hide resolved
|
||
*/ | ||
function _elementName(name) { | ||
if (name === 'label' || name === 'fallback') { | ||
throw new Error('element name should not be LABEL or FALLBACK'); | ||
} | ||
//check if last character of string n is '.', ';', or ',' | ||
if (name.endsWith('.') || name.endsWith(';') || name.endsWith(',')) { | ||
//replace last character with ':' | ||
name = name.replace(/.$/, ':'); | ||
//if string n does not end with ':' | ||
} else if (!name.endsWith(':')) { | ||
//add ':'' at the end of string | ||
name = name + ':'; | ||
} | ||
return name; | ||
} | ||
|
||
export default p5; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.