mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-18 11:17:19 +08:00
fix(datetime, input, textarea): remove aria-labelledby when there is no adjacent ion-label (#23211)
* fix(datetime, input, textarea): remove aria-labelledby when there is no adjacent ion-label * Update core/src/components/input/test/a11y/input.spec.ts Co-authored-by: Liam DeBeasi <liamdebeasi@users.noreply.github.com> * Update core/src/components/textarea/test/a11y/textarea.spec.ts Co-authored-by: Liam DeBeasi <liamdebeasi@users.noreply.github.com> * Update core/src/components/datetime/test/a11y/datetime.spec.ts Co-authored-by: Liam DeBeasi <liamdebeasi@users.noreply.github.com> * remove hard-coded label, fix whitespace Co-authored-by: Liam DeBeasi <liamdebeasi@users.noreply.github.com>
This commit is contained in:
@ -641,7 +641,7 @@ export class Datetime implements ComponentInterface {
|
|||||||
aria-disabled={disabled ? 'true' : null}
|
aria-disabled={disabled ? 'true' : null}
|
||||||
aria-expanded={`${isExpanded}`}
|
aria-expanded={`${isExpanded}`}
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
aria-labelledby={labelId}
|
aria-labelledby={label ? labelId : null}
|
||||||
class={{
|
class={{
|
||||||
[mode]: true,
|
[mode]: true,
|
||||||
'datetime-disabled': disabled,
|
'datetime-disabled': disabled,
|
||||||
|
30
core/src/components/datetime/test/a11y/datetime.spec.ts
Normal file
30
core/src/components/datetime/test/a11y/datetime.spec.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { newSpecPage } from '@stencil/core/testing';
|
||||||
|
import { Datetime } from '../../datetime';
|
||||||
|
import { Item } from '../../../item/item';
|
||||||
|
import { Label } from '../../../label/label';
|
||||||
|
|
||||||
|
describe('Datetime a11y', () => {
|
||||||
|
it('does not set a default aria-labelledby when there is not a neighboring ion-label', async () => {
|
||||||
|
const page = await newSpecPage({
|
||||||
|
components: [Datetime, Item, Label],
|
||||||
|
html: `<ion-datetime></ion-datetime>`
|
||||||
|
})
|
||||||
|
|
||||||
|
const ariaLabelledBy = page.root.getAttribute('aria-labelledby');
|
||||||
|
expect(ariaLabelledBy).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('set a default aria-labelledby when a neighboring ion-label exists', async () => {
|
||||||
|
const page = await newSpecPage({
|
||||||
|
components: [Datetime, Item, Label],
|
||||||
|
html: `<ion-item>
|
||||||
|
<ion-label>A11y Test</ion-label>
|
||||||
|
<ion-datetime></ion-datetime>
|
||||||
|
</ion-item>`
|
||||||
|
})
|
||||||
|
|
||||||
|
const label = page.body.querySelector('ion-label');
|
||||||
|
const ariaLabelledBy = page.body.querySelector('ion-datetime').getAttribute('aria-labelledby');
|
||||||
|
expect(ariaLabelledBy).toBe(label.id);
|
||||||
|
});
|
||||||
|
});
|
@ -12,6 +12,6 @@
|
|||||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script></head>
|
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script></head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<ion-datetime id="basic" display-format="MMMM" value="2012-12-15T13:47:20.789"></ion-datetime>
|
<ion-datetime id="basic" display-format="MMMM" value="2012-12-15T13:47:20.789" aria-label="datetime picker"></ion-datetime>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -408,7 +408,7 @@ export class Input implements ComponentInterface {
|
|||||||
<input
|
<input
|
||||||
class="native-input"
|
class="native-input"
|
||||||
ref={input => this.nativeInput = input}
|
ref={input => this.nativeInput = input}
|
||||||
aria-labelledby={labelId}
|
aria-labelledby={label ? labelId : null}
|
||||||
disabled={this.disabled}
|
disabled={this.disabled}
|
||||||
accept={this.accept}
|
accept={this.accept}
|
||||||
autoCapitalize={this.autocapitalize}
|
autoCapitalize={this.autocapitalize}
|
||||||
|
30
core/src/components/input/test/a11y/input.spec.ts
Normal file
30
core/src/components/input/test/a11y/input.spec.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { newSpecPage } from '@stencil/core/testing';
|
||||||
|
import { Input } from '../../input';
|
||||||
|
import { Item } from '../../../item/item';
|
||||||
|
import { Label } from '../../../label/label';
|
||||||
|
|
||||||
|
describe('Input a11y', () => {
|
||||||
|
it('does not set a default aria-labelledby when there is not a neighboring ion-label', async () => {
|
||||||
|
const page = await newSpecPage({
|
||||||
|
components: [Input, Item, Label],
|
||||||
|
html: `<ion-input></ion-input>`
|
||||||
|
})
|
||||||
|
|
||||||
|
const ariaLabelledBy = page.body.querySelector('ion-input > input').getAttribute('aria-labelledby');
|
||||||
|
expect(ariaLabelledBy).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('set a default aria-labelledby when a neighboring ion-label exists', async () => {
|
||||||
|
const page = await newSpecPage({
|
||||||
|
components: [Input, Item, Label],
|
||||||
|
html: `<ion-item>
|
||||||
|
<ion-label>A11y Test</ion-label>
|
||||||
|
<ion-input></ion-input>
|
||||||
|
</ion-item>`
|
||||||
|
})
|
||||||
|
|
||||||
|
const label = page.body.querySelector('ion-label');
|
||||||
|
const ariaLabelledBy = page.body.querySelector('ion-input > input').getAttribute('aria-labelledby');
|
||||||
|
expect(ariaLabelledBy).toBe(label.id);
|
||||||
|
});
|
||||||
|
});
|
@ -46,6 +46,10 @@
|
|||||||
<ion-input id="input3" value="inputs"></ion-input>
|
<ion-input id="input3" value="inputs"></ion-input>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
||||||
|
<ion-item>
|
||||||
|
<ion-input id="input4" placeholder="No Label" aria-label="input4"></ion-input>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
<ion-list>
|
<ion-list>
|
||||||
<ion-item>
|
<ion-item>
|
||||||
Number Test
|
Number Test
|
||||||
@ -59,6 +63,10 @@
|
|||||||
Default Test
|
Default Test
|
||||||
<span id="defaultInputResult"></span>
|
<span id="defaultInputResult"></span>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
<ion-item>
|
||||||
|
No Label Test
|
||||||
|
<span id="noLabelInputResult"></span>
|
||||||
|
</ion-item>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
|
|
||||||
</ion-list>
|
</ion-list>
|
||||||
@ -74,6 +82,9 @@
|
|||||||
var defaultInput = checkInput('input3');
|
var defaultInput = checkInput('input3');
|
||||||
updateResult(defaultInput, 'defaultInputResult');
|
updateResult(defaultInput, 'defaultInputResult');
|
||||||
|
|
||||||
|
var noLabelInput = checkInput('input4');
|
||||||
|
updateResult(noLabelInput, 'noLabelInputResult');
|
||||||
|
|
||||||
// Update results of input
|
// Update results of input
|
||||||
function updateResult(result, resultId) {
|
function updateResult(result, resultId) {
|
||||||
var resultEl = document.getElementById(resultId);
|
var resultEl = document.getElementById(resultId);
|
||||||
@ -122,6 +133,14 @@
|
|||||||
readonly: undefined,
|
readonly: undefined,
|
||||||
disabled: undefined
|
disabled: undefined
|
||||||
});
|
});
|
||||||
|
} else if (id === 'input4') {
|
||||||
|
return testAttributes(el, inputEl, {
|
||||||
|
id: 'input4',
|
||||||
|
type: undefined,
|
||||||
|
readonly: undefined,
|
||||||
|
disabled: undefined,
|
||||||
|
'aria-label': 'input4'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
30
core/src/components/textarea/test/a11y/textarea.spec.ts
Normal file
30
core/src/components/textarea/test/a11y/textarea.spec.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { newSpecPage } from '@stencil/core/testing';
|
||||||
|
import { Textarea } from '../../textarea';
|
||||||
|
import { Item } from '../../../item/item';
|
||||||
|
import { Label } from '../../../label/label';
|
||||||
|
|
||||||
|
describe('Textarea a11y', () => {
|
||||||
|
it('does not set a default aria-labelledby when there is not a neighboring ion-label', async () => {
|
||||||
|
const page = await newSpecPage({
|
||||||
|
components: [Textarea, Item, Label],
|
||||||
|
html: `<ion-textarea></ion-textarea>`
|
||||||
|
})
|
||||||
|
|
||||||
|
const ariaLabelledBy = page.body.querySelector('ion-textarea textarea').getAttribute('aria-labelledby');
|
||||||
|
expect(ariaLabelledBy).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('set a default aria-labelledby when a neighboring ion-label exists', async () => {
|
||||||
|
const page = await newSpecPage({
|
||||||
|
components: [Textarea, Item, Label],
|
||||||
|
html: `<ion-item>
|
||||||
|
<ion-label>A11y Test</ion-label>
|
||||||
|
<ion-textarea></ion-textarea>
|
||||||
|
</ion-item>`
|
||||||
|
})
|
||||||
|
|
||||||
|
const label = page.body.querySelector('ion-label');
|
||||||
|
const ariaLabelledBy = page.body.querySelector('ion-textarea textarea').getAttribute('aria-labelledby');
|
||||||
|
expect(ariaLabelledBy).toBe(label.id);
|
||||||
|
});
|
||||||
|
});
|
@ -363,7 +363,7 @@ export class Textarea implements ComponentInterface {
|
|||||||
>
|
>
|
||||||
<textarea
|
<textarea
|
||||||
class="native-textarea"
|
class="native-textarea"
|
||||||
aria-labelledby={labelId}
|
aria-labelledby={label ? labelId : null}
|
||||||
ref={el => this.nativeInput = el}
|
ref={el => this.nativeInput = el}
|
||||||
autoCapitalize={this.autocapitalize}
|
autoCapitalize={this.autocapitalize}
|
||||||
autoFocus={this.autofocus}
|
autoFocus={this.autofocus}
|
||||||
|
Reference in New Issue
Block a user