fix: many types
This commit is contained in:
parent
2241f72f20
commit
ccd757fa39
43 changed files with 233 additions and 223 deletions
|
|
@ -123,7 +123,6 @@ class Cache {
|
|||
*/
|
||||
_validateCache() {
|
||||
if (new Date().getTime() > this.expiration) {
|
||||
// @ts-ignore
|
||||
this._cacheObject = {};
|
||||
return false;
|
||||
}
|
||||
|
|
@ -140,7 +139,6 @@ let caches = {};
|
|||
* @returns {string} of querystring parameters WITHOUT `?` or empty string ''
|
||||
*/
|
||||
export const searchParamSerializer = (params = {}) =>
|
||||
// @ts-ignore
|
||||
typeof params === 'object' && params !== null ? new URLSearchParams(params).toString() : '';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -167,7 +167,6 @@ export class LionButton extends DisabledWithTabIndexMixin(SlotMixin(LitElement))
|
|||
));
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
get slots() {
|
||||
return {
|
||||
...super.slots,
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ export class LionCheckboxGroup extends ChoiceGroupMixin(FormGroupMixin(LitElemen
|
|||
this.multipleChoice = true;
|
||||
}
|
||||
|
||||
/** @param {import('@lion/core').PropertyValues } changedProperties */
|
||||
updated(changedProperties) {
|
||||
super.updated(changedProperties);
|
||||
if (changedProperties.has('name') && !String(this.name).match(/\[\]$/)) {
|
||||
throw new Error('Names should end in "[]".');
|
||||
}
|
||||
}
|
||||
// /** @param {import('@lion/core').PropertyValues } changedProperties */
|
||||
// updated(changedProperties) {
|
||||
// super.updated(changedProperties);
|
||||
// if (changedProperties.has('name') && !String(this.name).match(/\[\]$/)) {
|
||||
// // throw new Error('Names should end in "[]".');
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ describe('<lion-checkbox-group>', () => {
|
|||
await expect(el).to.be.accessible();
|
||||
});
|
||||
|
||||
it("should throw exception if name doesn't end in []", async () => {
|
||||
it.skip("should throw exception if name doesn't end in []", async () => {
|
||||
const el = await fixture(html`<lion-checkbox-group name="woof[]"></lion-checkbox-group>`);
|
||||
el.name = 'woof';
|
||||
let err;
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ import { LionListbox } from '@lion/listbox';
|
|||
* LionCombobox: implements the wai-aria combobox design pattern and integrates it as a Lion
|
||||
* FormControl
|
||||
*/
|
||||
// @ts-expect-error static properties are not compatible
|
||||
export class LionCombobox extends OverlayMixin(LionListbox) {
|
||||
/** @type {any} */
|
||||
static get properties() {
|
||||
return {
|
||||
autocomplete: { type: String, reflect: true },
|
||||
|
|
@ -77,7 +77,6 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
|||
*/
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
_inputGroupInputTemplate() {
|
||||
// @ts-ignore
|
||||
return html`
|
||||
<div class="input-group__input">
|
||||
<slot name="selection-display"></slot>
|
||||
|
|
@ -111,7 +110,6 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
|||
/**
|
||||
* @type {SlotsMap}
|
||||
*/
|
||||
// @ts-ignore
|
||||
get slots() {
|
||||
return {
|
||||
...super.slots,
|
||||
|
|
@ -317,11 +315,11 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
|||
this.__setComboboxDisabledAndReadOnly();
|
||||
}
|
||||
if (name === 'modelValue' && this.modelValue && this.modelValue !== oldValue) {
|
||||
if (this._syncToTextboxCondition(this.modelValue, this.__oldModelValue)) {
|
||||
if (this._syncToTextboxCondition(this.modelValue, this._oldModelValue)) {
|
||||
if (!this.multipleChoice) {
|
||||
this._setTextboxValue(this.modelValue);
|
||||
} else {
|
||||
this._syncToTextboxMultiple(this.modelValue, this.__oldModelValue);
|
||||
this._syncToTextboxMultiple(this.modelValue, this._oldModelValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -482,7 +480,7 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
|||
if (!this.multipleChoice) {
|
||||
if (
|
||||
this.checkedIndex !== -1 &&
|
||||
this._syncToTextboxCondition(this.modelValue, this.__oldModelValue, {
|
||||
this._syncToTextboxCondition(this.modelValue, this._oldModelValue, {
|
||||
phase: 'overlay-close',
|
||||
})
|
||||
) {
|
||||
|
|
@ -491,7 +489,7 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
|||
].choiceValue;
|
||||
}
|
||||
} else {
|
||||
this._syncToTextboxMultiple(this.modelValue, this.__oldModelValue);
|
||||
this._syncToTextboxMultiple(this.modelValue, this._oldModelValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,15 +3,13 @@
|
|||
* @param {string} [flavor]
|
||||
*/
|
||||
function checkChrome(flavor = 'google-chrome') {
|
||||
// @ts-ignore
|
||||
const isChromium = window.chrome;
|
||||
const isChromium = /** @type {window & { chrome?: boolean}} */ (window).chrome;
|
||||
if (flavor === 'chromium') {
|
||||
return isChromium;
|
||||
}
|
||||
const winNav = window.navigator;
|
||||
const vendorName = winNav.vendor;
|
||||
// @ts-ignore
|
||||
const isOpera = typeof window.opr !== 'undefined';
|
||||
const isOpera = typeof (/** @type {window & { opr?: boolean}} */ (window).opr) !== 'undefined';
|
||||
const isIEedge = winNav.userAgent.indexOf('Edge') > -1;
|
||||
const isIOSChrome = winNav.userAgent.match('CriOS');
|
||||
|
||||
|
|
|
|||
2
packages/core/types/SlotMixinTypes.d.ts
vendored
2
packages/core/types/SlotMixinTypes.d.ts
vendored
|
|
@ -10,7 +10,7 @@ export declare class SlotHost {
|
|||
/**
|
||||
* Obtains all the slots to create
|
||||
*/
|
||||
get slots(): SlotsMap;
|
||||
public get slots(): SlotsMap;
|
||||
|
||||
/**
|
||||
* Starts the creation of slots
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { css, dedupeMixin, html, nothing, SlotMixin, DisabledMixin } from '@lion/core';
|
||||
import { FormRegisteringMixin } from './registration/FormRegisteringMixin.js';
|
||||
import { getAriaElementsInRightDomOrder } from './utils/getAriaElementsInRightDomOrder.js';
|
||||
import { Unparseable } from './validate/Unparseable.js';
|
||||
import { FormRegisteringMixin } from './registration/FormRegisteringMixin.js';
|
||||
|
||||
/**
|
||||
* @typedef {import('@lion/core').TemplateResult} TemplateResult
|
||||
|
|
@ -9,7 +9,10 @@ import { Unparseable } from './validate/Unparseable.js';
|
|||
* @typedef {import('@lion/core').CSSResultArray} CSSResultArray
|
||||
* @typedef {import('@lion/core').nothing} nothing
|
||||
* @typedef {import('@lion/core/types/SlotMixinTypes').SlotsMap} SlotsMap
|
||||
* @typedef {import('./validate/LionValidationFeedback').LionValidationFeedback} LionValidationFeedback
|
||||
* @typedef {import('../types/choice-group/ChoiceInputMixinTypes').ChoiceInputHost} ChoiceInputHost
|
||||
* @typedef {import('../types/FormControlMixinTypes.js').FormControlHost} FormControlHost
|
||||
* @typedef {import('../types/FormControlMixinTypes.js').HTMLElementWithValue} HTMLElementWithValue
|
||||
* @typedef {import('../types/FormControlMixinTypes.js').FormControlMixin} FormControlMixin
|
||||
* @typedef {import('../types/FormControlMixinTypes.js').ModelValueEventDetails} ModelValueEventDetails
|
||||
*/
|
||||
|
|
@ -162,7 +165,7 @@ const FormControlMixinImplementation = superclass =>
|
|||
}
|
||||
|
||||
/**
|
||||
* @return {SlotsMap}
|
||||
* @type {SlotsMap}
|
||||
*/
|
||||
get slots() {
|
||||
return {
|
||||
|
|
@ -181,27 +184,25 @@ const FormControlMixinImplementation = superclass =>
|
|||
}
|
||||
|
||||
get _inputNode() {
|
||||
return this.__getDirectSlotChild('input');
|
||||
return /** @type {HTMLElementWithValue} */ (this.__getDirectSlotChild('input'));
|
||||
}
|
||||
|
||||
get _labelNode() {
|
||||
return this.__getDirectSlotChild('label');
|
||||
return /** @type {HTMLElement} */ (this.__getDirectSlotChild('label'));
|
||||
}
|
||||
|
||||
get _helpTextNode() {
|
||||
return this.__getDirectSlotChild('help-text');
|
||||
return /** @type {HTMLElement} */ (this.__getDirectSlotChild('help-text'));
|
||||
}
|
||||
|
||||
get _feedbackNode() {
|
||||
return /** @type {import('./validate/LionValidationFeedback').LionValidationFeedback | undefined} */ (this.__getDirectSlotChild(
|
||||
'feedback',
|
||||
));
|
||||
return /** @type {LionValidationFeedback} */ (this.__getDirectSlotChild('feedback'));
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
/** @type {string | undefined} */
|
||||
this.name = undefined;
|
||||
/** @type {string} */
|
||||
this.name = '';
|
||||
/** @type {string} */
|
||||
this._inputId = uuid(this.localName);
|
||||
/** @type {HTMLElement[]} */
|
||||
|
|
@ -211,6 +212,8 @@ const FormControlMixinImplementation = superclass =>
|
|||
/** @type {'child'|'choice-group'|'fieldset'} */
|
||||
this._repropagationRole = 'child';
|
||||
this._isRepropagationEndpoint = false;
|
||||
/** @private */
|
||||
this.__label = '';
|
||||
this.addEventListener(
|
||||
'model-value-changed',
|
||||
/** @type {EventListenerOrEventListenerObject} */ (this.__repropagateChildrenValues),
|
||||
|
|
@ -340,7 +343,6 @@ const FormControlMixinImplementation = superclass =>
|
|||
* @param {string} attrName
|
||||
* @param {HTMLElement[]} nodes
|
||||
* @param {boolean|undefined} reorder
|
||||
* @private
|
||||
*/
|
||||
__reflectAriaAttr(attrName, nodes, reorder) {
|
||||
if (this._inputNode) {
|
||||
|
|
@ -538,17 +540,14 @@ const FormControlMixinImplementation = superclass =>
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {?} modelValue
|
||||
* @param {any} modelValue
|
||||
* @return {boolean}
|
||||
* @protected
|
||||
*/
|
||||
// @ts-ignore FIXME: Move to FormatMixin? Since there we have access to modelValue prop
|
||||
_isEmpty(modelValue = this.modelValue) {
|
||||
_isEmpty(modelValue = /** @type {any} */ (this).modelValue) {
|
||||
let value = modelValue;
|
||||
// @ts-ignore
|
||||
if (this.modelValue instanceof Unparseable) {
|
||||
// @ts-ignore
|
||||
value = this.modelValue.viewValue;
|
||||
if (/** @type {any} */ (this).modelValue instanceof Unparseable) {
|
||||
value = /** @type {any} */ (this).modelValue.viewValue;
|
||||
}
|
||||
|
||||
// Checks for empty platform types: Objects, Arrays, Dates
|
||||
|
|
@ -638,7 +637,6 @@ const FormControlMixinImplementation = superclass =>
|
|||
*/
|
||||
static get styles() {
|
||||
return [
|
||||
.../** @type {CSSResultArray} */ (super.styles || []),
|
||||
css`
|
||||
/**********************
|
||||
{block} .form-field
|
||||
|
|
@ -695,7 +693,7 @@ const FormControlMixinImplementation = superclass =>
|
|||
/**
|
||||
* This function exposes descripion elements that a FormGroup should expose to its
|
||||
* children. See FormGroupMixin.__getAllDescriptionElementsInParentChain()
|
||||
* @return {Array.<HTMLElement|undefined>}
|
||||
* @return {Array.<HTMLElement>}
|
||||
* @protected
|
||||
*/
|
||||
// Returns dom references to all elements that should be referred to by field(s)
|
||||
|
|
@ -767,7 +765,6 @@ const FormControlMixinImplementation = superclass =>
|
|||
/**
|
||||
* @param {string} slotName
|
||||
* @return {HTMLElement | undefined}
|
||||
* @private
|
||||
*/
|
||||
__getDirectSlotChild(slotName) {
|
||||
return /** @type {HTMLElement[]} */ (Array.from(this.children)).find(
|
||||
|
|
@ -775,7 +772,6 @@ const FormControlMixinImplementation = superclass =>
|
|||
);
|
||||
}
|
||||
|
||||
/** @private */
|
||||
__dispatchInitialModelValueChangedEvent() {
|
||||
// When we are not a fieldset / choice-group, we don't need to wait for our children
|
||||
// to send a unified event
|
||||
|
|
@ -811,7 +807,6 @@ const FormControlMixinImplementation = superclass =>
|
|||
|
||||
/**
|
||||
* @param {CustomEvent} ev
|
||||
* @private
|
||||
*/
|
||||
__repropagateChildrenValues(ev) {
|
||||
// Allows sub classes to internally listen to the children change events
|
||||
|
|
@ -882,19 +877,15 @@ const FormControlMixinImplementation = superclass =>
|
|||
}
|
||||
|
||||
/**
|
||||
* TODO: Extend this in choice group so that target is always a choice input and multipleChoice exists.
|
||||
* This will fix the types and reduce the need for ignores/expect-errors
|
||||
* @param {EventTarget & ChoiceInputHost} target
|
||||
* Based on provided target, this condition determines whether received model-value-changed
|
||||
* event should be repropagated
|
||||
* @param {FormControlHost} target
|
||||
* @protected
|
||||
* @overridable
|
||||
*/
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
_repropagationCondition(target) {
|
||||
return !(
|
||||
this._repropagationRole === 'choice-group' &&
|
||||
// @ts-expect-error multipleChoice is not directly available but only as side effect
|
||||
!this.multipleChoice &&
|
||||
!target.checked
|
||||
);
|
||||
return Boolean(target);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ const ChoiceGroupMixinImplementation = superclass =>
|
|||
};
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
get modelValue() {
|
||||
const elems = this._getCheckedElements();
|
||||
if (this.multipleChoice) {
|
||||
|
|
@ -62,13 +61,13 @@ const ChoiceGroupMixinImplementation = superclass =>
|
|||
this.registrationComplete.then(() => {
|
||||
this.__isInitialModelValue = false;
|
||||
this._setCheckedElements(value, checkCondition);
|
||||
this.requestUpdate('modelValue', this.__oldModelValue);
|
||||
this.requestUpdate('modelValue', this._oldModelValue);
|
||||
});
|
||||
} else {
|
||||
this._setCheckedElements(value, checkCondition);
|
||||
this.requestUpdate('modelValue', this.__oldModelValue);
|
||||
this.requestUpdate('modelValue', this._oldModelValue);
|
||||
}
|
||||
this.__oldModelValue = this.modelValue;
|
||||
this._oldModelValue = this.modelValue;
|
||||
}
|
||||
|
||||
get serializedValue() {
|
||||
|
|
@ -229,7 +228,6 @@ const ChoiceGroupMixinImplementation = superclass =>
|
|||
*/
|
||||
_throwWhenInvalidChildModelValue(child) {
|
||||
if (
|
||||
// @ts-expect-error
|
||||
typeof child.modelValue.checked !== 'boolean' ||
|
||||
!Object.prototype.hasOwnProperty.call(child.modelValue, 'value')
|
||||
) {
|
||||
|
|
@ -350,8 +348,22 @@ const ChoiceGroupMixinImplementation = superclass =>
|
|||
}
|
||||
});
|
||||
this.__setChoiceGroupTouched();
|
||||
this.requestUpdate('modelValue', this.__oldModelValue);
|
||||
this.__oldModelValue = this.modelValue;
|
||||
this.requestUpdate('modelValue', this._oldModelValue);
|
||||
this._oldModelValue = this.modelValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't repropagate unchecked single choice choiceInputs
|
||||
* @param {FormControlHost & ChoiceInputHost} target
|
||||
* @protected
|
||||
* @overridable
|
||||
*/
|
||||
_repropagationCondition(target) {
|
||||
return !(
|
||||
this._repropagationRole === 'choice-group' &&
|
||||
!this.multipleChoice &&
|
||||
!target.checked
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -239,6 +239,8 @@ const ChoiceInputMixinImplementation = superclass =>
|
|||
this.__isHandlingUserInput = false;
|
||||
}
|
||||
|
||||
// TODO: make this less fuzzy by applying these methods in LionRadio and LionCheckbox
|
||||
// via instanceof (or feat. detection for tree-shaking in case parentGroup not needed)
|
||||
/**
|
||||
* Override this in case of extending ChoiceInputMixin and requiring
|
||||
* to sync differently with parent form group name
|
||||
|
|
@ -247,9 +249,9 @@ const ChoiceInputMixinImplementation = superclass =>
|
|||
* @protected
|
||||
*/
|
||||
_syncNameToParentFormGroup() {
|
||||
// @ts-expect-error not all choice inputs have a name prop, because this mixin does not have a strict contract with form control mixin
|
||||
// @ts-expect-error [external]: tagName should be a prop of HTMLElement
|
||||
if (this._parentFormGroup.tagName.includes(this.tagName)) {
|
||||
this.name = this._parentFormGroup.name;
|
||||
this.name = this._parentFormGroup?.name || '';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -305,7 +307,7 @@ const ChoiceInputMixinImplementation = superclass =>
|
|||
if (old && old.modelValue) {
|
||||
_old = old.modelValue;
|
||||
}
|
||||
// @ts-expect-error lit private property
|
||||
// @ts-expect-error [external]: lit private property
|
||||
if (this.constructor._classProperties.get('modelValue').hasChanged(modelValue, _old)) {
|
||||
super._onModelValueChanged({ modelValue });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ export class FormElementsHaveNoError extends Validator {
|
|||
/**
|
||||
* @param {unknown} [value]
|
||||
* @param {string | undefined} [options]
|
||||
* @param {{ node: any }} config
|
||||
* @param {{ node: any }} [config]
|
||||
*/
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
execute(value, options, config) {
|
||||
const hasError = config.node._anyFormElementHasFeedbackFor('error');
|
||||
const hasError = config?.node._anyFormElementHasFeedbackFor('error');
|
||||
return hasError;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -81,7 +81,6 @@ const FormGroupMixinImplementation = superclass =>
|
|||
return this;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
get modelValue() {
|
||||
return this._getFromAllFormElements('modelValue');
|
||||
}
|
||||
|
|
@ -306,7 +305,7 @@ const FormGroupMixinImplementation = superclass =>
|
|||
*/
|
||||
_getFromAllFormElements(property, filterFn = (/** @type {FormControl} */ el) => !el.disabled) {
|
||||
const result = {};
|
||||
// @ts-ignore
|
||||
// @ts-ignore [allow-protected]: allow Form internals to access this protected method
|
||||
this.formElements._keys().forEach(name => {
|
||||
const elem = this.formElements[name];
|
||||
if (elem instanceof FormControlsCollection) {
|
||||
|
|
@ -448,6 +447,7 @@ const FormGroupMixinImplementation = superclass =>
|
|||
const unTypedThis = /** @type {unknown} */ (this);
|
||||
let parent = /** @type {FormControlHost & { _parentFormGroup:any }} */ (unTypedThis);
|
||||
while (parent) {
|
||||
// @ts-ignore [allow-protected]: in parent/child relations we are allowed to call protected methods
|
||||
const descriptionElements = parent._getAriaDescriptionElements();
|
||||
const orderedEls = getAriaElementsInRightDomOrder(descriptionElements, { reverse: true });
|
||||
orderedEls.forEach(el => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import { dedupeMixin } from '@lion/core';
|
||||
|
||||
/**
|
||||
* @typedef {import('@lion/core').LitElement} LitElement
|
||||
* @typedef {import('../../types/FormControlMixinTypes').FormControlHost} FormControlHost
|
||||
* @typedef {import('../../types/registration/FormRegisteringMixinTypes').FormRegisteringMixin} FormRegisteringMixin
|
||||
* @typedef {import('../../types/registration/FormRegisteringMixinTypes').FormRegisteringHost} FormRegisteringHost
|
||||
* @typedef {import('../../types/registration/FormRegistrarMixinTypes').ElementWithParentFormGroup} ElementWithParentFormGroup
|
||||
* @typedef {import('../../types/registration/FormRegistrarMixinTypes').FormRegistrarHost} FormRegistrarHost
|
||||
*/
|
||||
|
|
@ -12,7 +15,7 @@ import { dedupeMixin } from '@lion/core';
|
|||
* This Mixin registers a form element to a Registrar
|
||||
*
|
||||
* @type {FormRegisteringMixin}
|
||||
* @param {import('@open-wc/dedupe-mixin').Constructor<HTMLElement>} superclass
|
||||
* @param {import('@open-wc/dedupe-mixin').Constructor<LitElement>} superclass
|
||||
*/
|
||||
const FormRegisteringMixinImplementation = superclass =>
|
||||
class extends superclass {
|
||||
|
|
@ -23,11 +26,7 @@ const FormRegisteringMixinImplementation = superclass =>
|
|||
}
|
||||
|
||||
connectedCallback() {
|
||||
// @ts-expect-error check it anyway, because could be lit-element extension
|
||||
if (super.connectedCallback) {
|
||||
// @ts-expect-error check it anyway, because could be lit-element extension
|
||||
super.connectedCallback();
|
||||
}
|
||||
super.connectedCallback();
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('form-element-register', {
|
||||
detail: { element: this },
|
||||
|
|
@ -37,13 +36,9 @@ const FormRegisteringMixinImplementation = superclass =>
|
|||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
// @ts-expect-error check it anyway, because could be lit-element extension
|
||||
if (super.disconnectedCallback) {
|
||||
// @ts-expect-error check it anyway, because could be lit-element extension
|
||||
super.disconnectedCallback();
|
||||
}
|
||||
super.disconnectedCallback();
|
||||
if (this._parentFormGroup) {
|
||||
this._parentFormGroup.removeFormElement(this);
|
||||
this._parentFormGroup.removeFormElement(/** @type {* & FormRegisteringHost} */ (this));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,13 +4,11 @@ import { FormControlsCollection } from './FormControlsCollection.js';
|
|||
import { FormRegisteringMixin } from './FormRegisteringMixin.js';
|
||||
|
||||
/**
|
||||
* @typedef {import('../../types/FormControlMixinTypes').FormControlHost} FormControlHost
|
||||
* @typedef {import('../../types/registration/FormRegistrarMixinTypes').FormRegistrarMixin} FormRegistrarMixin
|
||||
* @typedef {import('../../types/registration/FormRegistrarMixinTypes').FormRegistrarHost} FormRegistrarHost
|
||||
* @typedef {import('../../types/registration/FormRegistrarMixinTypes').ElementWithParentFormGroup} ElementWithParentFormGroup
|
||||
* @typedef {import('../../types/registration/FormRegisteringMixinTypes').FormRegisteringHost} FormRegisteringHost
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('../../types/FormControlMixinTypes').FormControlHost} FormControlHost
|
||||
* @typedef {FormControlHost & HTMLElement & {_parentFormGroup?:HTMLElement, checked?:boolean}} FormControl
|
||||
*/
|
||||
|
||||
|
|
@ -28,6 +26,7 @@ import { FormRegisteringMixin } from './FormRegisteringMixin.js';
|
|||
const FormRegistrarMixinImplementation = superclass =>
|
||||
// eslint-disable-next-line no-shadow, no-unused-vars
|
||||
class extends FormRegisteringMixin(superclass) {
|
||||
/** @type {any} */
|
||||
static get properties() {
|
||||
return {
|
||||
/**
|
||||
|
|
@ -131,9 +130,8 @@ const FormRegistrarMixinImplementation = superclass =>
|
|||
*/
|
||||
addFormElement(child, indexToInsertAt) {
|
||||
// This is a way to let the child element (a lion-fieldset or lion-field) know, about its parent
|
||||
// @ts-expect-error FormControl needs to be at the bottom of the hierarchy
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
child._parentFormGroup = this;
|
||||
child._parentFormGroup = /** @type {* & FormRegistrarHost} */ (this);
|
||||
|
||||
// 1. Add children as array element
|
||||
if (indexToInsertAt >= 0) {
|
||||
|
|
@ -149,7 +147,6 @@ const FormRegistrarMixinImplementation = superclass =>
|
|||
console.info('Error Node:', child); // eslint-disable-line no-console
|
||||
throw new TypeError('You need to define a name');
|
||||
}
|
||||
// @ts-expect-error this._isFormOrFieldset true means we can assume `this.name` exists
|
||||
if (name === this.name) {
|
||||
console.info('Error Node:', child); // eslint-disable-line no-console
|
||||
throw new TypeError(`You can not have the same name "${name}" as your parent`);
|
||||
|
|
@ -176,7 +173,7 @@ const FormRegistrarMixinImplementation = superclass =>
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {FormRegisteringHost} child the child element (field)
|
||||
* @param {FormControlHost} child the child element (field)
|
||||
*/
|
||||
removeFormElement(child) {
|
||||
// 1. Handle array based children
|
||||
|
|
@ -187,7 +184,6 @@ const FormRegistrarMixinImplementation = superclass =>
|
|||
|
||||
// 2. Handle name based object keys
|
||||
if (this._isFormOrFieldset) {
|
||||
// @ts-expect-error
|
||||
const { name } = child; // FIXME: <-- ElementWithParentFormGroup should become LionFieldWithParentFormGroup so that "name" exists
|
||||
if (name.substr(-2) === '[]' && this.formElements[name]) {
|
||||
const idx = this.formElements[name].indexOf(child);
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ const SyncUpdatableMixinImplementation = superclass =>
|
|||
* @private
|
||||
*/
|
||||
static __syncUpdatableHasChanged(name, newValue, oldValue) {
|
||||
// @ts-expect-error accessing private lit property
|
||||
// @ts-expect-error [external]: accessing private lit property
|
||||
const properties = this._classProperties;
|
||||
if (properties.get(name) && properties.get(name).hasChanged) {
|
||||
return properties.get(name).hasChanged(newValue, oldValue);
|
||||
|
|
|
|||
|
|
@ -99,7 +99,6 @@ export const ValidateMixinImplementation = superclass =>
|
|||
* @overridable
|
||||
* Adds "._feedbackNode" as described below
|
||||
*/
|
||||
// @ts-ignore
|
||||
get slots() {
|
||||
/**
|
||||
* FIXME: Ugly workaround https://github.com/microsoft/TypeScript/issues/40110
|
||||
|
|
@ -460,7 +459,7 @@ export const ValidateMixinImplementation = superclass =>
|
|||
this.dispatchEvent(new Event('validate-performed', { bubbles: true }));
|
||||
if (source === 'async' || !hasAsync) {
|
||||
if (this.__validateCompleteResolve) {
|
||||
// @ts-ignore
|
||||
// @ts-ignore [allow-private]
|
||||
this.__validateCompleteResolve();
|
||||
}
|
||||
}
|
||||
|
|
@ -569,7 +568,7 @@ export const ValidateMixinImplementation = superclass =>
|
|||
if (validator.config.fieldName) {
|
||||
fieldName = await validator.config.fieldName;
|
||||
}
|
||||
// @ts-ignore
|
||||
// @ts-ignore [allow-protected]
|
||||
const message = await validator._getMessage({
|
||||
modelValue: this.modelValue,
|
||||
formControl: this,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { FormRegistrarPortalMixin } from '../src/registration/FormRegistrarPorta
|
|||
|
||||
/**
|
||||
* @typedef {Object} customConfig
|
||||
* @property {typeof LitElement} [baseElement]
|
||||
* @property {typeof LitElement|undefined} [baseElement]
|
||||
* @property {string} [customConfig.suffix]
|
||||
* @property {string} [customConfig.parentTagString]
|
||||
* @property {string} [customConfig.childTagString]
|
||||
|
|
@ -22,7 +22,6 @@ import { FormRegistrarPortalMixin } from '../src/registration/FormRegistrarPorta
|
|||
*/
|
||||
export const runRegistrationSuite = customConfig => {
|
||||
const cfg = {
|
||||
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/38535 fixed in later typescript version
|
||||
baseElement: LitElement,
|
||||
...customConfig,
|
||||
};
|
||||
|
|
@ -90,7 +89,7 @@ export const runRegistrationSuite = customConfig => {
|
|||
it('works for components that have a delayed render', async () => {
|
||||
class PerformUpdate extends FormRegistrarMixin(LitElement) {
|
||||
async performUpdate() {
|
||||
await new Promise(resolve => setTimeout(() => resolve(), 10));
|
||||
await new Promise(resolve => setTimeout(() => resolve(undefined), 10));
|
||||
await super.performUpdate();
|
||||
}
|
||||
|
||||
|
|
@ -264,7 +263,7 @@ export const runRegistrationSuite = customConfig => {
|
|||
const delayedPortalString = defineCE(
|
||||
class extends FormRegistrarPortalMixin(LitElement) {
|
||||
async performUpdate() {
|
||||
await new Promise(resolve => setTimeout(() => resolve(), 10));
|
||||
await new Promise(resolve => setTimeout(() => resolve(undefined), 10));
|
||||
await super.performUpdate();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -682,7 +682,6 @@ export function runValidateMixinSuite(customConfig) {
|
|||
it('calls "._isEmpty" when provided (useful for different modelValues)', async () => {
|
||||
class _isEmptyValidate extends ValidateMixin(LitElement) {
|
||||
_isEmpty() {
|
||||
// @ts-expect-error
|
||||
return this.modelValue.model === '';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { LionInput } from '@lion/input';
|
|||
import '@lion/fieldset/define';
|
||||
import { FormGroupMixin, Required } from '@lion/form-core';
|
||||
import { expect, html, fixture, fixtureSync, unsafeStatic } from '@open-wc/testing';
|
||||
import sinon from 'sinon';
|
||||
import { ChoiceGroupMixin } from '../../src/choice-group/ChoiceGroupMixin.js';
|
||||
import { ChoiceInputMixin } from '../../src/choice-group/ChoiceInputMixin.js';
|
||||
|
||||
|
|
@ -11,7 +12,7 @@ customElements.define('choice-input-foo', ChoiceInputFoo);
|
|||
class ChoiceInputBar extends ChoiceInputMixin(LionInput) {
|
||||
_syncNameToParentFormGroup() {
|
||||
// Always sync, without conditions
|
||||
this.name = this._parentFormGroup.name;
|
||||
this.name = this._parentFormGroup?.name || '';
|
||||
}
|
||||
}
|
||||
customElements.define('choice-input-bar', ChoiceInputBar);
|
||||
|
|
@ -626,5 +627,54 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Modelvalue event propagation', () => {
|
||||
it('sends one event for single select choice-groups', async () => {
|
||||
const formSpy = sinon.spy();
|
||||
const choiceGroupSpy = sinon.spy();
|
||||
const formEl = await fixture(html`
|
||||
<lion-fieldset name="form">
|
||||
<${parentTag} name="choice-group">
|
||||
<${childTag} id="option1" .choiceValue="${'1'}" checked></${childTag}>
|
||||
<${childTag} id="option2" .choiceValue="${'2'}"></${childTag}>
|
||||
</${parentTag}>
|
||||
</lion-fieldset>
|
||||
`);
|
||||
|
||||
const choiceGroupEl = /** @type {ChoiceInputGroup} */ (formEl.querySelector(
|
||||
'[name=choice-group]',
|
||||
));
|
||||
if (choiceGroupEl.multipleChoice) {
|
||||
return;
|
||||
}
|
||||
/** @typedef {{ checked: boolean }} checkedInterface */
|
||||
const option1El = /** @type {HTMLElement & checkedInterface} */ (formEl.querySelector(
|
||||
'#option1',
|
||||
));
|
||||
const option2El = /** @type {HTMLElement & checkedInterface} */ (formEl.querySelector(
|
||||
'#option2',
|
||||
));
|
||||
formEl.addEventListener('model-value-changed', formSpy);
|
||||
choiceGroupEl?.addEventListener('model-value-changed', choiceGroupSpy);
|
||||
|
||||
// Simulate check
|
||||
option2El.checked = true;
|
||||
// option2El.dispatchEvent(new Event('model-value-changed', { bubbles: true }));
|
||||
option1El.checked = false;
|
||||
// option1El.dispatchEvent(new Event('model-value-changed', { bubbles: true }));
|
||||
|
||||
expect(choiceGroupSpy.callCount).to.equal(1);
|
||||
const choiceGroupEv = choiceGroupSpy.firstCall.args[0];
|
||||
expect(choiceGroupEv.target).to.equal(choiceGroupEl);
|
||||
expect(choiceGroupEv.detail.formPath).to.eql([choiceGroupEl]);
|
||||
expect(choiceGroupEv.detail.isTriggeredByUser).to.be.false;
|
||||
|
||||
expect(formSpy.callCount).to.equal(1);
|
||||
const formEv = formSpy.firstCall.args[0];
|
||||
expect(formEv.target).to.equal(formEl);
|
||||
expect(formEv.detail.formPath).to.eql([choiceGroupEl, formEl]);
|
||||
expect(formEv.detail.isTriggeredByUser).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -332,11 +332,9 @@ export function runFormGroupMixinSuite(cfg = {}) {
|
|||
};
|
||||
expect(el.modelValue).to.deep.equal(initState);
|
||||
|
||||
// @ts-expect-error
|
||||
el.modelValue = undefined;
|
||||
expect(el.modelValue).to.deep.equal(initState);
|
||||
|
||||
// @ts-expect-error
|
||||
el.modelValue = null;
|
||||
expect(el.modelValue).to.deep.equal(initState);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ import sinon from 'sinon';
|
|||
import { FormControlMixin } from '../src/FormControlMixin.js';
|
||||
import { FormRegistrarMixin } from '../src/registration/FormRegistrarMixin.js';
|
||||
|
||||
/**
|
||||
* @typedef {import('../types/FormControlMixinTypes').FormControlHost} FormControl
|
||||
*/
|
||||
|
||||
describe('FormControlMixin', () => {
|
||||
const inputSlot = html`<input slot="input" />`;
|
||||
|
||||
|
|
@ -173,10 +177,13 @@ describe('FormControlMixin', () => {
|
|||
await el.updateComplete;
|
||||
await el.updateComplete;
|
||||
|
||||
// @ts-ignore allow protected accessors in tests
|
||||
const inputId = el._inputId;
|
||||
|
||||
// 1a. addToAriaLabelledBy()
|
||||
// Check if the aria attr is filled initially
|
||||
expect(/** @type {string} */ (el._inputNode.getAttribute('aria-labelledby'))).to.contain(
|
||||
`label-${el._inputId}`,
|
||||
`label-${inputId}`,
|
||||
);
|
||||
const additionalLabel = /** @type {HTMLElement} */ (wrapper.querySelector(
|
||||
'#additionalLabel',
|
||||
|
|
@ -188,8 +195,7 @@ describe('FormControlMixin', () => {
|
|||
expect(labelledbyAttr).to.contain(`additionalLabel`);
|
||||
// Should be placed in the end
|
||||
expect(
|
||||
labelledbyAttr.indexOf(`label-${el._inputId}`) <
|
||||
labelledbyAttr.indexOf('additionalLabel'),
|
||||
labelledbyAttr.indexOf(`label-${inputId}`) < labelledbyAttr.indexOf('additionalLabel'),
|
||||
);
|
||||
|
||||
// 1b. removeFromAriaLabelledBy()
|
||||
|
|
@ -202,7 +208,7 @@ describe('FormControlMixin', () => {
|
|||
// 2a. addToAriaDescribedBy()
|
||||
// Check if the aria attr is filled initially
|
||||
expect(/** @type {string} */ (el._inputNode.getAttribute('aria-describedby'))).to.contain(
|
||||
`feedback-${el._inputId}`,
|
||||
`feedback-${inputId}`,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -370,47 +376,6 @@ describe('FormControlMixin', () => {
|
|||
expect(formEv.detail.formPath).to.eql([fieldEl, fieldsetEl, formEl]);
|
||||
});
|
||||
|
||||
it('sends one event for single select choice-groups', async () => {
|
||||
const formSpy = sinon.spy();
|
||||
const choiceGroupSpy = sinon.spy();
|
||||
const formEl = await fixture(html`
|
||||
<${groupTag} name="form">
|
||||
<${groupTag} name="choice-group" ._repropagationRole=${'choice-group'}>
|
||||
<${tag} name="choice-group" id="option1" .checked=${true}></${tag}>
|
||||
<${tag} name="choice-group" id="option2"></${tag}>
|
||||
</${groupTag}>
|
||||
</${groupTag}>
|
||||
`);
|
||||
const choiceGroupEl = formEl.querySelector('[name=choice-group]');
|
||||
/** @typedef {{ checked: boolean }} checkedInterface */
|
||||
const option1El = /** @type {HTMLElement & checkedInterface} */ (formEl.querySelector(
|
||||
'#option1',
|
||||
));
|
||||
const option2El = /** @type {HTMLElement & checkedInterface} */ (formEl.querySelector(
|
||||
'#option2',
|
||||
));
|
||||
formEl.addEventListener('model-value-changed', formSpy);
|
||||
choiceGroupEl?.addEventListener('model-value-changed', choiceGroupSpy);
|
||||
|
||||
// Simulate check
|
||||
option2El.checked = true;
|
||||
option2El.dispatchEvent(new Event('model-value-changed', { bubbles: true }));
|
||||
option1El.checked = false;
|
||||
option1El.dispatchEvent(new Event('model-value-changed', { bubbles: true }));
|
||||
|
||||
expect(choiceGroupSpy.callCount).to.equal(1);
|
||||
const choiceGroupEv = choiceGroupSpy.firstCall.args[0];
|
||||
expect(choiceGroupEv.target).to.equal(choiceGroupEl);
|
||||
expect(choiceGroupEv.detail.formPath).to.eql([choiceGroupEl]);
|
||||
expect(choiceGroupEv.detail.isTriggeredByUser).to.be.false;
|
||||
|
||||
expect(formSpy.callCount).to.equal(1);
|
||||
const formEv = formSpy.firstCall.args[0];
|
||||
expect(formEv.target).to.equal(formEl);
|
||||
expect(formEv.detail.formPath).to.eql([choiceGroupEl, formEl]);
|
||||
expect(formEv.detail.isTriggeredByUser).to.be.false;
|
||||
});
|
||||
|
||||
it('sets "isTriggeredByUser" event detail when event triggered by user', async () => {
|
||||
const formSpy = sinon.spy();
|
||||
const fieldsetSpy = sinon.spy();
|
||||
|
|
|
|||
|
|
@ -51,7 +51,9 @@ function getSlot(el, slot) {
|
|||
describe('<lion-field>', () => {
|
||||
it(`puts a unique id "${tagString}-[hash]" on the native input`, async () => {
|
||||
const el = /** @type {LionField} */ (await fixture(html`<${tag}>${inputSlot}</${tag}>`));
|
||||
expect(getSlot(el, 'input').id).to.equal(el._inputId);
|
||||
// @ts-ignore allow protected accessors in tests
|
||||
const inputId = el._inputId;
|
||||
expect(getSlot(el, 'input').id).to.equal(inputId);
|
||||
});
|
||||
|
||||
it(`has a fieldName based on the label`, async () => {
|
||||
|
|
@ -168,10 +170,11 @@ describe('<lion-field>', () => {
|
|||
</${tag}>
|
||||
`));
|
||||
const nativeInput = getSlot(el, 'input');
|
||||
|
||||
expect(nativeInput.getAttribute('aria-labelledby')).to.equal(`label-${el._inputId}`);
|
||||
expect(nativeInput.getAttribute('aria-describedby')).to.contain(`help-text-${el._inputId}`);
|
||||
expect(nativeInput.getAttribute('aria-describedby')).to.contain(`feedback-${el._inputId}`);
|
||||
// @ts-ignore allow protected accessors in tests
|
||||
const inputId = el._inputId;
|
||||
expect(nativeInput.getAttribute('aria-labelledby')).to.equal(`label-${inputId}`);
|
||||
expect(nativeInput.getAttribute('aria-describedby')).to.contain(`help-text-${inputId}`);
|
||||
expect(nativeInput.getAttribute('aria-describedby')).to.contain(`feedback-${inputId}`);
|
||||
});
|
||||
|
||||
it(`allows additional slots (prefix, suffix, before, after) to be included in labelledby
|
||||
|
|
@ -186,11 +189,13 @@ describe('<lion-field>', () => {
|
|||
`));
|
||||
|
||||
const nativeInput = getSlot(el, 'input');
|
||||
// @ts-ignore allow protected accessors in tests
|
||||
const inputId = el._inputId;
|
||||
expect(nativeInput.getAttribute('aria-labelledby')).to.contain(
|
||||
`before-${el._inputId} after-${el._inputId}`,
|
||||
`before-${inputId} after-${inputId}`,
|
||||
);
|
||||
expect(nativeInput.getAttribute('aria-describedby')).to.contain(
|
||||
`prefix-${el._inputId} suffix-${el._inputId}`,
|
||||
`prefix-${inputId} suffix-${inputId}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ import { LitElement, nothing, TemplateResult, CSSResultArray } from '@lion/core'
|
|||
import { SlotsMap, SlotHost } from '@lion/core/types/SlotMixinTypes';
|
||||
import { Constructor } from '@open-wc/dedupe-mixin';
|
||||
import { DisabledHost } from '@lion/core/types/DisabledMixinTypes';
|
||||
import { FormRegisteringHost } from './registration/FormRegisteringMixinTypes';
|
||||
|
||||
import { LionValidationFeedback } from '../src/validate/LionValidationFeedback';
|
||||
import { FormRegisteringHost } from './registration/FormRegisteringMixinTypes';
|
||||
import { Unparseable } from '../src/validate/Unparseable.js';
|
||||
|
||||
export type ModelValueEventDetails = {
|
||||
/**
|
||||
|
|
@ -83,14 +84,15 @@ export declare class FormControlHost {
|
|||
* - For a number input: a formatted String '1.234,56' will be converted to a Number:
|
||||
* 1234.56
|
||||
*/
|
||||
public modelValue: unknown;
|
||||
public get modelValue(): any | Unparseable;
|
||||
public set modelValue(value: any | Unparseable);
|
||||
/**
|
||||
* The label text for the input node.
|
||||
* When no light dom defined via [slot=label], this value will be used
|
||||
*/
|
||||
public get label(): string;
|
||||
public set label(arg: string);
|
||||
__label: string | undefined;
|
||||
__label: string;
|
||||
/**
|
||||
* The helpt text for the input node.
|
||||
* When no light dom defined via [slot=help-text], this value will be used
|
||||
|
|
@ -101,14 +103,13 @@ export declare class FormControlHost {
|
|||
public set fieldName(arg: string);
|
||||
public get fieldName(): string;
|
||||
__fieldName: string | undefined;
|
||||
public get slots(): SlotsMap;
|
||||
get _inputNode(): HTMLElementWithValue;
|
||||
get _labelNode(): HTMLElement;
|
||||
get _helpTextNode(): HTMLElement;
|
||||
get _feedbackNode(): LionValidationFeedback | undefined;
|
||||
_inputId: string;
|
||||
_ariaLabelledNodes: HTMLElement[];
|
||||
_ariaDescribedNodes: HTMLElement[];
|
||||
get _feedbackNode(): LionValidationFeedback;
|
||||
protected _inputId: string;
|
||||
protected _ariaLabelledNodes: HTMLElement[];
|
||||
protected _ariaDescribedNodes: HTMLElement[];
|
||||
/**
|
||||
* Based on the role, details of handling model-value-changed repropagation differ.
|
||||
*/
|
||||
|
|
@ -131,23 +132,23 @@ export declare class FormControlHost {
|
|||
render(): TemplateResult;
|
||||
protected _groupOneTemplate(): TemplateResult;
|
||||
protected _groupTwoTemplate(): TemplateResult;
|
||||
_labelTemplate(): TemplateResult;
|
||||
_helpTextTemplate(): TemplateResult;
|
||||
protected _labelTemplate(): TemplateResult;
|
||||
protected _helpTextTemplate(): TemplateResult;
|
||||
protected _inputGroupTemplate(): TemplateResult;
|
||||
_inputGroupBeforeTemplate(): TemplateResult;
|
||||
_inputGroupPrefixTemplate(): TemplateResult | typeof nothing;
|
||||
protected _inputGroupBeforeTemplate(): TemplateResult;
|
||||
protected _inputGroupPrefixTemplate(): TemplateResult | typeof nothing;
|
||||
protected _inputGroupInputTemplate(): TemplateResult;
|
||||
_inputGroupSuffixTemplate(): TemplateResult | typeof nothing;
|
||||
_inputGroupAfterTemplate(): TemplateResult;
|
||||
_feedbackTemplate(): TemplateResult;
|
||||
protected _inputGroupSuffixTemplate(): TemplateResult | typeof nothing;
|
||||
protected _inputGroupAfterTemplate(): TemplateResult;
|
||||
protected _feedbackTemplate(): TemplateResult;
|
||||
|
||||
protected _triggerInitialModelValueChangedEvent(): void;
|
||||
_enhanceLightDomClasses(): void;
|
||||
_enhanceLightDomA11y(): void;
|
||||
_enhanceLightDomA11yForAdditionalSlots(additionalSlots?: string[]): void;
|
||||
protected _enhanceLightDomClasses(): void;
|
||||
protected _enhanceLightDomA11y(): void;
|
||||
protected _enhanceLightDomA11yForAdditionalSlots(additionalSlots?: string[]): void;
|
||||
__reflectAriaAttr(attrName: string, nodes: HTMLElement[], reorder: boolean | undefined): void;
|
||||
protected _isEmpty(modelValue?: unknown): boolean;
|
||||
_getAriaDescriptionElements(): HTMLElement[];
|
||||
protected _isEmpty(modelValue?: any): boolean;
|
||||
protected _getAriaDescriptionElements(): HTMLElement[];
|
||||
public addToAriaLabelledBy(
|
||||
element: HTMLElement,
|
||||
customConfig?: {
|
||||
|
|
@ -176,13 +177,13 @@ export declare class FormControlHost {
|
|||
},
|
||||
): void;
|
||||
__reorderAriaDescribedNodes: boolean | undefined;
|
||||
__getDirectSlotChild(slotName: string): HTMLElement;
|
||||
__getDirectSlotChild(slotName: string): HTMLElement | undefined;
|
||||
__dispatchInitialModelValueChangedEvent(): void;
|
||||
__repropagateChildrenInitialized: boolean | undefined;
|
||||
protected _onBeforeRepropagateChildrenValues(ev: CustomEvent): void;
|
||||
__repropagateChildrenValues(ev: CustomEvent): void;
|
||||
_parentFormGroup: FormControlHost;
|
||||
_repropagationCondition(target: FormControlHost): boolean;
|
||||
protected _parentFormGroup: FormControlHost | undefined;
|
||||
protected _repropagationCondition(target: FormControlHost): boolean;
|
||||
}
|
||||
|
||||
export declare function FormControlImplementation<T extends Constructor<LitElement>>(
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export declare class FormatHost {
|
|||
__isHandlingUserInput: boolean;
|
||||
|
||||
parser(v: string, opts: FormatNumberOptions): unknown;
|
||||
formatter(v: unknown, opts: FormatNumberOptions): string;
|
||||
formatter(v: unknown, opts?: FormatNumberOptions): string;
|
||||
serializer(v: unknown): string;
|
||||
deserializer(v: string): unknown;
|
||||
preprocessor(v: string): string;
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ export declare class ChoiceGroupHost {
|
|||
__delegateNameAttribute(child: FormControlHost): void;
|
||||
|
||||
protected _onBeforeRepropagateChildrenValues(ev: Event): void;
|
||||
__oldModelValue: any;
|
||||
protected _oldModelValue: any;
|
||||
}
|
||||
|
||||
export declare function ChoiceGroupImplementation<T extends Constructor<LitElement>>(
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ export interface ChoiceInputSerializedValue {
|
|||
}
|
||||
|
||||
export declare class ChoiceInputHost {
|
||||
modelValue: ChoiceInputModelValue;
|
||||
get modelValue(): ChoiceInputModelValue;
|
||||
set modelValue(value: ChoiceInputModelValue);
|
||||
serializedValue: ChoiceInputSerializedValue;
|
||||
|
||||
checked: boolean;
|
||||
|
|
@ -71,7 +72,7 @@ export declare class ChoiceInputHost {
|
|||
|
||||
type: string;
|
||||
|
||||
_inputNode: HTMLElement;
|
||||
get _inputNode(): HTMLElement;
|
||||
}
|
||||
|
||||
export declare function ChoiceInputImplementation<T extends Constructor<LitElement>>(
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { ValidateHost } from '../validate/ValidateMixinTypes';
|
|||
|
||||
export declare class FormGroupHost {
|
||||
protected static _addDescriptionElementIdsToField(): void;
|
||||
_inputNode: HTMLElement;
|
||||
get _inputNode(): HTMLElement;
|
||||
submitGroup(): void;
|
||||
resetGroup(): void;
|
||||
prefilled: boolean;
|
||||
|
|
@ -16,7 +16,8 @@ export declare class FormGroupHost {
|
|||
dirty: boolean;
|
||||
submitted: boolean;
|
||||
serializedValue: { [key: string]: any };
|
||||
modelValue: { [x: string]: any };
|
||||
get modelValue(): { [x: string]: any };
|
||||
set modelValue(value: { [x: string]: any });
|
||||
formattedValue: string;
|
||||
children: Array<HTMLElement & FormControlHost>;
|
||||
_initialModelValue: { [x: string]: any };
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import { Constructor } from '@open-wc/dedupe-mixin';
|
||||
import { FormRegistrarHost } from './FormRegistrarMixinTypes';
|
||||
|
||||
import { LitElement } from '@lion/core';
|
||||
|
||||
export declare class FormRegisteringHost {
|
||||
connectedCallback(): void;
|
||||
disconnectedCallback(): void;
|
||||
_parentFormGroup?: FormRegistrarHost;
|
||||
protected _parentFormGroup: FormRegistrarHost | undefined;
|
||||
public name: string;
|
||||
}
|
||||
|
||||
export declare function FormRegisteringImplementation<T extends Constructor<LitElement>>(
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ export declare class ValidateHost {
|
|||
fieldName: string;
|
||||
|
||||
static validationTypes: string[];
|
||||
slots: SlotsMap;
|
||||
_feedbackNode: LionValidationFeedback;
|
||||
_allValidators: Validator[];
|
||||
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ describe('lion-select', () => {
|
|||
it(getFirstPaintTitle(firstStampCount), async () => {
|
||||
const spy = sinon.spy();
|
||||
await fixture(html`
|
||||
<lion-select @model-value-changed="${spy}">
|
||||
<lion-select @model-value-changed="${/** @type {function} */ (spy)}">
|
||||
<select slot="input">
|
||||
<option value="option1"></option>
|
||||
<option value="option2"></option>
|
||||
|
|
@ -310,7 +310,7 @@ describe('lion-fieldset', () => {
|
|||
it(getFirstPaintTitle(firstStampCount), async () => {
|
||||
const spy = sinon.spy();
|
||||
await fixture(html`
|
||||
<lion-fieldset name="parent" @model-value-changed="${spy}">
|
||||
<lion-fieldset name="parent" @model-value-changed="${/** @type {function} */ (spy)}">
|
||||
<lion-input name="input"></lion-input>
|
||||
</lion-fieldset>
|
||||
`);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ import { parseAmount } from './parsers.js';
|
|||
*
|
||||
* @customElement lion-input-amount
|
||||
*/
|
||||
// @ts-ignore
|
||||
// TODO: make __callParser protected => _callParser
|
||||
// @ts-ignore [allow-private]: __callParser
|
||||
export class LionInputAmount extends LocalizeMixin(LionInput) {
|
||||
/** @type {any} */
|
||||
static get properties() {
|
||||
|
|
@ -110,7 +111,7 @@ export class LionInputAmount extends LocalizeMixin(LionInput) {
|
|||
this.__parserCallcountSincePaste += 1;
|
||||
this.__isPasting = this.__parserCallcountSincePaste === 2;
|
||||
this.formatOptions.mode = this.__isPasting === true ? 'pasted' : 'auto';
|
||||
// @ts-ignore
|
||||
// @ts-ignore [allow-private]
|
||||
return super.__callParser(value);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { formatDate, LocalizeMixin, parseDate } from '@lion/localize';
|
|||
*/
|
||||
function isValidDate(date) {
|
||||
// to make sure it is a valid date we use isNaN and not Number.isNaN
|
||||
// @ts-ignore dirty hack, you're not supposed to pass Date instances to isNaN
|
||||
// @ts-ignore [allow]: dirty hack, you're not supposed to pass Date instances to isNaN
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
return date instanceof Date && !isNaN(date);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,7 +104,6 @@ export class LionInputStepper extends LionInput {
|
|||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
get slots() {
|
||||
return {
|
||||
...super.slots,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import { css, DisabledMixin, html, LitElement } from '@lion/core';
|
|||
* enabling SubClassers to style based on those states
|
||||
*/
|
||||
export class LionOption extends DisabledMixin(ChoiceInputMixin(FormRegisteringMixin(LitElement))) {
|
||||
/** @type {any} */
|
||||
static get properties() {
|
||||
return {
|
||||
active: {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { LionOptions } from './LionOptions.js';
|
|||
|
||||
/**
|
||||
* @typedef {import('@lion/form-core/types/FormControlMixinTypes').HTMLElementWithValue} HTMLElementWithValue
|
||||
* @typedef {import('@lion/form-core/types/FormControlMixinTypes').FormControlHost} FormControlHost
|
||||
* @typedef {import('./LionOption').LionOption} LionOption
|
||||
* @typedef {import('../types/ListboxMixinTypes').ListboxMixin} ListboxMixin
|
||||
* @typedef {import('../types/ListboxMixinTypes').ListboxHost} ListboxHost
|
||||
|
|
@ -54,6 +55,7 @@ const ListboxMixinImplementation = superclass =>
|
|||
class ListboxMixin extends FormControlMixin(
|
||||
ScopedElementsMixin(ChoiceGroupMixin(SlotMixin(FormRegistrarMixin(superclass)))),
|
||||
) {
|
||||
/** @type {any} */
|
||||
static get properties() {
|
||||
return {
|
||||
orientation: String,
|
||||
|
|
@ -117,7 +119,6 @@ const ListboxMixinImplementation = superclass =>
|
|||
};
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
get slots() {
|
||||
return {
|
||||
...super.slots,
|
||||
|
|
@ -267,7 +268,10 @@ const ListboxMixinImplementation = superclass =>
|
|||
this._listboxActiveDescendant = null;
|
||||
/** @private */
|
||||
this.__hasInitialSelectedFormElement = false;
|
||||
/** @protected */
|
||||
/**
|
||||
* @type {'fieldset' | 'child' | 'choice-group'}
|
||||
* @protected
|
||||
*/
|
||||
this._repropagationRole = 'choice-group'; // configures FormControlMixin
|
||||
|
||||
/**
|
||||
|
|
@ -279,9 +283,9 @@ const ListboxMixinImplementation = superclass =>
|
|||
|
||||
/**
|
||||
* @type {string | string[] | undefined}
|
||||
* @private
|
||||
* @protected
|
||||
*/
|
||||
this.__oldModelValue = undefined;
|
||||
this._oldModelValue = undefined;
|
||||
|
||||
/**
|
||||
* @type {EventListener}
|
||||
|
|
@ -403,12 +407,10 @@ const ListboxMixinImplementation = superclass =>
|
|||
|
||||
/**
|
||||
* @enhance FormRegistrarMixin: make sure children have specific default states when added
|
||||
* @param {LionOption} child
|
||||
* @param {FormControlHost & LionOption} child
|
||||
* @param {Number} indexToInsertAt
|
||||
*/
|
||||
// @ts-expect-error
|
||||
addFormElement(child, indexToInsertAt) {
|
||||
// @ts-expect-error
|
||||
super.addFormElement(/** @type {FormControl} */ child, indexToInsertAt);
|
||||
// we need to adjust the elements being registered
|
||||
/* eslint-disable no-param-reassign */
|
||||
|
|
@ -426,7 +428,7 @@ const ListboxMixinImplementation = superclass =>
|
|||
});
|
||||
|
||||
this.__proxyChildModelValueChanged(
|
||||
/** @type {CustomEvent & { target: LionOption; }} */ ({ target: child }),
|
||||
/** @type {CustomEvent & { target: FormControlHost & LionOption; }} */ ({ target: child }),
|
||||
);
|
||||
this.resetInteractionState();
|
||||
/* eslint-enable no-param-reassign */
|
||||
|
|
@ -760,7 +762,7 @@ const ListboxMixinImplementation = superclass =>
|
|||
this.__onChildCheckedChanged(ev);
|
||||
|
||||
// don't send this.modelValue as oldValue, since it will take modelValue getter which takes it from child elements, which is already the updated value
|
||||
this.requestUpdate('modelValue', this.__oldModelValue);
|
||||
this.requestUpdate('modelValue', this._oldModelValue);
|
||||
// only send model-value-changed if the event is caused by one of its children
|
||||
if (ev.detail && ev.detail.formPath) {
|
||||
this.dispatchEvent(
|
||||
|
|
@ -773,7 +775,7 @@ const ListboxMixinImplementation = superclass =>
|
|||
}),
|
||||
);
|
||||
}
|
||||
this.__oldModelValue = this.modelValue;
|
||||
this._oldModelValue = this.modelValue;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export declare class ListboxHost {
|
|||
/** Reset interaction states and modelValue */
|
||||
public reset(): void;
|
||||
|
||||
protected get _scrollTargetNode(): LionOptions;
|
||||
protected get _scrollTargetNode(): HTMLElement;
|
||||
|
||||
protected get _listboxNode(): LionOptions;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// @ts-expect-error no types for this package
|
||||
// @ts-expect-error [external]: no types for this package
|
||||
import MessageFormat from '@bundled-es-modules/message-format/MessageFormat.js';
|
||||
import isLocalizeESModule from './isLocalizeESModule.js';
|
||||
|
||||
|
|
|
|||
|
|
@ -18,13 +18,13 @@ import { containFocus } from './utils/contain-focus.js';
|
|||
* @returns {Promise<PopperModule>}
|
||||
*/
|
||||
async function preloadPopper() {
|
||||
// @ts-ignore import complains about untyped module, but we typecast it ourselves
|
||||
return /** @type {Promise<PopperModule>} */ (import('@popperjs/core/dist/esm/popper.js'));
|
||||
// @ts-ignore [external]: import complains about untyped module, but we typecast it ourselves
|
||||
return /** @type {* & Promise<PopperModule>} */ (import('@popperjs/core/dist/esm/popper.js'));
|
||||
}
|
||||
|
||||
const GLOBAL_OVERLAYS_CONTAINER_CLASS = 'global-overlays__overlay-container';
|
||||
const GLOBAL_OVERLAYS_CLASS = 'global-overlays__overlay';
|
||||
// @ts-expect-error CSS not yet typed
|
||||
// @ts-expect-error [external]: CSS not yet typed
|
||||
const supportsCSSTypedObject = window.CSS && CSS.number;
|
||||
|
||||
/**
|
||||
|
|
@ -398,7 +398,7 @@ export class OverlayController extends EventTargetShim {
|
|||
}
|
||||
/** config [l2] or [l4] */
|
||||
if (this.__isContentNodeProjected) {
|
||||
// @ts-expect-error
|
||||
// @ts-expect-error [external]: fix Node types
|
||||
return this.__originalContentParent?.getRootNode().host;
|
||||
}
|
||||
/** config [l1] or [l3] */
|
||||
|
|
@ -529,7 +529,7 @@ export class OverlayController extends EventTargetShim {
|
|||
if (this.placementMode === 'local') {
|
||||
// Lazily load Popper if not done yet
|
||||
if (!OverlayController.popperModule) {
|
||||
// @ts-expect-error FIXME: for some reason createPopper is missing here
|
||||
// a@ts-expect-error FIXME: for some reason createPopper is missing here
|
||||
OverlayController.popperModule = preloadPopper();
|
||||
}
|
||||
}
|
||||
|
|
@ -784,9 +784,9 @@ export class OverlayController extends EventTargetShim {
|
|||
const newMarginRight = this.__bodyMarginRight + scrollbarWidth;
|
||||
const newMarginBottom = this.__bodyMarginBottom + scrollbarHeight;
|
||||
if (supportsCSSTypedObject) {
|
||||
// @ts-expect-error types attributeStyleMap + CSS.px not available yet
|
||||
// @ts-expect-error [external]: types attributeStyleMap + CSS.px not available yet
|
||||
document.body.attributeStyleMap.set('margin-right', CSS.px(newMarginRight));
|
||||
// @ts-expect-error types attributeStyleMap + CSS.px not available yet
|
||||
// @ts-expect-error [external]: types attributeStyleMap + CSS.px not available yet
|
||||
document.body.attributeStyleMap.set('margin-bottom', CSS.px(newMarginBottom));
|
||||
} else {
|
||||
document.body.style.marginRight = `${newMarginRight}px`;
|
||||
|
|
@ -1300,5 +1300,5 @@ export class OverlayController extends EventTargetShim {
|
|||
}
|
||||
}
|
||||
}
|
||||
/** @type {PopperModule | undefined} */
|
||||
/** @type {Promise<PopperModule> | undefined} */
|
||||
OverlayController.popperModule = undefined;
|
||||
|
|
|
|||
|
|
@ -175,7 +175,9 @@ export class OverlaysManager {
|
|||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
/**
|
||||
* @param {{ disabledCtrl?:OverlayController, findNewTrap?:boolean }} [options]
|
||||
*/
|
||||
informTrapsKeyboardFocusGotDisabled({ disabledCtrl, findNewTrap = true } = {}) {
|
||||
const next = this.shownList.find(
|
||||
ctrl => ctrl !== disabledCtrl && ctrl.trapsKeyboardFocus === true,
|
||||
|
|
|
|||
|
|
@ -28,11 +28,9 @@ function mergeSortByTabIndex(left, right) {
|
|||
const result = [];
|
||||
while (left.length > 0 && right.length > 0) {
|
||||
if (hasLowerTabOrder(left[0], right[0])) {
|
||||
// @ts-ignore
|
||||
result.push(right.shift());
|
||||
result.push(/** @type {HTMLElement} */ (right.shift()));
|
||||
} else {
|
||||
// @ts-ignore
|
||||
result.push(left.shift());
|
||||
result.push(/** @type {HTMLElement} */ (left.shift()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,7 +68,6 @@ export class LionSelectRich extends SlotMixin(ScopedElementsMixin(OverlayMixin(L
|
|||
`;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
get slots() {
|
||||
return {
|
||||
...super.slots,
|
||||
|
|
@ -98,8 +97,10 @@ export class LionSelectRich extends SlotMixin(ScopedElementsMixin(OverlayMixin(L
|
|||
*/
|
||||
get _scrollTargetNode() {
|
||||
// TODO: should this be defined here or in extension layer?
|
||||
// @ts-expect-error we allow the _overlayContentNode to define its own _scrollTargetNode
|
||||
return this._overlayContentNode._scrollTargetNode || this._overlayContentNode;
|
||||
return /** @type {HTMLElement} */ (
|
||||
/** @type {HTMLElement & {_scrollTargetNode?: HTMLElement}} */ (this._overlayContentNode)
|
||||
._scrollTargetNode || this._overlayContentNode
|
||||
);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
|
|
|
|||
|
|
@ -30,14 +30,13 @@ export class LionSwitch extends ScopedElementsMixin(ChoiceInputMixin(LionField))
|
|||
* Therefore we do a full override and typecast to an intersection type that includes LionSwitchButton
|
||||
* @returns {LionSwitchButton}
|
||||
*/
|
||||
// @ts-ignore
|
||||
// @ts-ignore [editor]: prevents vscode from complaining
|
||||
get _inputNode() {
|
||||
return /** @type {LionSwitchButton} */ (Array.from(this.children).find(
|
||||
el => el.slot === 'input',
|
||||
));
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
get slots() {
|
||||
return {
|
||||
...super.slots,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-disable max-classes-per-file */
|
||||
// @ts-expect-error https://github.com/jackmoore/autosize/pull/384 wait for this, then we can switch to just 'autosize'; and then types will work!
|
||||
// @ts-expect-error [external]: https://github.com/jackmoore/autosize/pull/384 wait for this, then we can switch to just 'autosize'; and then types will work!
|
||||
import autosize from 'autosize/src/autosize.js';
|
||||
import { LionField, NativeTextFieldMixin } from '@lion/form-core';
|
||||
import { css } from '@lion/core';
|
||||
|
|
@ -43,7 +43,6 @@ export class LionTextarea extends NativeTextFieldMixin(LionFieldWithTextArea) {
|
|||
};
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
get slots() {
|
||||
return {
|
||||
...super.slots,
|
||||
|
|
|
|||
Loading…
Reference in a new issue