Skip to content
Merged
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
21 changes: 10 additions & 11 deletions src/static/js/vendors/html10n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,16 @@ export class Html10n {
// @ts-ignore
node[prop] = str.str!
populateAriaLabel()
} else if (node.tagName === 'SELECT' || node.tagName === 'INPUT' ||
node.tagName === 'TEXTAREA') {
// Form-controllable elements carry their accessible name on aria-label
// rather than as text content — a <select>'s text is its <option>
// labels, not its own name. Plugins that put `data-l10n-id` on a
// <select> (ep_headings2, ep_align, ep_font_size, …) used to trigger a
// spurious "could not translate element content" warning here because
// the text-node hunt below finds nothing to write. Short-circuit and
// just localize aria-label. See ether/ep_align#182 review.
populateAriaLabel()
} else {
let children = node.childNodes,
found = false
Expand All @@ -697,17 +707,6 @@ export class Html10n {
if (!found) {
console.warn('Unexpected error: could not translate element content for key '+str.id, node)
}
// Form-controllable elements (<select>, <input>, <textarea>) carry their
// accessible name on aria-label rather than as text content (a <select>'s
// text is its <option> labels, not its own name). The textContent branch
// above doesn't fall through to populateAriaLabel(), so plugins that put
// data-l10n-id on a <select> and rely on the auto-population (introduced
// in #7584) end up with no accessible name. Populate aria-label here so
// those controls stay localized too. See ether/ep_align#182 review.
const tag = node.tagName;
if (tag === 'SELECT' || tag === 'INPUT' || tag === 'TEXTAREA') {
populateAriaLabel()
}
}
}

Expand Down
43 changes: 43 additions & 0 deletions src/tests/frontend-new/specs/html10n_form_controls_aria.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,49 @@ test('html10n auto-populates aria-label on <textarea> with data-l10n-id', async
await expect(ta).toHaveAttribute('data-l10n-aria-label', 'true');
});

test('no "could not translate element content" warning for <select data-l10n-id>', async ({page}) => {
// Regression: thm reported the warning on Etherpad 3.1.0 because the
// <select data-l10n-id="ep_headings.style"> in ep_headings2 has only
// <option> element children — the text-node hunt in translateNode finds
// nothing and used to warn before the SELECT/INPUT/TEXTAREA short-circuit
// landed. The accessible name still ends up on aria-label.
//
// To actually exercise the bug path the key must default `prop` to
// textContent (i.e. the suffix after the last `.` must NOT be one of
// title|innerHTML|alt|textContent|value|placeholder, see
// html10n.translateNode). `pad.loading` is a stable pad-bundle key that
// satisfies that.
const warnings: string[] = [];
page.on('console', (msg) => {
if (msg.type() === 'warning') warnings.push(msg.text());
});

await page.evaluate(() => new Promise<void>((res) => {
const sel = document.createElement('select');
sel.id = 'html10n-test-no-warn';
sel.setAttribute('data-l10n-id', 'pad.loading');
for (const v of ['a', 'b', 'c']) {
const opt = document.createElement('option');
opt.value = v;
opt.textContent = v.toUpperCase();
sel.appendChild(opt);
}
document.body.appendChild(sel);
// localize completes asynchronously inside a build() callback; wait
// on the `localized` event instead of a fixed timeout so the test
// is deterministic on slow CI runners.
// @ts-ignore window.html10n is exposed by pad.ts
window.html10n.mt.bind('localized', () => res());
// @ts-ignore
window.html10n.localize(['en']);
}));

const offending = warnings.filter((m) =>
m.includes('could not translate element content') &&
m.includes('pad.loading'));
expect(offending).toEqual([]);
});

test('an author-supplied aria-label on a form control is preserved', async ({page}) => {
// Mirror the existing semantics for non-form-control elements: if the
// template author wrote their own aria-label, html10n must not
Expand Down
Loading