From 708b6f991d1f37013a69f5e880f44ac267131661 Mon Sep 17 00:00:00 2001 From: Thijs Louisse Date: Wed, 27 Nov 2019 13:25:59 +0100 Subject: [PATCH 1/3] feat(localize): add getCurrencyName --- packages/localize/index.js | 1 + .../localize/src/number/getCurrencyName.js | 22 +++++++++++++++++++ .../test/number/getCurrencyName.test.js | 14 ++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 packages/localize/src/number/getCurrencyName.js create mode 100644 packages/localize/test/number/getCurrencyName.test.js diff --git a/packages/localize/index.js b/packages/localize/index.js index 28534aa83..86412a7c8 100644 --- a/packages/localize/index.js +++ b/packages/localize/index.js @@ -6,6 +6,7 @@ export { normalizeDateTime } from './src/date/normalizeDateTime.js'; export { parseDate } from './src/date/parseDate.js'; export { formatNumber } from './src/number/formatNumber.js'; export { formatNumberToParts } from './src/number/formatNumberToParts.js'; +export { getCurrencyName } from './src/number/getCurrencyName.js'; export { getDecimalSeparator } from './src/number/getDecimalSeparator.js'; export { getFractionDigits } from './src/number/getFractionDigits.js'; export { getGroupSeparator } from './src/number/getGroupSeparator.js'; diff --git a/packages/localize/src/number/getCurrencyName.js b/packages/localize/src/number/getCurrencyName.js new file mode 100644 index 000000000..b2cc8e3dc --- /dev/null +++ b/packages/localize/src/number/getCurrencyName.js @@ -0,0 +1,22 @@ +import { formatNumberToParts } from './formatNumberToParts.js'; + +/** + * Based on number, returns currency name like 'US dollar' + * + * @param {string} currencyIso iso code like USD + * @param {Object} options Intl options are available extended by roundMode + * @returns {string} currency name like 'US dollar' + */ +export function getCurrencyName(currencyIso, options) { + const parts = formatNumberToParts(1, { + ...options, + style: 'currency', + currency: currencyIso, + currencyDisplay: 'name', + }); + const currencyName = parts + .filter(p => p.type === 'currency') + .map(o => o.value) + .join(' '); + return currencyName; +} diff --git a/packages/localize/test/number/getCurrencyName.test.js b/packages/localize/test/number/getCurrencyName.test.js new file mode 100644 index 000000000..dcb0776d6 --- /dev/null +++ b/packages/localize/test/number/getCurrencyName.test.js @@ -0,0 +1,14 @@ +import { expect } from '@open-wc/testing'; +import { localizeTearDown } from '../../test-helpers.js'; +import { getCurrencyName } from '../../src/number/getCurrencyName.js'; + +describe('getCurrencyName', () => { + afterEach(localizeTearDown); + + it('returns the right name for currency and locale combination', async () => { + expect(getCurrencyName('USD', { locale: 'en-GB' })).to.equal('US dollars'); + expect(getCurrencyName('USD', { locale: 'nl-NL' })).to.equal('Amerikaanse dollar'); + expect(getCurrencyName('EUR', { locale: 'en-GB' })).to.equal('euros'); + expect(getCurrencyName('EUR', { locale: 'nl-NL' })).to.equal('euro'); + }); +}); From 76492af889bc35b99a3aaf9d3315a9f6c896d43d Mon Sep 17 00:00:00 2001 From: Thijs Louisse Date: Wed, 27 Nov 2019 13:29:40 +0100 Subject: [PATCH 2/3] fix(field): getAriaElementsInRightDomOrder IE fix --- .../field/src/utils/getAriaElementsInRightDomOrder.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/field/src/utils/getAriaElementsInRightDomOrder.js b/packages/field/src/utils/getAriaElementsInRightDomOrder.js index 7bc3a17da..549f4ba04 100644 --- a/packages/field/src/utils/getAriaElementsInRightDomOrder.js +++ b/packages/field/src/utils/getAriaElementsInRightDomOrder.js @@ -1,3 +1,5 @@ +const isIE11 = /Trident/.test(window.navigator.userAgent); + /** * @desc Let the order of adding ids to aria element by DOM order, so that the screen reader * respects visual order when reading: @@ -5,16 +7,16 @@ * @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 - * */ export function getAriaElementsInRightDomOrder(descriptionElements, { reverse } = {}) { const putPrecedingSiblingsAndLocalParentsFirst = (a, b) => { // https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition const pos = a.compareDocumentPosition(b); + // Unfortunately, for IE, we have to switch the order (?) if (pos === Node.DOCUMENT_POSITION_PRECEDING || pos === Node.DOCUMENT_POSITION_CONTAINED_BY) { - return 1; + return isIE11 ? -1 : 1; } - return -1; + return isIE11 ? 1 : -1; }; const descriptionEls = descriptionElements.filter(el => el); // filter out null references From 0d687bbe1e953b2b559737b9825cb055a2e9c75a Mon Sep 17 00:00:00 2001 From: Thijs Louisse Date: Wed, 27 Nov 2019 13:30:55 +0100 Subject: [PATCH 3/3] fix(input-amount): a11y currency label --- packages/input-amount/src/LionInputAmount.js | 20 ++++++++++++++-- .../test/lion-input-amount.test.js | 23 +++++++++++++++++-- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/packages/input-amount/src/LionInputAmount.js b/packages/input-amount/src/LionInputAmount.js index d49c950b8..b0d03820e 100644 --- a/packages/input-amount/src/LionInputAmount.js +++ b/packages/input-amount/src/LionInputAmount.js @@ -1,5 +1,5 @@ import { css } from '@lion/core'; -import { LocalizeMixin } from '@lion/localize'; +import { LocalizeMixin, getCurrencyName } from '@lion/localize'; import { LionInput } from '@lion/input'; import { FieldCustomMixin } from '@lion/field'; import { IsNumber } from '@lion/validate'; @@ -34,6 +34,9 @@ export class LionInputAmount extends FieldCustomMixin(LocalizeMixin(LionInput)) after: () => { if (this.currency) { const el = document.createElement('span'); + // The data-label attribute will make sure that FormControl adds this to + // input[aria-labelledby] + el.setAttribute('data-label', ''); el.textContent = this.currency; return el; } @@ -54,14 +57,27 @@ export class LionInputAmount extends FieldCustomMixin(LocalizeMixin(LionInput)) // eslint-disable-next-line wc/guard-super-call super.connectedCallback(); this.type = 'text'; + + if (this.currency) { + this.__setCurrencyDisplayLabel(); + } + } + + __setCurrencyDisplayLabel() { + this._currencyDisplayNode.setAttribute('aria-label', getCurrencyName(this.currency)); + } + + get _currencyDisplayNode() { + return Array.from(this.children).find(child => child.slot === 'after'); } _onCurrencyChanged({ currency }) { if (this._isPrivateSlot('after')) { - Array.from(this.children).find(child => child.slot === 'after').textContent = currency; + this._currencyDisplayNode.textContent = currency; } this.formatOptions.currency = currency; this._calculateValues(); + this.__setCurrencyDisplayLabel(); } static get styles() { diff --git a/packages/input-amount/test/lion-input-amount.test.js b/packages/input-amount/test/lion-input-amount.test.js index e908f5a22..03ea82489 100644 --- a/packages/input-amount/test/lion-input-amount.test.js +++ b/packages/input-amount/test/lion-input-amount.test.js @@ -69,9 +69,9 @@ describe('', () => { expect(el._inputNode.type).to.equal('text'); }); - it('shows no currency', async () => { + it('shows no currency by default', async () => { const el = await fixture(``); - expect(Array.from(el.children).find(child => child.slot === 'suffix')).to.be.undefined; + expect(Array.from(el.children).find(child => child.slot === 'after')).to.be.undefined; }); it('displays currency if provided', async () => { @@ -99,4 +99,23 @@ describe('', () => { 'my-currency', ); }); + + describe('Accessibility', () => { + it('adds currency id to aria-labelledby of input', async () => { + const el = await fixture(``); + expect(el._currencyDisplayNode.getAttribute('data-label')).to.be.not.null; + expect(el._inputNode.getAttribute('aria-labelledby')).to.contain(el._currencyDisplayNode.id); + }); + + it('adds an aria-label to currency slot', async () => { + const el = await fixture(``); + expect(el._currencyDisplayNode.getAttribute('aria-label')).to.equal('euros'); + el.currency = 'USD'; + await el.updateComplete; + expect(el._currencyDisplayNode.getAttribute('aria-label')).to.equal('US dollars'); + el.currency = 'PHP'; + await el.updateComplete; + expect(el._currencyDisplayNode.getAttribute('aria-label')).to.equal('Philippine pisos'); + }); + }); });