fix(form-core) sync autofocus to focusable node
This commit is contained in:
parent
5b8d655f10
commit
2683a73080
4 changed files with 75 additions and 0 deletions
5
.changeset/silly-shirts-promise.md
Normal file
5
.changeset/silly-shirts-promise.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@lion/ui': patch
|
||||
---
|
||||
|
||||
[FocusMixin] now syncs autofocus between host and the focusable node.
|
||||
|
|
@ -27,6 +27,7 @@ const FocusMixinImplementation = superclass =>
|
|||
return {
|
||||
focused: { type: Boolean, reflect: true },
|
||||
focusedVisible: { type: Boolean, reflect: true, attribute: 'focused-visible' },
|
||||
autofocus: { type: Boolean, reflect: true }, // Required in Lit to observe autofocus
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -52,6 +53,7 @@ const FocusMixinImplementation = superclass =>
|
|||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.__registerEventsForFocusMixin();
|
||||
this.__syncAutofocusToFocusableElement();
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
|
|
@ -59,6 +61,35 @@ const FocusMixinImplementation = superclass =>
|
|||
this.__teardownEventsForFocusMixin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets called when an attribute is changed.
|
||||
* @param {String} name
|
||||
* @param {String | null} _old
|
||||
* @param {String | null} value
|
||||
* @protected
|
||||
*/
|
||||
attributeChangedCallback(name, _old, value) {
|
||||
super.attributeChangedCallback(name, _old, value);
|
||||
if (name === 'autofocus') {
|
||||
this.__syncAutofocusToFocusableElement();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
__syncAutofocusToFocusableElement() {
|
||||
if (!this._focusableNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.hasAttribute('autofocus')) {
|
||||
this._focusableNode.setAttribute('autofocus', '');
|
||||
} else {
|
||||
this._focusableNode.removeAttribute('autofocus');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls `focus()` on focusable element within
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -323,6 +323,39 @@ describe('FocusMixin', () => {
|
|||
spy4.restore();
|
||||
restoreMock4();
|
||||
});
|
||||
|
||||
it('has mirrors syncs autofocus on the focusable element when autofocus changes', async () => {
|
||||
const el = /** @type {Focusable} */ (
|
||||
await fixture(html`
|
||||
<${tag}><input slot="input"></${tag}>
|
||||
`)
|
||||
);
|
||||
|
||||
// @ts-ignore [allow-protected] in test
|
||||
const { _focusableNode } = el;
|
||||
|
||||
el.setAttribute('autofocus', '');
|
||||
await el.updateComplete;
|
||||
expect(_focusableNode.hasAttribute('autofocus')).to.be.true;
|
||||
|
||||
el.removeAttribute('autofocus');
|
||||
await el.updateComplete;
|
||||
expect(_focusableNode.hasAttribute('autofocus')).not.to.be.true;
|
||||
});
|
||||
|
||||
it('has mirrors syncs autofocus on the focusable element when autofocus was set on render', async () => {
|
||||
const el = /** @type {Focusable} */ (
|
||||
await fixture(html`
|
||||
<${tag} autofocus><input slot="input"></${tag}>
|
||||
`)
|
||||
);
|
||||
|
||||
// @ts-ignore [allow-protected] in test
|
||||
const { _focusableNode } = el;
|
||||
|
||||
expect(el.hasAttribute('autofocus')).to.be.true;
|
||||
expect(_focusableNode.hasAttribute('autofocus')).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -25,6 +25,12 @@ export declare class FocusHost {
|
|||
*/
|
||||
blur(): void;
|
||||
|
||||
/**
|
||||
* Synchronizes property values when attributes change.
|
||||
* @category attributes
|
||||
*/
|
||||
attributeChangedCallback(name: string, _old: string | null, value: string | null): void;
|
||||
|
||||
/**
|
||||
* The focusable element:
|
||||
* could be an input, textarea, select, button or any other element with tabindex > -1
|
||||
|
|
|
|||
Loading…
Reference in a new issue