fixes #596; improves radio a11y

pull/638/head
Cory LaViska 2021-12-30 16:32:57 -05:00
rodzic d60e9f3bc2
commit 0295d9c573
2 zmienionych plików z 38 dodań i 14 usunięć

Wyświetl plik

@ -11,7 +11,9 @@ _During the beta period, these restrictions may be relaxed in the event of a mis
- Added `role="status"` to `<sl-spinner>`
- Fixed broken spinner animation in Safari [#633](https://github.com/shoelace-style/shoelace/issues/633)
- Fixed an a11y bug in `<sl-tooltip>` where `aria-describedby` referenced an id in the shadow root
- Fixed a bug in `<sl-radio>` where tabbing didn't work properly in Firefox [#596](https://github.com/shoelace-style/shoelace/issues/596)
- Improved `<sl-spinner>` track color when used on various backgrounds
- Improved a11y in `<sl-radio>` so VoiceOver announces radios properly in a radio group
- Refactored internal id usage in `<sl-details>`, `<sl-dialog>`, `<sl-drawer>`, and `<sl-dropdow>`
- Removed `position: relative` from the common component stylesheet

Wyświetl plik

@ -7,8 +7,6 @@ import { emit } from '../../internal/event';
import { watch } from '../../internal/watch';
import styles from './radio.styles';
let id = 0;
/**
* @since 2.0
* @status stable
@ -30,9 +28,6 @@ export default class SlRadio extends LitElement {
@query('input[type="radio"]') input: HTMLInputElement;
private inputId = `radio-${++id}`;
private labelId = `radio-label-${id}`;
@state() private hasFocus = false;
/** The radio's name attribute. */
@ -53,6 +48,23 @@ export default class SlRadio extends LitElement {
*/
@property({ type: Boolean, reflect: true }) invalid = false;
firstUpdated() {
const radios = this.getAllRadios();
const checkedRadio = radios.find(radio => radio.checked);
radios.map(radio => {
if (radio.input) {
radio.input.tabIndex = -1;
}
});
if (checkedRadio) {
checkedRadio.input.tabIndex = 0;
} else if (radios.length) {
radios[0].input.tabIndex = 0;
}
}
/** Simulates a click on the radio. */
click() {
this.input.click();
@ -102,7 +114,12 @@ export default class SlRadio extends LitElement {
@watch('checked', { waitUntilFirstUpdate: true })
handleCheckedChange() {
if (this.checked) {
this.getSiblingRadios().map(radio => (radio.checked = false));
this.input.tabIndex = 0;
this.getSiblingRadios().map(radio => {
radio.input.tabIndex = -1;
radio.checked = false;
});
}
}
@ -133,9 +150,15 @@ export default class SlRadio extends LitElement {
if (index < 0) index = radios.length - 1;
if (index > radios.length - 1) index = 0;
this.getAllRadios().map(radio => (radio.checked = false));
this.getAllRadios().map(radio => {
radio.checked = false;
radio.input.tabIndex = -1;
});
radios[index].focus();
radios[index].checked = true;
radios[index].input.tabIndex = 0;
emit(radios[index], 'sl-change');
event.preventDefault();
@ -143,6 +166,10 @@ export default class SlRadio extends LitElement {
}
render() {
this.setAttribute('role', 'radio');
this.setAttribute('aria-checked', this.checked ? 'true' : 'false');
this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
return html`
<label
part="base"
@ -152,25 +179,20 @@ export default class SlRadio extends LitElement {
'radio--disabled': this.disabled,
'radio--focused': this.hasFocus
})}
for=${this.inputId}
@keydown=${this.handleKeyDown}
>
<input
id=${this.inputId}
class="radio__input"
type="radio"
name=${ifDefined(this.name)}
value=${ifDefined(this.value)}
.checked=${live(this.checked)}
.disabled=${this.disabled}
aria-checked=${this.checked ? 'true' : 'false'}
aria-disabled=${this.disabled ? 'true' : 'false'}
aria-labelledby=${this.labelId}
aria-hidden="true"
@click=${this.handleClick}
@blur=${this.handleBlur}
@focus=${this.handleFocus}
/>
<span part="control" class="radio__control">
<span part="checked-icon" class="radio__icon">
<svg viewBox="0 0 16 16">
@ -183,7 +205,7 @@ export default class SlRadio extends LitElement {
</span>
</span>
<span part="label" id=${this.labelId} class="radio__label">
<span part="label" class="radio__label">
<slot></slot>
</span>
</label>