Merge branch 'next' of https://github.com/shoelace-style/shoelace into rtl-for-older-browsers

rtl-for-older-browsers
konnorrogers 2024-10-10 14:00:22 -04:00
commit bb563a20c5
4 zmienionych plików z 113 dodań i 8 usunięć

Wyświetl plik

@ -16,6 +16,7 @@ New versions of Shoelace are released as-needed and generally occur when a criti
- Updated all checks for directionality to use `this.localize.dir()` instead of `el.matches(:dir(rtl))` so older browsers don't error out [#2188]
- Added Finnish translations [#2211]
- Added the `.focus` function to `<sl-radio-group>` [#2192]
- Fixed a bug with with `<sl-select>` not respecting its initial value. [#2204]
- Fixed a bug with certain bundlers when using dynamic imports [#2210]
- Fixed a bug in `<sl-textarea>` causing scroll jumping when using `resize="auto"` [#2182]

Wyświetl plik

@ -192,14 +192,7 @@ export default class SlRadioGroup extends ShoelaceElement implements ShoelaceFor
}
private handleLabelClick() {
const radios = this.getAllRadios();
const checked = radios.find(radio => radio.checked);
const radioToFocus = checked || radios[0];
// Move focus to the checked radio (or the first one if none are checked) when clicking the label
if (radioToFocus) {
radioToFocus.focus();
}
this.focus();
}
private handleInvalid(event: Event) {
@ -325,6 +318,20 @@ export default class SlRadioGroup extends ShoelaceElement implements ShoelaceFor
this.formControlController.updateValidity();
}
/** Sets focus on the radio-group. */
public focus(options?: FocusOptions) {
const radios = this.getAllRadios();
const checked = radios.find(radio => radio.checked);
const firstEnabledRadio = radios.find(radio => !radio.disabled);
const radioToFocus = checked || firstEnabledRadio;
// Call focus for the checked radio
// If no radio is checked, focus the first one that is not disabled
if (radioToFocus) {
radioToFocus.focus(options);
}
}
render() {
const hasLabelSlot = this.hasSlotController.test('label');
const hasHelpTextSlot = this.hasSlotController.test('help-text');

Wyświetl plik

@ -300,6 +300,102 @@ describe('when a size is applied', () => {
});
});
describe('when handling focus', () => {
const doAction = async (instance: SlRadioGroup, type: string) => {
if (type === 'focus') {
instance.focus();
await instance.updateComplete;
return;
}
const label = instance.shadowRoot!.querySelector<HTMLLabelElement>('#label')!;
label.click();
await instance.updateComplete;
};
// Tests for focus and label actions with radio buttons
['focus', 'label'].forEach(actionType => {
describe(`when using ${actionType}`, () => {
it('should do nothing if all elements are disabled', async () => {
const el = await fixture<SlRadioGroup>(html`
<sl-radio-group>
<sl-radio id="radio-0" value="0" disabled></sl-radio>
<sl-radio id="radio-1" value="1" disabled></sl-radio>
<sl-radio id="radio-2" value="2" disabled></sl-radio>
<sl-radio id="radio-3" value="3" disabled></sl-radio>
</sl-radio-group>
`);
const validFocusHandler = sinon.spy();
Array.from(el.querySelectorAll<SlRadio>('sl-radio')).forEach(radio =>
radio.addEventListener('sl-focus', validFocusHandler)
);
expect(validFocusHandler).to.not.have.been.called;
await doAction(el, actionType);
expect(validFocusHandler).to.not.have.been.called;
});
it('should focus the first radio that is enabled when the group receives focus', async () => {
const el = await fixture<SlRadioGroup>(html`
<sl-radio-group>
<sl-radio id="radio-0" value="0" disabled></sl-radio>
<sl-radio id="radio-1" value="1"></sl-radio>
<sl-radio id="radio-2" value="2"></sl-radio>
<sl-radio id="radio-3" value="3"></sl-radio>
</sl-radio-group>
`);
const invalidFocusHandler = sinon.spy();
const validFocusHandler = sinon.spy();
const disabledRadio = el.querySelector('#radio-0')!;
const validRadio = el.querySelector('#radio-1')!;
disabledRadio.addEventListener('sl-focus', invalidFocusHandler);
validRadio.addEventListener('sl-focus', validFocusHandler);
expect(invalidFocusHandler).to.not.have.been.called;
expect(validFocusHandler).to.not.have.been.called;
await doAction(el, actionType);
expect(invalidFocusHandler).to.not.have.been.called;
expect(validFocusHandler).to.have.been.called;
});
it('should focus the currently enabled radio when the group receives focus', async () => {
const el = await fixture<SlRadioGroup>(html`
<sl-radio-group value="2">
<sl-radio id="radio-0" value="0" disabled></sl-radio>
<sl-radio id="radio-1" value="1"></sl-radio>
<sl-radio id="radio-2" value="2" checked></sl-radio>
<sl-radio id="radio-3" value="3"></sl-radio>
</sl-radio-group>
`);
const invalidFocusHandler = sinon.spy();
const validFocusHandler = sinon.spy();
const disabledRadio = el.querySelector('#radio-0')!;
const validRadio = el.querySelector('#radio-2')!;
disabledRadio.addEventListener('sl-focus', invalidFocusHandler);
validRadio.addEventListener('sl-focus', validFocusHandler);
expect(invalidFocusHandler).to.not.have.been.called;
expect(validFocusHandler).to.not.have.been.called;
await doAction(el, actionType);
expect(invalidFocusHandler).to.not.have.been.called;
expect(validFocusHandler).to.have.been.called;
});
});
});
});
describe('when the value changes', () => {
it('should emit sl-change when toggled with the arrow keys', async () => {
const radioGroup = await fixture<SlRadioGroup>(html`

Wyświetl plik

@ -601,6 +601,7 @@ describe('<sl-select>', () => {
);
const el = form.querySelector<SlSelect>('sl-select')!;
await aTimeout(10);
expect(el.value).to.equal('');
expect(new FormData(form).get('select')).equal('');