-
Notifications
You must be signed in to change notification settings - Fork 779
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(commons/standards): create the commons/standards object for help…
…er functions against the standards table (#2358) * feat(commons/standards): create the commons/standards object for helper functions against the standards table * Update lib/commons/standards/get-html-element-spec.js Co-authored-by: Wilco Fiers <[email protected]> * rename * comments Co-authored-by: Wilco Fiers <[email protected]>
- Loading branch information
1 parent
f6b3484
commit 6dce974
Showing
10 changed files
with
350 additions
and
8 deletions.
There are no files selected for viewing
This file contains 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 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,14 @@ | ||
import standards from '../../standards'; | ||
|
||
/** | ||
* Return a list of aria roles whose type matches the provided value. | ||
* @param {String} type The desired role type | ||
* @return {String[]} List of all roles matching the type | ||
*/ | ||
function getAriaRolesByType(type) { | ||
return Object.keys(standards.ariaRoles).filter(roleName => { | ||
return standards.ariaRoles[roleName].type === type; | ||
}); | ||
} | ||
|
||
export default getAriaRolesByType; |
This file contains 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,49 @@ | ||
import standards from '../../standards'; | ||
import matchesFn from '../../commons/matches'; | ||
|
||
/** | ||
* Return the spec for an HTML element from the standards object. Since the spec is determined by the node and what attributes it has, a node is required. | ||
* @param {VirtualNode} vNode The VirtualNode to get the spec for. | ||
* @return {Object} The standard spec object | ||
*/ | ||
function getElementSpec(vNode) { | ||
const standard = standards.htmlElms[vNode.props.nodeName]; | ||
|
||
if (!standard.variant) { | ||
return standard; | ||
} | ||
|
||
// start with the information at the top level | ||
const { variant, ...spec } = standard; | ||
|
||
// loop through all variants (excluding default) finding anything | ||
// that matches | ||
for (const variantName in variant) { | ||
if (!variant.hasOwnProperty(variantName) || variantName === 'default') { | ||
continue; | ||
} | ||
|
||
const { matches, ...props } = variant[variantName]; | ||
if (matchesFn(vNode, matches)) { | ||
for (const propName in props) { | ||
if (props.hasOwnProperty(propName)) { | ||
spec[propName] = props[propName]; | ||
} | ||
} | ||
} | ||
} | ||
|
||
// apply defaults if properties were not found | ||
for (const propName in variant.default) { | ||
if ( | ||
variant.default.hasOwnProperty(propName) && | ||
typeof spec[propName] === 'undefined' | ||
) { | ||
spec[propName] = variant.default[propName]; | ||
} | ||
} | ||
|
||
return spec; | ||
} | ||
|
||
export default getElementSpec; |
This file contains 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,13 @@ | ||
import standards from '../../standards'; | ||
|
||
/** | ||
* Return a list of global aria attributes. | ||
* @return {String[]} List of all global aria attributes | ||
*/ | ||
function getGlobalAriaAttrs() { | ||
return Object.keys(standards.ariaAttrs).filter(attrName => { | ||
return standards.ariaAttrs[attrName].global; | ||
}); | ||
} | ||
|
||
export default getGlobalAriaAttrs; |
This file contains 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,8 @@ | ||
/** | ||
* Namespace for standards-related utilities. | ||
* @namespace commons.standards | ||
* @memberof axe | ||
*/ | ||
export { default as getAriaRolesByType } from './get-aria-roles-by-type'; | ||
export { default as getGlobalAriaAttrs } from './get-global-aria-attrs'; | ||
export { default as getElementSpec } from './get-element-spec'; |
This file contains 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 |
---|---|---|
@@ -1,18 +1,33 @@ | ||
import ariaAttrs from './aria-attrs'; | ||
import { clone, deepMerge } from '../core/utils'; | ||
import ariaRoles from './aria-roles'; | ||
import dpubRoles from './dpub-roles'; | ||
import htmlElms from './html-elms'; | ||
import { deepMerge } from '../core/utils'; | ||
|
||
const origAriaAttrs = clone(ariaAttrs); | ||
const originals = { | ||
ariaAttrs, | ||
ariaRoles: { | ||
...ariaRoles, | ||
...dpubRoles | ||
}, | ||
htmlElms | ||
}; | ||
const standards = { | ||
ariaAttrs | ||
...originals | ||
}; | ||
|
||
export function configureStandards(config) { | ||
if (config.ariaAttrs) { | ||
standards.ariaAttrs = deepMerge(standards.ariaAttrs, config.ariaAttrs); | ||
} | ||
Object.keys(standards).forEach(propName => { | ||
if (config[propName]) { | ||
standards[propName] = deepMerge(standards[propName], config[propName]); | ||
} | ||
}); | ||
} | ||
|
||
export function resetStandards() { | ||
standards.ariaAttrs = origAriaAttrs; | ||
Object.keys(standards).forEach(propName => { | ||
standards[propName] = originals[propName]; | ||
}); | ||
} | ||
|
||
export default standards; |
This file contains 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 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,72 @@ | ||
describe('standards.getAriaRolesByType', function() { | ||
var getAriaRolesByType = axe.commons.standards.getAriaRolesByType; | ||
|
||
before(function() { | ||
axe._load({}); | ||
}); | ||
|
||
after(function() { | ||
axe.reset(); | ||
}); | ||
|
||
it('should return a list of role names by type', function() { | ||
// Source: https://www.w3.org/TR/wai-aria-1.1/#document_structure_roles | ||
var structureRoles = getAriaRolesByType('structure'); | ||
assert.deepEqual(structureRoles, [ | ||
'article', | ||
'cell', | ||
'columnheader', | ||
'definition', | ||
'directory', | ||
'document', | ||
'feed', | ||
'figure', | ||
'group', | ||
'heading', | ||
'img', | ||
'list', | ||
'listitem', | ||
'math', | ||
'note', | ||
'presentation', | ||
'row', | ||
'rowgroup', | ||
'rowheader', | ||
'separator', | ||
'table', | ||
'term', | ||
'toolbar', | ||
'tooltip' | ||
]); | ||
}); | ||
|
||
it('should return configured roles', function() { | ||
axe.configure({ | ||
standards: { | ||
ariaRoles: { | ||
myRole: { | ||
type: 'structure' | ||
} | ||
} | ||
} | ||
}); | ||
|
||
var structureRoles = getAriaRolesByType('structure'); | ||
assert.include(structureRoles, 'myRole'); | ||
}); | ||
|
||
it('should not return role that is configured to not be of the type', function() { | ||
axe.configure({ | ||
standards: { | ||
ariaRoles: { | ||
article: { | ||
type: 'notstructure' | ||
} | ||
} | ||
} | ||
}); | ||
|
||
var structureRoles = getAriaRolesByType('structure'); | ||
assert.notInclude(structureRoles, 'article'); | ||
}); | ||
}); |
This file contains 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,99 @@ | ||
describe('standards.getElementSpec', function() { | ||
var getElementSpec = axe.commons.standards.getElementSpec; | ||
var queryFixture = axe.testUtils.queryFixture; | ||
var fixture = document.querySelector('#fixture'); | ||
|
||
before(function() { | ||
axe._load({}); | ||
}); | ||
|
||
afterEach(function() { | ||
fixture.innerHTML = ''; | ||
}); | ||
|
||
after(function() { | ||
axe.reset(); | ||
}); | ||
|
||
it('should return a spec for an element without variants', function() { | ||
axe.configure({ | ||
standards: { | ||
htmlElms: { | ||
abbr: { | ||
contentTypes: ['phrasing', 'flow'], | ||
allowedRoles: true | ||
} | ||
} | ||
} | ||
}); | ||
|
||
var vNode = queryFixture('<abbr id="target"></abbr>'); | ||
assert.deepEqual(getElementSpec(vNode), { | ||
contentTypes: ['phrasing', 'flow'], | ||
allowedRoles: true | ||
}); | ||
}); | ||
|
||
describe('variants', function() { | ||
before(function() { | ||
axe.configure({ | ||
standards: { | ||
htmlElms: { | ||
abbr: { | ||
variant: { | ||
controls: { | ||
matches: '[controls]', | ||
customProp: 'controls' | ||
}, | ||
label: { | ||
matches: '[aria-label]', | ||
anotherProp: 'label' | ||
}, | ||
default: { | ||
customProp: 'default', | ||
anotherProp: 'default' | ||
} | ||
}, | ||
allowedRoles: false | ||
} | ||
} | ||
} | ||
}); | ||
}); | ||
|
||
it('should return top level properties', function() { | ||
var vNode = queryFixture('<abbr id="target" controls></abbr>'); | ||
var spec = getElementSpec(vNode); | ||
assert.equal(spec.allowedRoles, false); | ||
}); | ||
|
||
it('should return properties from matching variant', function() { | ||
var vNode = queryFixture('<abbr id="target" controls></abbr>'); | ||
var spec = getElementSpec(vNode); | ||
assert.equal(spec.customProp, 'controls'); | ||
}); | ||
|
||
it('should return all properties from matching variants', function() { | ||
var vNode = queryFixture( | ||
'<abbr id="target" controls aria-label="foo"></abbr>' | ||
); | ||
var spec = getElementSpec(vNode); | ||
assert.equal(spec.customProp, 'controls'); | ||
assert.equal(spec.anotherProp, 'label'); | ||
}); | ||
|
||
it('should return default props in no variants match', function() { | ||
var vNode = queryFixture('<abbr id="target"></abbr>'); | ||
var spec = getElementSpec(vNode); | ||
assert.equal(spec.customProp, 'default'); | ||
assert.equal(spec.anotherProp, 'default'); | ||
}); | ||
|
||
it('should return default props that were not part of other matches', function() { | ||
var vNode = queryFixture('<abbr id="target" controls></abbr>'); | ||
var spec = getElementSpec(vNode); | ||
assert.equal(spec.customProp, 'controls'); | ||
assert.equal(spec.anotherProp, 'default'); | ||
}); | ||
}); | ||
}); |
This file contains 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,69 @@ | ||
describe('standards.getGlobalAriaAttrs', function() { | ||
var getGlobalAriaAttrs = axe.commons.standards.getGlobalAriaAttrs; | ||
|
||
before(function() { | ||
axe._load({}); | ||
}); | ||
|
||
after(function() { | ||
axe.reset(); | ||
}); | ||
|
||
it('should return global attrs', function() { | ||
// Source: https://www.w3.org/TR/wai-aria-1.1/#global_states | ||
var globalAttrs = getGlobalAriaAttrs(); | ||
assert.deepEqual(globalAttrs, [ | ||
'aria-atomic', | ||
'aria-busy', | ||
'aria-controls', | ||
'aria-current', | ||
'aria-describedby', | ||
'aria-details', | ||
'aria-disabled', | ||
'aria-dropeffect', | ||
'aria-errormessage', | ||
'aria-flowto', | ||
'aria-grabbed', | ||
'aria-haspopup', | ||
'aria-hidden', | ||
'aria-invalid', | ||
'aria-keyshortcuts', | ||
'aria-label', | ||
'aria-labelledby', | ||
'aria-live', | ||
'aria-owns', | ||
'aria-relevant', | ||
'aria-roledescription' | ||
]); | ||
}); | ||
|
||
it('should return configured global attrs', function() { | ||
axe.configure({ | ||
standards: { | ||
ariaAttrs: { | ||
myAttr: { | ||
global: true | ||
} | ||
} | ||
} | ||
}); | ||
|
||
var globalAttrs = getGlobalAriaAttrs(); | ||
assert.include(globalAttrs, 'myAttr'); | ||
}); | ||
|
||
it('should not return global attr that is configured to not be global', function() { | ||
axe.configure({ | ||
standards: { | ||
ariaAttrs: { | ||
'aria-atomic': { | ||
global: false | ||
} | ||
} | ||
} | ||
}); | ||
|
||
var globalAttrs = getGlobalAriaAttrs(); | ||
assert.notInclude(globalAttrs, 'aria-atomic'); | ||
}); | ||
}); |