Socket
Socket
Sign inDemoInstall

@lion/field

Package Overview
Dependencies
Maintainers
1
Versions
103
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@lion/field - npm Package Compare versions

Comparing version 0.5.0 to 0.6.0

src/utils/getAriaElementsInRightDomOrder.js

11

CHANGELOG.md

@@ -6,2 +6,13 @@ # Change Log

# [0.6.0](https://github.com/ing-bank/lion/compare/@lion/field@0.5.0...@lion/field@0.6.0) (2019-11-22)
### Features
* **field:** order aria attributes based on nodes ([95d553e](https://github.com/ing-bank/lion/commit/95d553e23994181b827a091b724572678bea3b2f))
# [0.5.0](https://github.com/ing-bank/lion/compare/@lion/field@0.4.1...@lion/field@0.5.0) (2019-11-18)

@@ -8,0 +19,0 @@

4

package.json
{
"name": "@lion/field",
"version": "0.5.0",
"version": "0.6.0",
"description": "Fields are the most fundamental building block of the Form System",

@@ -45,3 +45,3 @@ "author": "ing-bank",

},
"gitHead": "da30ee3bb13d26f873d633ed960163bec9cba164"
"gitHead": "3cd6c43e9a24f15b9f87c1761f71573f55f2985e"
}
import { html, css, nothing, dedupeMixin, SlotMixin } from '@lion/core';
import { FormRegisteringMixin } from './FormRegisteringMixin.js';
import { getAriaElementsInRightDomOrder } from './utils/getAriaElementsInRightDomOrder.js';
/**
* Generates random unique identifier (for dom elements)
* @param {string} prefix
*/
function uuid(prefix) {
return `${prefix}-${Math.random()
.toString(36)
.substr(2, 10)}`;
}
/**
* #FormControlMixin :

@@ -21,29 +32,23 @@ *

/**
* A list of ids that will be put on the _inputNode as a serialized string
* When no light dom defined and prop set
*/
_ariaDescribedby: {
type: String,
},
label: String,
/**
* A list of ids that will be put on the _inputNode as a serialized string
* When no light dom defined and prop set
*/
_ariaLabelledby: {
helpText: {
type: String,
attribute: 'help-text',
},
/**
* When no light dom defined and prop set
* Contains all elements that should end up in aria-labelledby of `._inputNode`
*/
label: {
type: String,
},
_ariaLabelledNodes: Array,
/**
* When no light dom defined and prop set
* Contains all elements that should end up in aria-describedby of `._inputNode`
*/
helpText: {
type: String,
attribute: 'help-text',
},
_ariaDescribedNodes: Array,
};

@@ -71,8 +76,16 @@ }

if (changedProps.has('_ariaLabelledby')) {
this._onAriaLabelledbyChanged({ _ariaLabelledby: this._ariaLabelledby });
if (changedProps.has('_ariaLabelledNodes')) {
this.__reflectAriaAttr(
'aria-labelledby',
this._ariaLabelledNodes,
this.__reorderAriaLabelledNodes,
);
}
if (changedProps.has('_ariaDescribedby')) {
this._onAriaDescribedbyChanged({ _ariaDescribedby: this._ariaDescribedby });
if (changedProps.has('_ariaDescribedNodes')) {
this.__reflectAriaAttr(
'aria-describedby',
this._ariaDescribedNodes,
this.__reorderAriaDescribedNodes,
);
}

@@ -107,7 +120,5 @@

super();
this._inputId = `${this.localName}-${Math.random()
.toString(36)
.substr(2, 10)}`;
this._ariaLabelledby = '';
this._ariaDescribedby = '';
this._inputId = uuid(this.localName);
this._ariaLabelledNodes = [];
this._ariaDescribedNodes = [];
}

@@ -124,3 +135,2 @@

*/
_enhanceLightDomClasses() {

@@ -140,22 +150,10 @@ if (this._inputNode) {

_labelNode.setAttribute('for', this._inputId);
_labelNode.id = _labelNode.id || `label-${this._inputId}`;
const labelledById = ` ${_labelNode.id}`;
if (this._ariaLabelledby.indexOf(labelledById) === -1) {
this._ariaLabelledby += ` ${_labelNode.id}`;
}
this.addToAriaLabelledBy(_labelNode, { idPrefix: 'label' });
}
if (_helpTextNode) {
_helpTextNode.id = _helpTextNode.id || `help-text-${this._inputId}`;
const describeIdHelpText = ` ${_helpTextNode.id}`;
if (this._ariaDescribedby.indexOf(describeIdHelpText) === -1) {
this._ariaDescribedby += ` ${_helpTextNode.id}`;
}
this.addToAriaDescribedBy(_helpTextNode, { idPrefix: 'help-text' });
}
if (_feedbackNode) {
_feedbackNode.setAttribute('aria-live', 'polite');
_feedbackNode.id = _feedbackNode.id || `feedback-${this._inputId}`;
const describeIdFeedback = ` ${_feedbackNode.id}`;
if (this._ariaDescribedby.indexOf(describeIdFeedback) === -1) {
this._ariaDescribedby += ` ${_feedbackNode.id}`;
}
this.addToAriaDescribedBy(_feedbackNode, { idPrefix: 'feedback' });
}

@@ -177,8 +175,7 @@ this._enhanceLightDomA11yForAdditionalSlots();

if (element) {
element.id = element.id || `${additionalSlot}-${this._inputId}`;
if (element.hasAttribute('data-label') === true) {
this._ariaLabelledby += ` ${element.id}`;
this.addToAriaLabelledBy(element, { idPrefix: additionalSlot });
}
if (element.hasAttribute('data-description') === true) {
this._ariaDescribedby += ` ${element.id}`;
this.addToAriaDescribedBy(element, { idPrefix: additionalSlot });
}

@@ -190,13 +187,2 @@ }

/**
* Will handle label, prefix/suffix/before/after (if they contain data-label flag attr).
* Also, contents of id references that will be put in the <lion-field>._ariaLabelledby property
* from an external context, will be read by a screen reader.
*/
_onAriaLabelledbyChanged({ _ariaLabelledby }) {
if (this._inputNode) {
this._inputNode.setAttribute('aria-labelledby', _ariaLabelledby);
}
}
/**
* Will handle help text, validation feedback and character counter,

@@ -207,5 +193,10 @@ * prefix/suffix/before/after (if they contain data-description flag attr).

*/
_onAriaDescribedbyChanged({ _ariaDescribedby }) {
__reflectAriaAttr(attrName, nodes, reorder) {
if (this._inputNode) {
this._inputNode.setAttribute('aria-describedby', _ariaDescribedby);
if (reorder) {
// eslint-disable-next-line no-param-reassign
nodes = getAriaElementsInRightDomOrder(nodes);
}
const string = nodes.map(n => n.id).join(' ');
this._inputNode.setAttribute(attrName, string);
}

@@ -475,32 +466,2 @@ }

// aria-labelledby and aria-describedby helpers
// TODO: consider extracting to generic ariaLabel helper mixin
/**
* Let the order of adding ids to aria element by DOM order, so that the screen reader
* respects visual order when reading:
* https://developers.google.com/web/fundamentals/accessibility/focus/dom-order-matters
* @param {array} descriptionElements - holds references to description or label elements whose
* id should be returned
* @returns {array} sorted set of elements based on dom order
*
* TODO: make this method part of a more generic mixin or util and also use for lion-field
*/
static _getAriaElementsInRightDomOrder(descriptionElements) {
const putPrecedingSiblingsAndLocalParentsFirst = (a, b) => {
// https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition
const pos = a.compareDocumentPosition(b);
if (
pos === Node.DOCUMENT_POSITION_PRECEDING ||
pos === Node.DOCUMENT_POSITION_CONTAINED_BY
) {
return 1;
}
return -1;
};
const descriptionEls = descriptionElements.filter(el => el); // filter out null references
return descriptionEls.sort(putPrecedingSiblingsAndLocalParentsFirst);
}
// Returns dom references to all elements that should be referred to by field(s)

@@ -513,8 +474,12 @@ _getAriaDescriptionElements() {

* Meant for Application Developers wanting to add to aria-labelledby attribute.
* @param {string} id - should be the id of an element that contains the label for the
* concerned field or fieldset, living in the same shadow root as the host element of field or
* fieldset.
* @param {Element} element
*/
addToAriaLabel(id) {
this._ariaLabelledby += ` ${id}`;
addToAriaLabelledBy(element, { idPrefix, reorder } = { reorder: true }) {
// eslint-disable-next-line no-param-reassign
element.id = element.id || `${idPrefix}-${this._inputId}`;
if (!this._ariaLabelledNodes.includes(element)) {
this._ariaLabelledNodes = [...this._ariaLabelledNodes, element];
// This value will be read when we need to reflect to attr
this.__reorderAriaLabelledNodes = Boolean(reorder);
}
}

@@ -524,8 +489,12 @@

* Meant for Application Developers wanting to add to aria-describedby attribute.
* @param {string} id - should be the id of an element that contains the label for the
* concerned field or fieldset, living in the same shadow root as the host element of field or
* fieldset.
* @param {Element} element
*/
addToAriaDescription(id) {
this._ariaDescribedby += ` ${id}`;
addToAriaDescribedBy(element, { idPrefix, reorder } = { reorder: true }) {
// eslint-disable-next-line no-param-reassign
element.id = element.id || `${idPrefix}-${this._inputId}`;
if (!this._ariaDescribedNodes.includes(element)) {
this._ariaDescribedNodes = [...this._ariaDescribedNodes, element];
// This value will be read when we need to reflect to attr
this.__reorderAriaDescribedNodes = Boolean(reorder);
}
}

@@ -532,0 +501,0 @@

@@ -18,3 +18,2 @@ import {

const nameSuffix = '';
const tagString = 'lion-field';

@@ -201,3 +200,3 @@ const tag = unsafeStatic(tagString);

describe(`A11y${nameSuffix}`, () => {
describe('Accessibility', () => {
it(`by setting corresponding aria-labelledby (for label) and aria-describedby (for helpText, feedback)

@@ -226,5 +225,5 @@ ~~~

expect(nativeInput.getAttribute('aria-labelledby')).to.equal(` label-${el._inputId}`);
expect(nativeInput.getAttribute('aria-describedby')).to.contain(` help-text-${el._inputId}`);
expect(nativeInput.getAttribute('aria-describedby')).to.contain(` feedback-${el._inputId}`);
expect(nativeInput.getAttribute('aria-labelledby')).to.equal(`label-${el._inputId}`);
expect(nativeInput.getAttribute('aria-describedby')).to.contain(`help-text-${el._inputId}`);
expect(nativeInput.getAttribute('aria-describedby')).to.contain(`feedback-${el._inputId}`);
});

@@ -245,6 +244,6 @@

expect(nativeInput.getAttribute('aria-labelledby')).to.contain(
` before-${el._inputId} after-${el._inputId}`,
`before-${el._inputId} after-${el._inputId}`,
);
expect(nativeInput.getAttribute('aria-describedby')).to.contain(
` prefix-${el._inputId} suffix-${el._inputId}`,
`prefix-${el._inputId} suffix-${el._inputId}`,
);

@@ -254,4 +253,4 @@ });

// TODO: put this test on FormControlMixin test once there
it(`allows to add to aria description or label via addToAriaLabel() and
addToAriaDescription()`, async () => {
it(`allows to add to aria description or label via addToAriaLabelledBy() and
addToAriaDescribedBy()`, async () => {
const wrapper = await fixture(html`

@@ -277,3 +276,3 @@ <div id="wrapper">

expect(_inputNode.getAttribute('aria-labelledby')).to.contain(`label-${el._inputId}`);
el.addToAriaLabel('additionalLabel');
el.addToAriaLabelledBy(wrapper.querySelector('#additionalLabel'));
// Now check if ids are added to the end (not overridden)

@@ -290,3 +289,3 @@ expect(_inputNode.getAttribute('aria-labelledby')).to.contain(`label-${el._inputId}`);

expect(_inputNode.getAttribute('aria-describedby')).to.contain(`feedback-${el._inputId}`);
el.addToAriaDescription('additionalDescription');
el.addToAriaDescribedBy(wrapper.querySelector('#additionalDescription'));
// Now check if ids are added to the end (not overridden)

@@ -302,3 +301,3 @@ expect(_inputNode.getAttribute('aria-describedby')).to.contain(`feedback-${el._inputId}`);

describe(`Validation${nameSuffix}`, () => {
describe(`Validation`, () => {
beforeEach(() => {

@@ -425,3 +424,3 @@ // Reset and preload validation translations

describe(`Content projection${nameSuffix}`, () => {
describe(`Content projection`, () => {
it('renders correctly all slot elements in light DOM', async () => {

@@ -428,0 +427,0 @@ const el = await fixture(html`

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc