Merge pull request #1180 from ing-bank/fix/types-base-constructor
fix: types base constructor same return type
This commit is contained in:
commit
0b28c46574
79 changed files with 409 additions and 298 deletions
21
.changeset/four-roses-fetch.md
Normal file
21
.changeset/four-roses-fetch.md
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
---
|
||||||
|
'@lion/calendar': patch
|
||||||
|
'@lion/checkbox-group': patch
|
||||||
|
'@lion/core': patch
|
||||||
|
'@lion/fieldset': patch
|
||||||
|
'@lion/form-core': patch
|
||||||
|
'@lion/input-date': patch
|
||||||
|
'@lion/input-datepicker': patch
|
||||||
|
'@lion/input-email': patch
|
||||||
|
'@lion/input-iban': patch
|
||||||
|
'@lion/listbox': patch
|
||||||
|
'@lion/localize': patch
|
||||||
|
'@lion/overlays': patch
|
||||||
|
'@lion/pagination': patch
|
||||||
|
'@lion/progress-indicator': patch
|
||||||
|
'@lion/radio-group': patch
|
||||||
|
'@lion/select-rich': patch
|
||||||
|
'@lion/switch': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix type issues where base constructors would not have the same return type. This allows us to remove a LOT of @ts-expect-errors/@ts-ignores across lion.
|
||||||
23
.changeset/lemon-countries-clap.md
Normal file
23
.changeset/lemon-countries-clap.md
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
'@lion/calendar': patch
|
||||||
|
'@lion/checkbox-group': patch
|
||||||
|
'@lion/combobox': patch
|
||||||
|
'@lion/core': patch
|
||||||
|
'@lion/fieldset': patch
|
||||||
|
'@lion/form-core': patch
|
||||||
|
'@lion/form-integrations': patch
|
||||||
|
'@lion/input-date': patch
|
||||||
|
'@lion/input-datepicker': patch
|
||||||
|
'@lion/input-email': patch
|
||||||
|
'@lion/input-iban': patch
|
||||||
|
'@lion/listbox': patch
|
||||||
|
'@lion/localize': patch
|
||||||
|
'@lion/overlays': patch
|
||||||
|
'@lion/pagination': patch
|
||||||
|
'@lion/progress-indicator': patch
|
||||||
|
'@lion/radio-group': patch
|
||||||
|
'@lion/select-rich': patch
|
||||||
|
'@lion/switch': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix missing types and update to latest scoped elements to fix constructor type.
|
||||||
|
|
@ -2,3 +2,4 @@ node_modules
|
||||||
coverage/
|
coverage/
|
||||||
bundlesize/
|
bundlesize/
|
||||||
.history/
|
.history/
|
||||||
|
*.d.ts
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ import { isSameDate } from './utils/isSameDate.js';
|
||||||
/**
|
/**
|
||||||
* @customElement lion-calendar
|
* @customElement lion-calendar
|
||||||
*/
|
*/
|
||||||
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/40110
|
|
||||||
export class LionCalendar extends LocalizeMixin(LitElement) {
|
export class LionCalendar extends LocalizeMixin(LitElement) {
|
||||||
static get localizeNamespaces() {
|
static get localizeNamespaces() {
|
||||||
return [
|
return [
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import { ChoiceGroupMixin, FormGroupMixin } from '@lion/form-core';
|
||||||
/**
|
/**
|
||||||
* A wrapper around multiple checkboxes
|
* A wrapper around multiple checkboxes
|
||||||
*/
|
*/
|
||||||
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/40110
|
|
||||||
export class LionCheckboxGroup extends ChoiceGroupMixin(FormGroupMixin(LitElement)) {
|
export class LionCheckboxGroup extends ChoiceGroupMixin(FormGroupMixin(LitElement)) {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
|
||||||
|
|
@ -445,7 +445,7 @@ export class GoogleCombobox extends LionCombobox {
|
||||||
}
|
}
|
||||||
|
|
||||||
_showOverlayCondition(options) {
|
_showOverlayCondition(options) {
|
||||||
return this.focused || super.showOverlayCondition(options);
|
return this.focused || super._showOverlayCondition(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
__resetFocus() {
|
__resetFocus() {
|
||||||
|
|
|
||||||
|
|
@ -372,7 +372,7 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* _showOverlayCondition(options) {
|
* _showOverlayCondition(options) {
|
||||||
* return this.focused || super.showOverlayCondition(options);
|
* return this.focused || super._showOverlayCondition(options);
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
|
|
@ -382,7 +382,7 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* _showOverlayCondition(options) {
|
* _showOverlayCondition(options) {
|
||||||
* return options.currentValue.length > 4 && super.showOverlayCondition(options);
|
* return options.currentValue.length > 4 && super._showOverlayCondition(options);
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* @param {{ currentValue: string, lastKey:string }} options
|
* @param {{ currentValue: string, lastKey:string }} options
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import { Required } from '@lion/form-core';
|
||||||
import { LionCombobox } from '../src/LionCombobox.js';
|
import { LionCombobox } from '../src/LionCombobox.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {import('../src/LionCombobox.js').LionCombobox} LionCombobox
|
|
||||||
* @typedef {import('../types/SelectionDisplay').SelectionDisplay} SelectionDisplay
|
* @typedef {import('../types/SelectionDisplay').SelectionDisplay} SelectionDisplay
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -384,8 +383,9 @@ describe('lion-combobox', () => {
|
||||||
describe('Subclassers', () => {
|
describe('Subclassers', () => {
|
||||||
it('allows to control overlay visibility via "_showOverlayCondition"', async () => {
|
it('allows to control overlay visibility via "_showOverlayCondition"', async () => {
|
||||||
class ShowOverlayConditionCombobox extends LionCombobox {
|
class ShowOverlayConditionCombobox extends LionCombobox {
|
||||||
|
/** @param {{ currentValue: string, lastKey:string }} options */
|
||||||
_showOverlayCondition(options) {
|
_showOverlayCondition(options) {
|
||||||
return this.focused || super.showOverlayCondition(options);
|
return this.focused || super._showOverlayCondition(options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const tagName = defineCE(ShowOverlayConditionCombobox);
|
const tagName = defineCE(ShowOverlayConditionCombobox);
|
||||||
|
|
@ -575,6 +575,7 @@ describe('lion-combobox', () => {
|
||||||
<lion-option .choiceValue="${'10'}" checked>Item 1</lion-option>
|
<lion-option .choiceValue="${'10'}" checked>Item 1</lion-option>
|
||||||
</lion-combobox>
|
</lion-combobox>
|
||||||
`));
|
`));
|
||||||
|
// @ts-ignore sinon type error
|
||||||
const spy = sinon.spy(el._selectionDisplayNode, 'onComboboxElementUpdated');
|
const spy = sinon.spy(el._selectionDisplayNode, 'onComboboxElementUpdated');
|
||||||
el.requestUpdate('modelValue');
|
el.requestUpdate('modelValue');
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
|
|
@ -823,6 +824,11 @@ describe('lion-combobox', () => {
|
||||||
`));
|
`));
|
||||||
expect(el._inputNode.value).to.equal('');
|
expect(el._inputNode.value).to.equal('');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {'none' | 'list' | 'inline' | 'both'} autocomplete
|
||||||
|
* @param {number|number[]} index
|
||||||
|
* @param {string} valueOnClose
|
||||||
|
*/
|
||||||
async function performChecks(autocomplete, index, valueOnClose) {
|
async function performChecks(autocomplete, index, valueOnClose) {
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
el.opened = true;
|
el.opened = true;
|
||||||
|
|
@ -945,7 +951,7 @@ describe('lion-combobox', () => {
|
||||||
`));
|
`));
|
||||||
// This ensures autocomplete would be off originally
|
// This ensures autocomplete would be off originally
|
||||||
el.autocomplete = 'list';
|
el.autocomplete = 'list';
|
||||||
await mimicUserTypingAdvanced(el, 'vi'); // so we have options ['Victoria Plum']
|
await mimicUserTypingAdvanced(el, ['v', 'i']); // so we have options ['Victoria Plum']
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
expect(el.checkedIndex).to.equal(3);
|
expect(el.checkedIndex).to.equal(3);
|
||||||
});
|
});
|
||||||
|
|
@ -1057,6 +1063,10 @@ describe('lion-combobox', () => {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {?} modelValue
|
||||||
|
* @param {?} oldModelValue
|
||||||
|
*/
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
_syncToTextboxMultiple(modelValue, oldModelValue) {
|
_syncToTextboxMultiple(modelValue, oldModelValue) {
|
||||||
// In a real scenario (depending on how selection display works),
|
// In a real scenario (depending on how selection display works),
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@open-wc/dedupe-mixin": "^1.2.18",
|
"@open-wc/dedupe-mixin": "^1.2.18",
|
||||||
"@open-wc/scoped-elements": "^1.3.2",
|
"@open-wc/scoped-elements": "^1.3.3",
|
||||||
"lit-element": "~2.4.0",
|
"lit-element": "~2.4.0",
|
||||||
"lit-html": "^1.3.0"
|
"lit-html": "^1.3.0"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
1
packages/core/types/DelegateMixinTypes.d.ts
vendored
1
packages/core/types/DelegateMixinTypes.d.ts
vendored
|
|
@ -10,6 +10,7 @@ export type Delegations = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare class DelegateHost {
|
export declare class DelegateHost {
|
||||||
|
constructor(...args: any[]);
|
||||||
delegations: Delegations;
|
delegations: Delegations;
|
||||||
|
|
||||||
protected _connectDelegateMixin(): void;
|
protected _connectDelegateMixin(): void;
|
||||||
|
|
|
||||||
3
packages/core/types/DisabledMixinTypes.d.ts
vendored
3
packages/core/types/DisabledMixinTypes.d.ts
vendored
|
|
@ -2,6 +2,7 @@ import { Constructor } from '@open-wc/dedupe-mixin';
|
||||||
import { LitElement } from '../index.js';
|
import { LitElement } from '../index.js';
|
||||||
|
|
||||||
export declare class DisabledHost {
|
export declare class DisabledHost {
|
||||||
|
constructor(...args: any[]);
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -20,6 +21,6 @@ export declare class DisabledHost {
|
||||||
|
|
||||||
export declare function DisabledMixinImplementation<T extends Constructor<LitElement>>(
|
export declare function DisabledMixinImplementation<T extends Constructor<LitElement>>(
|
||||||
superclass: T,
|
superclass: T,
|
||||||
): T & Constructor<DisabledHost>;
|
): T & Constructor<DisabledHost> & typeof DisabledHost;
|
||||||
|
|
||||||
export type DisabledMixin = typeof DisabledMixinImplementation;
|
export type DisabledMixin = typeof DisabledMixinImplementation;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { Constructor } from '@open-wc/dedupe-mixin';
|
||||||
import { DisabledHost } from './DisabledMixinTypes';
|
import { DisabledHost } from './DisabledMixinTypes';
|
||||||
import { LitElement } from '../index.js';
|
import { LitElement } from '../index.js';
|
||||||
export declare class DisabledWithTabIndexHost {
|
export declare class DisabledWithTabIndexHost {
|
||||||
|
constructor(...args: any[]);
|
||||||
tabIndex: number;
|
tabIndex: number;
|
||||||
/**
|
/**
|
||||||
* Makes request to make the element disabled and set the tabindex
|
* Makes request to make the element disabled and set the tabindex
|
||||||
|
|
|
||||||
3
packages/core/types/SlotMixinTypes.d.ts
vendored
3
packages/core/types/SlotMixinTypes.d.ts
vendored
|
|
@ -7,6 +7,7 @@ export type SlotsMap = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare class SlotHost {
|
export declare class SlotHost {
|
||||||
|
constructor(...args: any[]);
|
||||||
/**
|
/**
|
||||||
* Obtains all the slots to create
|
* Obtains all the slots to create
|
||||||
*/
|
*/
|
||||||
|
|
@ -50,6 +51,6 @@ export declare class SlotHost {
|
||||||
*/
|
*/
|
||||||
export declare function SlotMixinImplementation<T extends Constructor<HTMLElement>>(
|
export declare function SlotMixinImplementation<T extends Constructor<HTMLElement>>(
|
||||||
superclass: T,
|
superclass: T,
|
||||||
): T & Constructor<SlotHost>;
|
): T & Constructor<SlotHost> & typeof SlotHost;
|
||||||
|
|
||||||
export type SlotMixin = typeof SlotMixinImplementation;
|
export type SlotMixin = typeof SlotMixinImplementation;
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ export declare class UpdateStylesHost {
|
||||||
*
|
*
|
||||||
* @param {StylesMap} updateStyles
|
* @param {StylesMap} updateStyles
|
||||||
*/
|
*/
|
||||||
|
constructor(...args: any[]);
|
||||||
public updateStyles(updateStyles: StylesMap): void;
|
public updateStyles(updateStyles: StylesMap): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,13 +19,15 @@ import { FormGroupMixin } from '@lion/form-core';
|
||||||
*
|
*
|
||||||
* @customElement lion-fieldset
|
* @customElement lion-fieldset
|
||||||
*/
|
*/
|
||||||
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/40110
|
|
||||||
export class LionFieldset extends FormGroupMixin(LitElement) {
|
export class LionFieldset extends FormGroupMixin(LitElement) {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
/** @override FormRegistrarMixin */
|
/** @override FormRegistrarMixin */
|
||||||
this._isFormOrFieldset = true;
|
this._isFormOrFieldset = true;
|
||||||
/** @override FormControlMixin */
|
/**
|
||||||
|
* @type {'child' | 'choice-group' | 'fieldset'}
|
||||||
|
* @override FormControlMixin
|
||||||
|
*/
|
||||||
this._repropagationRole = 'fieldset';
|
this._repropagationRole = 'fieldset';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import { Unparseable } from './validate/Unparseable.js';
|
||||||
* @typedef {import('@lion/core').CSSResult} CSSResult
|
* @typedef {import('@lion/core').CSSResult} CSSResult
|
||||||
* @typedef {import('@lion/core').nothing} nothing
|
* @typedef {import('@lion/core').nothing} nothing
|
||||||
* @typedef {import('@lion/core/types/SlotMixinTypes').SlotsMap} SlotsMap
|
* @typedef {import('@lion/core/types/SlotMixinTypes').SlotsMap} SlotsMap
|
||||||
* @typedef {import('../types/FormControlMixinTypes.js').FormControlHost} FormControlHost
|
|
||||||
* @typedef {import('../types/FormControlMixinTypes.js').FormControlMixin} FormControlMixin
|
* @typedef {import('../types/FormControlMixinTypes.js').FormControlMixin} FormControlMixin
|
||||||
* @typedef {import('../types/FormControlMixinTypes.js').ModelValueEventDetails} ModelValueEventDetails
|
* @typedef {import('../types/FormControlMixinTypes.js').ModelValueEventDetails} ModelValueEventDetails
|
||||||
*/
|
*/
|
||||||
|
|
@ -522,15 +521,13 @@ const FormControlMixinImplementation = superclass =>
|
||||||
/**
|
/**
|
||||||
* @param {?} modelValue
|
* @param {?} modelValue
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*
|
|
||||||
* FIXME: Move to FormatMixin? Since there we have access to modelValue prop
|
|
||||||
*/
|
*/
|
||||||
// @ts-expect-error
|
// @ts-ignore FIXME: Move to FormatMixin? Since there we have access to modelValue prop
|
||||||
_isEmpty(modelValue = this.modelValue) {
|
_isEmpty(modelValue = this.modelValue) {
|
||||||
let value = modelValue;
|
let value = modelValue;
|
||||||
// @ts-expect-error
|
// @ts-ignore
|
||||||
if (this.modelValue instanceof Unparseable) {
|
if (this.modelValue instanceof Unparseable) {
|
||||||
// @ts-expect-error
|
// @ts-ignore
|
||||||
value = this.modelValue.viewValue;
|
value = this.modelValue.viewValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -304,15 +304,16 @@ const FormatMixinImplementation = superclass =>
|
||||||
*/
|
*/
|
||||||
_onModelValueChanged(...args) {
|
_onModelValueChanged(...args) {
|
||||||
this._calculateValues({ source: 'model' });
|
this._calculateValues({ source: 'model' });
|
||||||
// @ts-expect-error only passing this so a subclasser can use it, but we do not use it ourselves
|
|
||||||
this._dispatchModelValueChangedEvent(...args);
|
this._dispatchModelValueChangedEvent(...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param {{ modelValue: unknown; }[]} args
|
||||||
* This is wrapped in a distinct method, so that parents can control when the changed event
|
* This is wrapped in a distinct method, so that parents can control when the changed event
|
||||||
* is fired. For objects, a deep comparison might be needed.
|
* is fired. For objects, a deep comparison might be needed.
|
||||||
*/
|
*/
|
||||||
_dispatchModelValueChangedEvent() {
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
_dispatchModelValueChangedEvent(...args) {
|
||||||
/** @event model-value-changed */
|
/** @event model-value-changed */
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent('model-value-changed', {
|
new CustomEvent('model-value-changed', {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import { InteractionStateMixin } from './InteractionStateMixin.js'; // applies F
|
||||||
*
|
*
|
||||||
* @customElement lion-field
|
* @customElement lion-field
|
||||||
*/
|
*/
|
||||||
// @ts-expect-error base constructors same return type
|
// @ts-expect-error lit properties false positive
|
||||||
export class LionField extends FormControlMixin(
|
export class LionField extends FormControlMixin(
|
||||||
InteractionStateMixin(FocusMixin(FormatMixin(ValidateMixin(SlotMixin(LitElement))))),
|
InteractionStateMixin(FocusMixin(FormatMixin(ValidateMixin(SlotMixin(LitElement))))),
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -244,12 +244,15 @@ const ChoiceInputMixinImplementation = superclass =>
|
||||||
* hasChanged is designed for async (updated) callback, also check for sync
|
* hasChanged is designed for async (updated) callback, also check for sync
|
||||||
* (requestUpdateInternal) callback
|
* (requestUpdateInternal) callback
|
||||||
* @param {{ modelValue:unknown }} newV
|
* @param {{ modelValue:unknown }} newV
|
||||||
* @param {{ modelValue:unknown }} [oldV]
|
* @param {{ modelValue:unknown }} [old]
|
||||||
*/
|
*/
|
||||||
// @ts-expect-error
|
_onModelValueChanged({ modelValue }, old) {
|
||||||
_onModelValueChanged({ modelValue }, { modelValue: old }) {
|
let _old;
|
||||||
// @ts-expect-error
|
if (old && old.modelValue) {
|
||||||
if (this.constructor._classProperties.get('modelValue').hasChanged(modelValue, old)) {
|
_old = old.modelValue;
|
||||||
|
}
|
||||||
|
// @ts-expect-error lit private property
|
||||||
|
if (this.constructor._classProperties.get('modelValue').hasChanged(modelValue, _old)) {
|
||||||
super._onModelValueChanged({ modelValue });
|
super._onModelValueChanged({ modelValue });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ const FormRegistrarMixinImplementation = superclass =>
|
||||||
console.info('Error Node:', child); // eslint-disable-line no-console
|
console.info('Error Node:', child); // eslint-disable-line no-console
|
||||||
throw new TypeError('You need to define a name');
|
throw new TypeError('You need to define a name');
|
||||||
}
|
}
|
||||||
// @ts-expect-error
|
// @ts-expect-error this._isFormOrFieldset true means we can assume `this.name` exists
|
||||||
if (name === this.name) {
|
if (name === this.name) {
|
||||||
console.info('Error Node:', child); // eslint-disable-line no-console
|
console.info('Error Node:', child); // eslint-disable-line no-console
|
||||||
throw new TypeError(`You can not have the same name "${name}" as your parent`);
|
throw new TypeError(`You can not have the same name "${name}" as your parent`);
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ const SyncUpdatableMixinImplementation = superclass =>
|
||||||
* @param {*} oldValue
|
* @param {*} oldValue
|
||||||
*/
|
*/
|
||||||
static __syncUpdatableHasChanged(name, newValue, oldValue) {
|
static __syncUpdatableHasChanged(name, newValue, oldValue) {
|
||||||
// @ts-expect-error FIXME: Typescript bug, superclass static method not availabe from static context
|
// @ts-expect-error accessing private lit property
|
||||||
const properties = this._classProperties;
|
const properties = this._classProperties;
|
||||||
if (properties.get(name) && properties.get(name).hasChanged) {
|
if (properties.get(name) && properties.get(name).hasChanged) {
|
||||||
return properties.get(name).hasChanged(newValue, oldValue);
|
return properties.get(name).hasChanged(newValue, oldValue);
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import { FormRegistrarPortalMixin } from '../src/registration/FormRegistrarPorta
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} customConfig
|
* @typedef {Object} customConfig
|
||||||
* @property {typeof HTMLElement | typeof import('@lion/core').UpdatingElement | typeof LitElement} [baseElement]
|
* @property {typeof LitElement} [baseElement]
|
||||||
* @property {string} [customConfig.suffix]
|
* @property {string} [customConfig.suffix]
|
||||||
* @property {string} [customConfig.parentTagString]
|
* @property {string} [customConfig.parentTagString]
|
||||||
* @property {string} [customConfig.childTagString]
|
* @property {string} [customConfig.childTagString]
|
||||||
|
|
@ -22,19 +22,16 @@ import { FormRegistrarPortalMixin } from '../src/registration/FormRegistrarPorta
|
||||||
*/
|
*/
|
||||||
export const runRegistrationSuite = customConfig => {
|
export const runRegistrationSuite = customConfig => {
|
||||||
const cfg = {
|
const cfg = {
|
||||||
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/38535
|
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/38535 fixed in later typescript version
|
||||||
baseElement: HTMLElement,
|
baseElement: LitElement,
|
||||||
...customConfig,
|
...customConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
describe(`FormRegistrationMixins ${cfg.suffix}`, () => {
|
describe(`FormRegistrationMixins ${cfg.suffix}`, () => {
|
||||||
// @ts-expect-error base constructors same return type & type cannot be assigned like this
|
|
||||||
class RegistrarClass extends FormRegistrarMixin(cfg.baseElement) {}
|
class RegistrarClass extends FormRegistrarMixin(cfg.baseElement) {}
|
||||||
cfg.parentTagString = defineCE(RegistrarClass);
|
cfg.parentTagString = defineCE(RegistrarClass);
|
||||||
// @ts-expect-error base constructors same return type & type cannot be assigned like this
|
|
||||||
class RegisteringClass extends FormRegisteringMixin(cfg.baseElement) {}
|
class RegisteringClass extends FormRegisteringMixin(cfg.baseElement) {}
|
||||||
cfg.childTagString = defineCE(RegisteringClass);
|
cfg.childTagString = defineCE(RegisteringClass);
|
||||||
// @ts-expect-error base constructors same return type & type cannot be assigned like this
|
|
||||||
class PortalClass extends FormRegistrarPortalMixin(cfg.baseElement) {}
|
class PortalClass extends FormRegistrarPortalMixin(cfg.baseElement) {}
|
||||||
cfg.portalTagString = defineCE(PortalClass);
|
cfg.portalTagString = defineCE(PortalClass);
|
||||||
|
|
||||||
|
|
@ -91,7 +88,6 @@ export const runRegistrationSuite = customConfig => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('works for components that have a delayed render', async () => {
|
it('works for components that have a delayed render', async () => {
|
||||||
// @ts-expect-error base constructors same return type
|
|
||||||
class PerformUpdate extends FormRegistrarMixin(LitElement) {
|
class PerformUpdate extends FormRegistrarMixin(LitElement) {
|
||||||
async performUpdate() {
|
async performUpdate() {
|
||||||
await new Promise(resolve => setTimeout(() => resolve(), 10));
|
await new Promise(resolve => setTimeout(() => resolve(), 10));
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import { Unparseable, Validator } from '../index.js';
|
||||||
* @typedef {ArrayConstructor | ObjectConstructor | NumberConstructor | BooleanConstructor | StringConstructor | DateConstructor | 'iban' | 'email'} modelValueType
|
* @typedef {ArrayConstructor | ObjectConstructor | NumberConstructor | BooleanConstructor | StringConstructor | DateConstructor | 'iban' | 'email'} modelValueType
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// @ts-expect-error base constructor same return type
|
|
||||||
class FormatClass extends FormatMixin(LitElement) {
|
class FormatClass extends FormatMixin(LitElement) {
|
||||||
get _inputNode() {
|
get _inputNode() {
|
||||||
return /** @type {HTMLInputElement} */ (super._inputNode); // casts type
|
return /** @type {HTMLInputElement} */ (super._inputNode); // casts type
|
||||||
|
|
@ -279,7 +278,6 @@ export function runFormatMixinSuite(customConfig) {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('works if there is no underlying _inputNode', async () => {
|
it('works if there is no underlying _inputNode', async () => {
|
||||||
// @ts-expect-error base constructor same return type
|
|
||||||
const tagNoInputString = defineCE(class extends FormatMixin(LitElement) {});
|
const tagNoInputString = defineCE(class extends FormatMixin(LitElement) {});
|
||||||
const tagNoInput = unsafeStatic(tagNoInputString);
|
const tagNoInput = unsafeStatic(tagNoInputString);
|
||||||
expect(async () => {
|
expect(async () => {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ export function runInteractionStateMixinSuite(customConfig) {
|
||||||
};
|
};
|
||||||
|
|
||||||
describe(`InteractionStateMixin`, async () => {
|
describe(`InteractionStateMixin`, async () => {
|
||||||
// @ts-expect-error base constructors same return type
|
|
||||||
class IState extends InteractionStateMixin(ValidateMixin(LitElement)) {
|
class IState extends InteractionStateMixin(ValidateMixin(LitElement)) {
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
|
|
@ -244,7 +243,6 @@ export function runInteractionStateMixinSuite(customConfig) {
|
||||||
|
|
||||||
describe('SubClassers', () => {
|
describe('SubClassers', () => {
|
||||||
it('can override the `_leaveEvent`', async () => {
|
it('can override the `_leaveEvent`', async () => {
|
||||||
// @ts-expect-error base constructor same return type
|
|
||||||
class IStateCustomBlur extends InteractionStateMixin(LitElement) {
|
class IStateCustomBlur extends InteractionStateMixin(LitElement) {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ export function runValidateMixinSuite(customConfig) {
|
||||||
|
|
||||||
const lightDom = cfg.lightDom || '';
|
const lightDom = cfg.lightDom || '';
|
||||||
|
|
||||||
// @ts-expect-error base constructor same return type
|
|
||||||
class ValidateElement extends ValidateMixin(LitElement) {
|
class ValidateElement extends ValidateMixin(LitElement) {
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
|
|
@ -562,7 +561,6 @@ export function runValidateMixinSuite(customConfig) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const withSuccessTagString = defineCE(
|
const withSuccessTagString = defineCE(
|
||||||
// @ts-expect-error
|
|
||||||
class extends ValidateMixin(LitElement) {
|
class extends ValidateMixin(LitElement) {
|
||||||
static get validationTypes() {
|
static get validationTypes() {
|
||||||
return [...super.validationTypes, 'success'];
|
return [...super.validationTypes, 'success'];
|
||||||
|
|
@ -677,7 +675,6 @@ export function runValidateMixinSuite(customConfig) {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls "._isEmpty" when provided (useful for different modelValues)', async () => {
|
it('calls "._isEmpty" when provided (useful for different modelValues)', async () => {
|
||||||
// @ts-expect-error base constructor same return type
|
|
||||||
class _isEmptyValidate extends ValidateMixin(LitElement) {
|
class _isEmptyValidate extends ValidateMixin(LitElement) {
|
||||||
_isEmpty() {
|
_isEmpty() {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
|
|
@ -729,7 +726,6 @@ export function runValidateMixinSuite(customConfig) {
|
||||||
|
|
||||||
describe('Default (preconfigured) Validators', () => {
|
describe('Default (preconfigured) Validators', () => {
|
||||||
const preconfTagString = defineCE(
|
const preconfTagString = defineCE(
|
||||||
// @ts-expect-error base constructor same return type
|
|
||||||
class extends ValidateMixin(LitElement) {
|
class extends ValidateMixin(LitElement) {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
@ -752,7 +748,6 @@ export function runValidateMixinSuite(customConfig) {
|
||||||
|
|
||||||
it('can be altered by App Developers', async () => {
|
it('can be altered by App Developers', async () => {
|
||||||
const altPreconfTagString = defineCE(
|
const altPreconfTagString = defineCE(
|
||||||
// @ts-expect-error base constructor same return type
|
|
||||||
class extends ValidateMixin(LitElement) {
|
class extends ValidateMixin(LitElement) {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
@ -918,7 +913,6 @@ export function runValidateMixinSuite(customConfig) {
|
||||||
|
|
||||||
describe('Extensibility: Custom Validator types', () => {
|
describe('Extensibility: Custom Validator types', () => {
|
||||||
const customTypeTagString = defineCE(
|
const customTypeTagString = defineCE(
|
||||||
// @ts-expect-error base constructor same return type
|
|
||||||
class extends ValidateMixin(LitElement) {
|
class extends ValidateMixin(LitElement) {
|
||||||
static get validationTypes() {
|
static get validationTypes() {
|
||||||
return [...super.validationTypes, 'x', 'y'];
|
return [...super.validationTypes, 'x', 'y'];
|
||||||
|
|
@ -1026,7 +1020,6 @@ export function runValidateMixinSuite(customConfig) {
|
||||||
describe('Subclassers', () => {
|
describe('Subclassers', () => {
|
||||||
describe('Adding new Validator types', () => {
|
describe('Adding new Validator types', () => {
|
||||||
it('can add helpers for validation types', async () => {
|
it('can add helpers for validation types', async () => {
|
||||||
// @ts-expect-error base constructor same return type
|
|
||||||
class ValidateHasX extends ValidateMixin(LitElement) {
|
class ValidateHasX extends ValidateMixin(LitElement) {
|
||||||
static get validationTypes() {
|
static get validationTypes() {
|
||||||
return [...super.validationTypes, 'x'];
|
return [...super.validationTypes, 'x'];
|
||||||
|
|
@ -1074,7 +1067,6 @@ export function runValidateMixinSuite(customConfig) {
|
||||||
.concat(array2.filter(x => !array1.includes(x)));
|
.concat(array2.filter(x => !array1.includes(x)));
|
||||||
}
|
}
|
||||||
const elTagString = defineCE(
|
const elTagString = defineCE(
|
||||||
// @ts-expect-error base constructor same return type
|
|
||||||
class extends ValidateMixin(LitElement) {
|
class extends ValidateMixin(LitElement) {
|
||||||
static get validationTypes() {
|
static get validationTypes() {
|
||||||
return [...super.validationTypes, 'x'];
|
return [...super.validationTypes, 'x'];
|
||||||
|
|
@ -1123,7 +1115,7 @@ export function runValidateMixinSuite(customConfig) {
|
||||||
// TODO: add this test on FormControl layer
|
// TODO: add this test on FormControl layer
|
||||||
it('reconsiders feedback visibility when interaction states changed', async () => {
|
it('reconsiders feedback visibility when interaction states changed', async () => {
|
||||||
const elTagString = defineCE(
|
const elTagString = defineCE(
|
||||||
// @ts-expect-error base constructor same return type
|
// @ts-expect-error lit properties false positive, they get merged with parent properties by lit automatically
|
||||||
class extends ValidateMixin(LitElement) {
|
class extends ValidateMixin(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -1168,7 +1160,7 @@ export function runValidateMixinSuite(customConfig) {
|
||||||
|
|
||||||
it('filters feedback visibility according interaction states', async () => {
|
it('filters feedback visibility according interaction states', async () => {
|
||||||
const elTagString = defineCE(
|
const elTagString = defineCE(
|
||||||
// @ts-expect-error base constructor same return type
|
// @ts-expect-error lit properties false positive, they get merged with parent properties by lit automatically
|
||||||
class extends ValidateMixin(LitElement) {
|
class extends ValidateMixin(LitElement) {
|
||||||
static get validationTypes() {
|
static get validationTypes() {
|
||||||
return ['error', 'info'];
|
return ['error', 'info'];
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ export function runValidateMixinFeedbackPart() {
|
||||||
localizeTearDown();
|
localizeTearDown();
|
||||||
});
|
});
|
||||||
|
|
||||||
// @ts-expect-error base constructor same return type
|
|
||||||
class ValidateElement extends ValidateMixin(LitElement) {
|
class ValidateElement extends ValidateMixin(LitElement) {
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
|
|
@ -80,7 +79,6 @@ export function runValidateMixinFeedbackPart() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reflects .showsFeedbackFor as attribute joined with "," to be used as a style hook', async () => {
|
it('reflects .showsFeedbackFor as attribute joined with "," to be used as a style hook', async () => {
|
||||||
// @ts-expect-error base constructors same return type
|
|
||||||
class ValidateElementCustomTypes extends ValidateMixin(LitElement) {
|
class ValidateElementCustomTypes extends ValidateMixin(LitElement) {
|
||||||
static get validationTypes() {
|
static get validationTypes() {
|
||||||
return [...super.validationTypes, 'x'];
|
return [...super.validationTypes, 'x'];
|
||||||
|
|
@ -307,7 +305,6 @@ export function runValidateMixinFeedbackPart() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows success message after fixing an error', async () => {
|
it('shows success message after fixing an error', async () => {
|
||||||
// @ts-expect-error base constructor same return type
|
|
||||||
class ValidateElementCustomTypes extends ValidateMixin(LitElement) {
|
class ValidateElementCustomTypes extends ValidateMixin(LitElement) {
|
||||||
static get validationTypes() {
|
static get validationTypes() {
|
||||||
return ['error', 'success'];
|
return ['error', 'success'];
|
||||||
|
|
@ -362,7 +359,6 @@ export function runValidateMixinFeedbackPart() {
|
||||||
|
|
||||||
describe('Meta data', () => {
|
describe('Meta data', () => {
|
||||||
it('".getMessage()" gets a reference to formControl, params, modelValue and type', async () => {
|
it('".getMessage()" gets a reference to formControl, params, modelValue and type', async () => {
|
||||||
// @ts-expect-error base constructor same return type
|
|
||||||
class ValidateElementCustomTypes extends ValidateMixin(LitElement) {
|
class ValidateElementCustomTypes extends ValidateMixin(LitElement) {
|
||||||
static get validationTypes() {
|
static get validationTypes() {
|
||||||
return ['error', 'x'];
|
return ['error', 'x'];
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import { ChoiceInputMixin } from '../../src/choice-group/ChoiceInputMixin.js';
|
||||||
|
|
||||||
class ChoiceInput extends ChoiceInputMixin(LionInput) {}
|
class ChoiceInput extends ChoiceInputMixin(LionInput) {}
|
||||||
customElements.define('choice-group-input', ChoiceInput);
|
customElements.define('choice-group-input', ChoiceInput);
|
||||||
// @ts-expect-error
|
|
||||||
class ChoiceGroup extends ChoiceGroupMixin(FormGroupMixin(LitElement)) {}
|
class ChoiceGroup extends ChoiceGroupMixin(FormGroupMixin(LitElement)) {}
|
||||||
customElements.define('choice-group', ChoiceGroup);
|
customElements.define('choice-group', ChoiceGroup);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,14 @@ export function runFormGroupMixinInputSuite(cfg = {}) {
|
||||||
|
|
||||||
const childTagString = cfg.childTagString || defineCE(FormChild);
|
const childTagString = cfg.childTagString || defineCE(FormChild);
|
||||||
|
|
||||||
// @ts-expect-error
|
|
||||||
const FormGroup = class extends FormGroupMixin(LitElement) {
|
const FormGroup = class extends FormGroupMixin(LitElement) {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
/** @override from FormRegistrarMixin */
|
/** @override from FormRegistrarMixin */
|
||||||
this._isFormOrFieldset = true;
|
this._isFormOrFieldset = true;
|
||||||
|
/**
|
||||||
|
* @type {'fieldset' | 'child' | 'choice-group'}
|
||||||
|
*/
|
||||||
this._repropagationRole = 'fieldset'; // configures FormControlMixin
|
this._repropagationRole = 'fieldset'; // configures FormControlMixin
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ export function runFormGroupMixinSuite(cfg = {}) {
|
||||||
|
|
||||||
const childTagString = cfg.childTagString || defineCE(FormChild);
|
const childTagString = cfg.childTagString || defineCE(FormChild);
|
||||||
|
|
||||||
// @ts-expect-error base constructors same return type
|
|
||||||
class FormGroup extends FormGroupMixin(LitElement) {
|
class FormGroup extends FormGroupMixin(LitElement) {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
@ -131,8 +130,9 @@ export function runFormGroupMixinSuite(cfg = {}) {
|
||||||
try {
|
try {
|
||||||
// we test the api directly as errors thrown from a web component are in a
|
// we test the api directly as errors thrown from a web component are in a
|
||||||
// different context and we can not catch them here => register fake elements
|
// different context and we can not catch them here => register fake elements
|
||||||
// @ts-expect-error
|
el.addFormElement(
|
||||||
el.addFormElement(/** @type {FormControlHost} */ ({}));
|
/** @type {HTMLElement & import('../../types/FormControlMixinTypes').FormControlHost} */ ({}),
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error = err;
|
error = err;
|
||||||
}
|
}
|
||||||
|
|
@ -151,8 +151,11 @@ export function runFormGroupMixinSuite(cfg = {}) {
|
||||||
try {
|
try {
|
||||||
// we test the api directly as errors thrown from a web component are in a
|
// we test the api directly as errors thrown from a web component are in a
|
||||||
// different context and we can not catch them here => register fake elements
|
// different context and we can not catch them here => register fake elements
|
||||||
// @ts-expect-error
|
el.addFormElement(
|
||||||
el.addFormElement(/** @type {FormControlHost} */ ({ name: 'foo' }));
|
/** @type {HTMLElement & import('../../types/FormControlMixinTypes').FormControlHost} */ ({
|
||||||
|
name: 'foo',
|
||||||
|
}),
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error = err;
|
error = err;
|
||||||
}
|
}
|
||||||
|
|
@ -171,10 +174,16 @@ export function runFormGroupMixinSuite(cfg = {}) {
|
||||||
try {
|
try {
|
||||||
// we test the api directly as errors thrown from a web component are in a
|
// we test the api directly as errors thrown from a web component are in a
|
||||||
// different context and we can not catch them here => register fake elements
|
// different context and we can not catch them here => register fake elements
|
||||||
// @ts-expect-error
|
el.addFormElement(
|
||||||
el.addFormElement(/** @type {FormControlHost} */ ({ name: 'fooBar' }));
|
/** @type {HTMLElement & import('../../types/FormControlMixinTypes').FormControlHost} */ ({
|
||||||
// @ts-expect-error
|
name: 'fooBar',
|
||||||
el.addFormElement(/** @type {FormControlHost} */ ({ name: 'fooBar' }));
|
}),
|
||||||
|
);
|
||||||
|
el.addFormElement(
|
||||||
|
/** @type {HTMLElement & import('../../types/FormControlMixinTypes').FormControlHost} */ ({
|
||||||
|
name: 'fooBar',
|
||||||
|
}),
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error = err;
|
error = err;
|
||||||
}
|
}
|
||||||
|
|
@ -628,7 +637,7 @@ export function runFormGroupMixinSuite(cfg = {}) {
|
||||||
return 'Input1IsTen';
|
return 'Input1IsTen';
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-expect-error
|
/** @param {?} value */
|
||||||
execute(value) {
|
execute(value) {
|
||||||
const hasError = value.input1 !== 10;
|
const hasError = value.input1 !== 10;
|
||||||
return hasError;
|
return hasError;
|
||||||
|
|
@ -643,10 +652,8 @@ export function runFormGroupMixinSuite(cfg = {}) {
|
||||||
<${childTag} name="input2" .validators=${[new IsNumber()]}></${childTag}>
|
<${childTag} name="input2" .validators=${[new IsNumber()]}></${childTag}>
|
||||||
</${tag}>
|
</${tag}>
|
||||||
`));
|
`));
|
||||||
const inputs = el.querySelectorAll(childTagString);
|
const inputs = /** @type {FormChild[]} */ (Array.from(el.querySelectorAll(childTagString)));
|
||||||
// @ts-expect-error
|
|
||||||
inputs[1].modelValue = 2; // make it dirty
|
inputs[1].modelValue = 2; // make it dirty
|
||||||
// @ts-expect-error
|
|
||||||
inputs[1].focus();
|
inputs[1].focus();
|
||||||
|
|
||||||
outSideButton.focus();
|
outSideButton.focus();
|
||||||
|
|
@ -662,8 +669,8 @@ export function runFormGroupMixinSuite(cfg = {}) {
|
||||||
const fieldset = /** @type {FormGroup} */ (await fixture(
|
const fieldset = /** @type {FormGroup} */ (await fixture(
|
||||||
html`<${tag}>${inputSlots}</${tag}>`,
|
html`<${tag}>${inputSlots}</${tag}>`,
|
||||||
));
|
));
|
||||||
// @ts-expect-error
|
fieldset.formElements['hobbies[]'][0].serializer = /** @param {?} v */ v =>
|
||||||
fieldset.formElements['hobbies[]'][0].serializer = v => `${v.value}-serialized`;
|
`${v.value}-serialized`;
|
||||||
fieldset.formElements['hobbies[]'][0].modelValue = { checked: false, value: 'Bar' };
|
fieldset.formElements['hobbies[]'][0].modelValue = { checked: false, value: 'Bar' };
|
||||||
fieldset.formElements['hobbies[]'][1].modelValue = { checked: false, value: 'rugby' };
|
fieldset.formElements['hobbies[]'][1].modelValue = { checked: false, value: 'rugby' };
|
||||||
fieldset.formElements['gender[]'][0].modelValue = { checked: false, value: 'male' };
|
fieldset.formElements['gender[]'][0].modelValue = { checked: false, value: 'male' };
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import { defineCE, expect, fixture, html, oneEvent, unsafeStatic } from '@open-w
|
||||||
import { FocusMixin } from '../src/FocusMixin.js';
|
import { FocusMixin } from '../src/FocusMixin.js';
|
||||||
|
|
||||||
describe('FocusMixin', () => {
|
describe('FocusMixin', () => {
|
||||||
// @ts-expect-error base constructors same return type
|
|
||||||
class Focusable extends FocusMixin(LitElement) {
|
class Focusable extends FocusMixin(LitElement) {
|
||||||
render() {
|
render() {
|
||||||
return html`<slot name="input"></slot>`;
|
return html`<slot name="input"></slot>`;
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import { FormRegistrarMixin } from '../src/registration/FormRegistrarMixin.js';
|
||||||
describe('FormControlMixin', () => {
|
describe('FormControlMixin', () => {
|
||||||
const inputSlot = '<input slot="input" />';
|
const inputSlot = '<input slot="input" />';
|
||||||
|
|
||||||
// @ts-expect-error base constructor same return type
|
|
||||||
class FormControlMixinClass extends FormControlMixin(LitElement) {}
|
class FormControlMixinClass extends FormControlMixin(LitElement) {}
|
||||||
|
|
||||||
const tagString = defineCE(FormControlMixinClass);
|
const tagString = defineCE(FormControlMixinClass);
|
||||||
|
|
@ -213,7 +212,6 @@ describe('FormControlMixin', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Model-value-changed event propagation', () => {
|
describe('Model-value-changed event propagation', () => {
|
||||||
// @ts-expect-error base constructor same return type
|
|
||||||
const FormControlWithRegistrarMixinClass = class extends FormControlMixin(
|
const FormControlWithRegistrarMixinClass = class extends FormControlMixin(
|
||||||
FormRegistrarMixin(LitElement),
|
FormRegistrarMixin(LitElement),
|
||||||
) {};
|
) {};
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { LitElement, UpdatingElement } from '@lion/core';
|
import { LitElement } from '@lion/core';
|
||||||
import { html } from '@open-wc/testing';
|
import { html } from '@open-wc/testing';
|
||||||
import { runRegistrationSuite } from '../test-suites/FormRegistrationMixins.suite.js';
|
import { runRegistrationSuite } from '../test-suites/FormRegistrationMixins.suite.js';
|
||||||
|
|
||||||
runRegistrationSuite({
|
runRegistrationSuite({
|
||||||
suffix: 'with UpdatingElement',
|
suffix: 'with LitElement',
|
||||||
baseElement: UpdatingElement,
|
baseElement: LitElement,
|
||||||
});
|
});
|
||||||
|
|
||||||
runRegistrationSuite({
|
runRegistrationSuite({
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ describe('SyncUpdatableMixin', () => {
|
||||||
it('initializes all properties', async () => {
|
it('initializes all properties', async () => {
|
||||||
let hasCalledFirstUpdated = false;
|
let hasCalledFirstUpdated = false;
|
||||||
let hasCalledUpdateSync = false;
|
let hasCalledUpdateSync = false;
|
||||||
// @ts-expect-error base constructors same return type
|
|
||||||
class UpdatableImplementation extends SyncUpdatableMixin(LitElement) {
|
class UpdatableImplementation extends SyncUpdatableMixin(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -65,7 +64,6 @@ describe('SyncUpdatableMixin', () => {
|
||||||
it('guarantees Member Order Independence', async () => {
|
it('guarantees Member Order Independence', async () => {
|
||||||
let hasCalledRunPropertyEffect = false;
|
let hasCalledRunPropertyEffect = false;
|
||||||
|
|
||||||
// @ts-expect-error base constructors same return type
|
|
||||||
class UpdatableImplementation extends SyncUpdatableMixin(LitElement) {
|
class UpdatableImplementation extends SyncUpdatableMixin(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -136,7 +134,6 @@ describe('SyncUpdatableMixin', () => {
|
||||||
let propChangedCount = 0;
|
let propChangedCount = 0;
|
||||||
let propUpdateSyncCount = 0;
|
let propUpdateSyncCount = 0;
|
||||||
|
|
||||||
// @ts-expect-error the private override is on purpose
|
|
||||||
class UpdatableImplementation extends SyncUpdatableMixin(LitElement) {
|
class UpdatableImplementation extends SyncUpdatableMixin(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -189,7 +186,6 @@ describe('SyncUpdatableMixin', () => {
|
||||||
|
|
||||||
describe('After firstUpdated', () => {
|
describe('After firstUpdated', () => {
|
||||||
it('calls "updateSync" immediately when the observed property is changed (newValue !== oldValue)', async () => {
|
it('calls "updateSync" immediately when the observed property is changed (newValue !== oldValue)', async () => {
|
||||||
// @ts-expect-error
|
|
||||||
class UpdatableImplementation extends SyncUpdatableMixin(LitElement) {
|
class UpdatableImplementation extends SyncUpdatableMixin(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -245,7 +241,6 @@ describe('SyncUpdatableMixin', () => {
|
||||||
describe('Features', () => {
|
describe('Features', () => {
|
||||||
// See: https://lit-element.polymer-project.org/guide/lifecycle#haschanged
|
// See: https://lit-element.polymer-project.org/guide/lifecycle#haschanged
|
||||||
it('supports "hasChanged" from UpdatingElement', async () => {
|
it('supports "hasChanged" from UpdatingElement', async () => {
|
||||||
// @ts-expect-error base constructors same return type
|
|
||||||
class UpdatableImplementation extends SyncUpdatableMixin(LitElement) {
|
class UpdatableImplementation extends SyncUpdatableMixin(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ describe('Validator', () => {
|
||||||
|
|
||||||
it('has access to FormControl', async () => {
|
it('has access to FormControl', async () => {
|
||||||
const lightDom = '';
|
const lightDom = '';
|
||||||
// @ts-expect-error base constructors same return type
|
// @ts-expect-error false positive for incompatible static get properties. Lit-element merges super properties already for you.
|
||||||
class ValidateElement extends ValidateMixin(LitElement) {
|
class ValidateElement extends ValidateMixin(LitElement) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return { modelValue: String };
|
return { modelValue: String };
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { LitElement } from '@lion/core';
|
||||||
import { FormControlHost } from './FormControlMixinTypes';
|
import { FormControlHost } from './FormControlMixinTypes';
|
||||||
|
|
||||||
export declare class FocusHost {
|
export declare class FocusHost {
|
||||||
|
constructor(...args: any[]);
|
||||||
focused: boolean;
|
focused: boolean;
|
||||||
|
|
||||||
connectedCallback(): void;
|
connectedCallback(): void;
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ declare interface HTMLElementWithValue extends HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare class FormControlHost {
|
export declare class FormControlHost {
|
||||||
|
constructor(...args: any[]);
|
||||||
static get styles(): CSSResultArray;
|
static get styles(): CSSResultArray;
|
||||||
static get properties(): {
|
static get properties(): {
|
||||||
name: {
|
name: {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { ValidateHost } from './validate/ValidateMixinTypes';
|
||||||
import { FormControlHost } from './FormControlMixinTypes';
|
import { FormControlHost } from './FormControlMixinTypes';
|
||||||
|
|
||||||
export declare class FormatHost {
|
export declare class FormatHost {
|
||||||
|
constructor(...args: any[]);
|
||||||
formattedValue: string;
|
formattedValue: string;
|
||||||
serializedValue: string;
|
serializedValue: string;
|
||||||
formatOn: string;
|
formatOn: string;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { LitElement } from '@lion/core';
|
||||||
import { FormControlHost } from './FormControlMixinTypes';
|
import { FormControlHost } from './FormControlMixinTypes';
|
||||||
|
|
||||||
export declare class InteractionStateHost {
|
export declare class InteractionStateHost {
|
||||||
|
constructor(...args: any[]);
|
||||||
prefilled: boolean;
|
prefilled: boolean;
|
||||||
filled: boolean;
|
filled: boolean;
|
||||||
touched: boolean;
|
touched: boolean;
|
||||||
|
|
@ -26,7 +27,7 @@ export declare function InteractionStateImplementation<T extends Constructor<Lit
|
||||||
superclass: T,
|
superclass: T,
|
||||||
): T &
|
): T &
|
||||||
Constructor<InteractionStateHost> &
|
Constructor<InteractionStateHost> &
|
||||||
InteractionStateHost &
|
typeof InteractionStateHost &
|
||||||
Constructor<FormControlHost> &
|
Constructor<FormControlHost> &
|
||||||
typeof FormControlHost;
|
typeof FormControlHost;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ export declare class NativeTextField extends LionField {
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare class NativeTextFieldHost {
|
export declare class NativeTextFieldHost {
|
||||||
|
constructor(...args: any[]);
|
||||||
get selectionStart(): number;
|
get selectionStart(): number;
|
||||||
set selectionStart(value: number);
|
set selectionStart(value: number);
|
||||||
get selectionEnd(): number;
|
get selectionEnd(): number;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { FormRegistrarHost } from '../registration/FormRegistrarMixinTypes';
|
||||||
import { InteractionStateHost } from '../InteractionStateMixinTypes';
|
import { InteractionStateHost } from '../InteractionStateMixinTypes';
|
||||||
|
|
||||||
export declare class ChoiceGroupHost {
|
export declare class ChoiceGroupHost {
|
||||||
|
constructor(...args: any[]);
|
||||||
multipleChoice: boolean;
|
multipleChoice: boolean;
|
||||||
|
|
||||||
connectedCallback(): void;
|
connectedCallback(): void;
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ export interface ChoiceInputSerializedValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare class ChoiceInputHost {
|
export declare class ChoiceInputHost {
|
||||||
|
constructor(...args: any[]);
|
||||||
modelValue: ChoiceInputModelValue;
|
modelValue: ChoiceInputModelValue;
|
||||||
serializedValue: ChoiceInputSerializedValue;
|
serializedValue: ChoiceInputSerializedValue;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import { FormRegistrarHost } from '../registration/FormRegistrarMixinTypes';
|
||||||
import { ValidateHost } from '../validate/ValidateMixinTypes';
|
import { ValidateHost } from '../validate/ValidateMixinTypes';
|
||||||
|
|
||||||
export declare class FormGroupHost {
|
export declare class FormGroupHost {
|
||||||
|
constructor(...args: any[]);
|
||||||
protected static _addDescriptionElementIdsToField(): void;
|
protected static _addDescriptionElementIdsToField(): void;
|
||||||
_inputNode: HTMLElement;
|
_inputNode: HTMLElement;
|
||||||
submitGroup(): void;
|
submitGroup(): void;
|
||||||
|
|
@ -29,7 +30,7 @@ export declare function FormGroupImplementation<T extends Constructor<LitElement
|
||||||
superclass: T,
|
superclass: T,
|
||||||
): T &
|
): T &
|
||||||
Constructor<FormGroupHost> &
|
Constructor<FormGroupHost> &
|
||||||
FormGroupHost &
|
typeof FormGroupHost &
|
||||||
Constructor<FormRegistrarHost> &
|
Constructor<FormRegistrarHost> &
|
||||||
typeof FormRegistrarHost &
|
typeof FormRegistrarHost &
|
||||||
Constructor<FormControlHost> &
|
Constructor<FormControlHost> &
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { FormRegistrarHost } from './FormRegistrarMixinTypes';
|
||||||
import { LitElement } from '@lion/core';
|
import { LitElement } from '@lion/core';
|
||||||
|
|
||||||
export declare class FormRegisteringHost {
|
export declare class FormRegisteringHost {
|
||||||
|
constructor(...args: any[]);
|
||||||
connectedCallback(): void;
|
connectedCallback(): void;
|
||||||
disconnectedCallback(): void;
|
disconnectedCallback(): void;
|
||||||
__parentFormGroup?: FormRegistrarHost;
|
__parentFormGroup?: FormRegistrarHost;
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ export declare class ElementWithParentFormGroup {
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare class FormRegistrarHost {
|
export declare class FormRegistrarHost {
|
||||||
|
constructor(...args: any[]);
|
||||||
_isFormOrFieldset: boolean;
|
_isFormOrFieldset: boolean;
|
||||||
formElements: FormControlsCollection & { [x: string]: any };
|
formElements: FormControlsCollection & { [x: string]: any };
|
||||||
addFormElement(
|
addFormElement(
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { Constructor } from '@open-wc/dedupe-mixin';
|
||||||
import { LitElement } from '@lion/core';
|
import { LitElement } from '@lion/core';
|
||||||
|
|
||||||
export declare class FormRegistrarPortalHost {
|
export declare class FormRegistrarPortalHost {
|
||||||
|
constructor(...args: any[]);
|
||||||
registrationTarget: HTMLElement;
|
registrationTarget: HTMLElement;
|
||||||
__redispatchEventForFormRegistrarPortalMixin(ev: CustomEvent): void;
|
__redispatchEventForFormRegistrarPortalMixin(ev: CustomEvent): void;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ export declare interface SyncUpdatableNamespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare class SyncUpdatableHost {
|
export declare class SyncUpdatableHost {
|
||||||
|
constructor(...args: any[]);
|
||||||
static __syncUpdatableHasChanged(name: string, newValue: any, oldValue: any): boolean;
|
static __syncUpdatableHasChanged(name: string, newValue: any, oldValue: any): boolean;
|
||||||
updateSync(name: string, oldValue: any): void;
|
updateSync(name: string, oldValue: any): void;
|
||||||
__syncUpdatableInitialize(): void;
|
__syncUpdatableInitialize(): void;
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ type FeedbackMessage = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare class ValidateHost {
|
export declare class ValidateHost {
|
||||||
|
constructor(...args: any[]);
|
||||||
validators: Validator[];
|
validators: Validator[];
|
||||||
hasFeedbackFor: string[];
|
hasFeedbackFor: string[];
|
||||||
shouldShowFeedbackFor: string[];
|
shouldShowFeedbackFor: string[];
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
import { expect, fixture, html } from '@open-wc/testing';
|
import { expect, fixture, html } from '@open-wc/testing';
|
||||||
import './helpers/umbrella-form.js';
|
import './helpers/umbrella-form.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('./helpers/umbrella-form.js').UmbrellaForm} UmbrellaForm
|
||||||
|
*/
|
||||||
|
|
||||||
// Test umbrella form.
|
// Test umbrella form.
|
||||||
describe('Form Integrations', () => {
|
describe('Form Integrations', () => {
|
||||||
it('".serializedValue" returns all non disabled fields based on form structure', async () => {
|
it('".serializedValue" returns all non disabled fields based on form structure', async () => {
|
||||||
const el = await fixture(html`<umbrella-form></umbrella-form>`);
|
const el = /** @type {UmbrellaForm} */ (await fixture(html`<umbrella-form></umbrella-form>`));
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
const formEl = el._lionFormNode;
|
const formEl = el._lionFormNode;
|
||||||
expect(formEl.serializedValue).to.eql({
|
expect(formEl.serializedValue).to.eql({
|
||||||
|
|
@ -29,7 +33,7 @@ describe('Form Integrations', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('".formattedValue" returns all non disabled fields based on form structure', async () => {
|
it('".formattedValue" returns all non disabled fields based on form structure', async () => {
|
||||||
const el = await fixture(html`<umbrella-form></umbrella-form>`);
|
const el = /** @type {UmbrellaForm} */ (await fixture(html`<umbrella-form></umbrella-form>`));
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
const formEl = el._lionFormNode;
|
const formEl = el._lionFormNode;
|
||||||
expect(formEl.formattedValue).to.eql({
|
expect(formEl.formattedValue).to.eql({
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import '@lion/textarea/lion-textarea';
|
||||||
import { elementUpdated, expect, fixture, html } from '@open-wc/testing';
|
import { elementUpdated, expect, fixture, html } from '@open-wc/testing';
|
||||||
|
|
||||||
describe(`Submitting/Resetting Form`, async () => {
|
describe(`Submitting/Resetting Form`, async () => {
|
||||||
|
/** @type {import('@lion/form').LionForm} */
|
||||||
let el;
|
let el;
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
el = await fixture(html`
|
el = await fixture(html`
|
||||||
|
|
@ -121,7 +122,7 @@ describe(`Submitting/Resetting Form`, async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Submitting a form should make submitted true for all fields', async () => {
|
it('Submitting a form should make submitted true for all fields', async () => {
|
||||||
el.querySelector('#submit_button').click();
|
/** @type {import('@lion/button').LionButton} */ (el.querySelector('#submit_button')).click();
|
||||||
await elementUpdated(el);
|
await elementUpdated(el);
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
el.formElements.forEach(field => {
|
el.formElements.forEach(field => {
|
||||||
|
|
@ -130,8 +131,8 @@ describe(`Submitting/Resetting Form`, async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Resetting a form should reset metadata of all fields', async () => {
|
it('Resetting a form should reset metadata of all fields', async () => {
|
||||||
el.querySelector('#submit_button').click();
|
/** @type {import('@lion/button').LionButton} */ (el.querySelector('#submit_button')).click();
|
||||||
el.querySelector('#reset_button').click();
|
/** @type {import('@lion/button').LionButton} */ (el.querySelector('#reset_button')).click();
|
||||||
await elementUpdated(el);
|
await elementUpdated(el);
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
expect(el.submitted).to.be.false;
|
expect(el.submitted).to.be.false;
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,9 @@ import '@lion/button/lion-button.js';
|
||||||
|
|
||||||
export class UmbrellaForm extends LitElement {
|
export class UmbrellaForm extends LitElement {
|
||||||
get _lionFormNode() {
|
get _lionFormNode() {
|
||||||
return this.shadowRoot.querySelector('lion-form');
|
return /** @type {import('@lion/form').LionForm} */ (this.shadowRoot?.querySelector(
|
||||||
|
'lion-form',
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
@ -113,8 +115,10 @@ export class UmbrellaForm extends LitElement {
|
||||||
<lion-button
|
<lion-button
|
||||||
type="button"
|
type="button"
|
||||||
raised
|
raised
|
||||||
@click=${ev =>
|
@click=${() => {
|
||||||
ev.currentTarget.parentElement.parentElement.parentElement.resetGroup()}
|
const lionForm = this._lionFormNode;
|
||||||
|
lionForm.resetGroup();
|
||||||
|
}}
|
||||||
>Reset</lion-button
|
>Reset</lion-button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -29,14 +29,29 @@ import '@lion/fieldset/lion-fieldset.js';
|
||||||
import '@lion/form/lion-form.js';
|
import '@lion/form/lion-form.js';
|
||||||
import '@lion/form-core/lion-field.js';
|
import '@lion/form-core/lion-field.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('@lion/core').LitElement} LitElement
|
||||||
|
* @typedef {import('@lion/form-core').LionField} LionField
|
||||||
|
* @typedef {import('@lion/form-core/types/FormControlMixinTypes').FormControlHost & HTMLElement & {__parentFormGroup?: HTMLElement, checked?: boolean, disabled: boolean, hasFeedbackFor: string[], makeRequestToBeDisabled: Function }} FormControl
|
||||||
|
* @typedef {import('@lion/input').LionInput} LionInput
|
||||||
|
* @typedef {import('@lion/select').LionSelect} LionSelect
|
||||||
|
* @typedef {import('@lion/listbox').LionOption} LionOption
|
||||||
|
*/
|
||||||
|
|
||||||
const featureName = 'model value';
|
const featureName = 'model value';
|
||||||
|
|
||||||
const getFirstPaintTitle = count => `should dispatch ${count} time(s) on first paint`;
|
const getFirstPaintTitle = /** @param {number} count */ count =>
|
||||||
const getInteractionTitle = count => `should dispatch ${count} time(s) on interaction`;
|
`should dispatch ${count} time(s) on first paint`;
|
||||||
|
const getInteractionTitle = /** @param {number} count */ count =>
|
||||||
|
`should dispatch ${count} time(s) on interaction`;
|
||||||
|
|
||||||
const firstStampCount = 1;
|
const firstStampCount = 1;
|
||||||
const interactionCount = 1;
|
const interactionCount = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} tagname
|
||||||
|
* @param {number} count
|
||||||
|
*/
|
||||||
const fieldDispatchesCountOnFirstPaint = (tagname, count) => {
|
const fieldDispatchesCountOnFirstPaint = (tagname, count) => {
|
||||||
const tag = unsafeStatic(tagname);
|
const tag = unsafeStatic(tagname);
|
||||||
const spy = sinon.spy();
|
const spy = sinon.spy();
|
||||||
|
|
@ -46,11 +61,15 @@ const fieldDispatchesCountOnFirstPaint = (tagname, count) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} tagname
|
||||||
|
* @param {number} count
|
||||||
|
*/
|
||||||
const fieldDispatchesCountOnInteraction = (tagname, count) => {
|
const fieldDispatchesCountOnInteraction = (tagname, count) => {
|
||||||
const tag = unsafeStatic(tagname);
|
const tag = unsafeStatic(tagname);
|
||||||
const spy = sinon.spy();
|
const spy = sinon.spy();
|
||||||
it(getInteractionTitle(count), async () => {
|
it(getInteractionTitle(count), async () => {
|
||||||
const el = await fixture(html`<${tag}></${tag}>`);
|
const el = /** @type {LionField} */ (await fixture(html`<${tag}></${tag}>`));
|
||||||
el.addEventListener('model-value-changed', spy);
|
el.addEventListener('model-value-changed', spy);
|
||||||
// TODO: discuss if this is the "correct" way to interact with component
|
// TODO: discuss if this is the "correct" way to interact with component
|
||||||
el.modelValue = 'foo';
|
el.modelValue = 'foo';
|
||||||
|
|
@ -59,6 +78,10 @@ const fieldDispatchesCountOnInteraction = (tagname, count) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} tagname
|
||||||
|
* @param {number} count
|
||||||
|
*/
|
||||||
const choiceDispatchesCountOnFirstPaint = (tagname, count) => {
|
const choiceDispatchesCountOnFirstPaint = (tagname, count) => {
|
||||||
const tag = unsafeStatic(tagname);
|
const tag = unsafeStatic(tagname);
|
||||||
const spy = sinon.spy();
|
const spy = sinon.spy();
|
||||||
|
|
@ -68,17 +91,28 @@ const choiceDispatchesCountOnFirstPaint = (tagname, count) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} tagname
|
||||||
|
* @param {number} count
|
||||||
|
*/
|
||||||
const choiceDispatchesCountOnInteraction = (tagname, count) => {
|
const choiceDispatchesCountOnInteraction = (tagname, count) => {
|
||||||
const tag = unsafeStatic(tagname);
|
const tag = unsafeStatic(tagname);
|
||||||
const spy = sinon.spy();
|
const spy = sinon.spy();
|
||||||
it(getInteractionTitle(count), async () => {
|
it(getInteractionTitle(count), async () => {
|
||||||
const el = await fixture(html`<${tag} .choiceValue="${'option'}"></${tag}>`);
|
const el = /** @type {HTMLElement & {checked: boolean}} */ (await fixture(
|
||||||
|
html`<${tag} .choiceValue="${'option'}"></${tag}>`,
|
||||||
|
));
|
||||||
el.addEventListener('model-value-changed', spy);
|
el.addEventListener('model-value-changed', spy);
|
||||||
el.checked = true;
|
el.checked = true;
|
||||||
expect(spy.callCount).to.equal(count);
|
expect(spy.callCount).to.equal(count);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} groupTagname
|
||||||
|
* @param {string} itemTagname
|
||||||
|
* @param {number} count
|
||||||
|
*/
|
||||||
const choiceGroupDispatchesCountOnFirstPaint = (groupTagname, itemTagname, count) => {
|
const choiceGroupDispatchesCountOnFirstPaint = (groupTagname, itemTagname, count) => {
|
||||||
const groupTag = unsafeStatic(groupTagname);
|
const groupTag = unsafeStatic(groupTagname);
|
||||||
const itemTag = unsafeStatic(itemTagname);
|
const itemTag = unsafeStatic(itemTagname);
|
||||||
|
|
@ -96,6 +130,11 @@ const choiceGroupDispatchesCountOnFirstPaint = (groupTagname, itemTagname, count
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} groupTagname
|
||||||
|
* @param {string} itemTagname
|
||||||
|
* @param {number} count
|
||||||
|
*/
|
||||||
const choiceGroupDispatchesCountOnInteraction = (groupTagname, itemTagname, count) => {
|
const choiceGroupDispatchesCountOnInteraction = (groupTagname, itemTagname, count) => {
|
||||||
const groupTag = unsafeStatic(groupTagname);
|
const groupTag = unsafeStatic(groupTagname);
|
||||||
const itemTag = unsafeStatic(itemTagname);
|
const itemTag = unsafeStatic(itemTagname);
|
||||||
|
|
@ -110,13 +149,17 @@ const choiceGroupDispatchesCountOnInteraction = (groupTagname, itemTagname, coun
|
||||||
`);
|
`);
|
||||||
|
|
||||||
el.addEventListener('model-value-changed', spy);
|
el.addEventListener('model-value-changed', spy);
|
||||||
const option2 = el.querySelector(`${itemTagname}:nth-child(2)`);
|
const option2 = /** @type {HTMLElement & {checked: boolean}} */ (el.querySelector(
|
||||||
|
`${itemTagname}:nth-child(2)`,
|
||||||
|
));
|
||||||
option2.checked = true;
|
option2.checked = true;
|
||||||
expect(spy.callCount).to.equal(count);
|
expect(spy.callCount).to.equal(count);
|
||||||
|
|
||||||
spy.resetHistory();
|
spy.resetHistory();
|
||||||
|
|
||||||
const option3 = el.querySelector(`${itemTagname}:nth-child(3)`);
|
const option3 = /** @type {HTMLElement & {checked: boolean}} */ (el.querySelector(
|
||||||
|
`${itemTagname}:nth-child(3)`,
|
||||||
|
));
|
||||||
option3.checked = true;
|
option3.checked = true;
|
||||||
expect(spy.callCount).to.equal(count);
|
expect(spy.callCount).to.equal(count);
|
||||||
});
|
});
|
||||||
|
|
@ -178,7 +221,7 @@ describe('lion-select', () => {
|
||||||
|
|
||||||
it(getInteractionTitle(interactionCount), async () => {
|
it(getInteractionTitle(interactionCount), async () => {
|
||||||
const spy = sinon.spy();
|
const spy = sinon.spy();
|
||||||
const el = await fixture(html`
|
const el = /** @type {LionSelect} */ (await fixture(html`
|
||||||
<lion-select>
|
<lion-select>
|
||||||
<select slot="input">
|
<select slot="input">
|
||||||
<option value="option1"></option>
|
<option value="option1"></option>
|
||||||
|
|
@ -186,9 +229,9 @@ describe('lion-select', () => {
|
||||||
<option value="option3"></option>
|
<option value="option3"></option>
|
||||||
</select>
|
</select>
|
||||||
</lion-select>
|
</lion-select>
|
||||||
`);
|
`));
|
||||||
el.addEventListener('model-value-changed', spy);
|
el.addEventListener('model-value-changed', spy);
|
||||||
const option2 = el.querySelector('option:nth-child(2)');
|
const option2 = /** @type {HTMLOptionElement} */ (el.querySelector('option:nth-child(2)'));
|
||||||
|
|
||||||
// mimic user input
|
// mimic user input
|
||||||
option2.selected = true;
|
option2.selected = true;
|
||||||
|
|
@ -198,7 +241,7 @@ describe('lion-select', () => {
|
||||||
|
|
||||||
spy.resetHistory();
|
spy.resetHistory();
|
||||||
|
|
||||||
const option3 = el.querySelector('option:nth-child(3)');
|
const option3 = /** @type {HTMLOptionElement} */ (el.querySelector('option:nth-child(3)'));
|
||||||
|
|
||||||
// mimic user input
|
// mimic user input
|
||||||
option3.selected = true;
|
option3.selected = true;
|
||||||
|
|
@ -238,13 +281,13 @@ describe('lion-select', () => {
|
||||||
`);
|
`);
|
||||||
|
|
||||||
el.addEventListener('model-value-changed', spy);
|
el.addEventListener('model-value-changed', spy);
|
||||||
const option2 = el.querySelector('lion-option:nth-child(2)');
|
const option2 = /** @type {LionOption} */ (el.querySelector('lion-option:nth-child(2)'));
|
||||||
option2.checked = true;
|
option2.checked = true;
|
||||||
expect(spy.callCount).to.equal(interactionCount);
|
expect(spy.callCount).to.equal(interactionCount);
|
||||||
|
|
||||||
spy.resetHistory();
|
spy.resetHistory();
|
||||||
|
|
||||||
const option3 = el.querySelector('lion-option:nth-child(3)');
|
const option3 = /** @type {LionOption} */ (el.querySelector('lion-option:nth-child(3)'));
|
||||||
option3.checked = true;
|
option3.checked = true;
|
||||||
expect(spy.callCount).to.equal(interactionCount);
|
expect(spy.callCount).to.equal(interactionCount);
|
||||||
});
|
});
|
||||||
|
|
@ -274,7 +317,7 @@ describe('lion-fieldset', () => {
|
||||||
`);
|
`);
|
||||||
|
|
||||||
el.addEventListener('model-value-changed', spy);
|
el.addEventListener('model-value-changed', spy);
|
||||||
const input = el.querySelector('lion-input');
|
const input = /** @type {LionInput} */ (el.querySelector('lion-input'));
|
||||||
input.modelValue = 'foo';
|
input.modelValue = 'foo';
|
||||||
expect(spy.callCount).to.equal(interactionCount);
|
expect(spy.callCount).to.equal(interactionCount);
|
||||||
});
|
});
|
||||||
|
|
@ -337,8 +380,9 @@ describe('detail.isTriggeredByUser', () => {
|
||||||
* - false: when child formElement condition for RegularField is not met
|
* - false: when child formElement condition for RegularField is not met
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const featureDetectChoiceField = el => 'checked' in el && 'choiceValue' in el;
|
const featureDetectChoiceField = /** @param {HTMLElement} el */ el =>
|
||||||
const featureDetectOptionChoiceField = el => 'active' in el;
|
'checked' in el && 'choiceValue' in el;
|
||||||
|
const featureDetectOptionChoiceField = /** @param {HTMLElement} el */ el => 'active' in el;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {FormControl} el
|
* @param {FormControl} el
|
||||||
|
|
@ -355,9 +399,8 @@ describe('detail.isTriggeredByUser', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {FormControl} el
|
* @param {FormControl & {value: string}} el
|
||||||
* @param {string} newViewValue
|
* @param {string} newViewValue
|
||||||
* @returns {'RegularField'|'ChoiceField'|'OptionChoiceField'|'ChoiceGroupField'|'FormOrFieldset'}
|
|
||||||
*/
|
*/
|
||||||
function mimicUserInput(el, newViewValue) {
|
function mimicUserInput(el, newViewValue) {
|
||||||
const type = detectType(el);
|
const type = detectType(el);
|
||||||
|
|
@ -391,27 +434,39 @@ describe('detail.isTriggeredByUser', () => {
|
||||||
} else if (controlName === 'field') {
|
} else if (controlName === 'field') {
|
||||||
childrenEl = await fixture(html`<input slot="input" />`);
|
childrenEl = await fixture(html`<input slot="input" />`);
|
||||||
}
|
}
|
||||||
const el = await fixture(html`<${tag}>${childrenEl}</${tag}>`);
|
|
||||||
|
const el = /** @type {LitElement & FormControl & {value: string} & {registrationComplete: Promise<boolean>} & {formElements: Array.<FormControl & {value: string}>}} */ (await fixture(
|
||||||
|
html`<${tag}>${childrenEl}</${tag}>`,
|
||||||
|
));
|
||||||
await el.registrationComplete;
|
await el.registrationComplete;
|
||||||
el.addEventListener('model-value-changed', spy);
|
el.addEventListener('model-value-changed', spy);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {FormControl & {value: string}} formControl
|
||||||
|
*/
|
||||||
function expectCorrectEventMetaRegularField(formControl) {
|
function expectCorrectEventMetaRegularField(formControl) {
|
||||||
mimicUserInput(formControl, 'userValue', 'RegularField');
|
mimicUserInput(formControl, 'userValue');
|
||||||
expect(spy.firstCall.args[0].detail.isTriggeredByUser).to.be.true;
|
expect(spy.firstCall.args[0].detail.isTriggeredByUser).to.be.true;
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
formControl.modelValue = 'programmaticValue';
|
formControl.modelValue = 'programmaticValue';
|
||||||
expect(spy.secondCall.args[0].detail.isTriggeredByUser).to.be.false;
|
expect(spy.secondCall.args[0].detail.isTriggeredByUser).to.be.false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {FormControl & {value: string}} formControl
|
||||||
|
*/
|
||||||
function resetChoiceFieldToForceRepropagation(formControl) {
|
function resetChoiceFieldToForceRepropagation(formControl) {
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
formControl.checked = false;
|
formControl.checked = false;
|
||||||
spy.resetHistory();
|
spy.resetHistory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {FormControl & {value: string}} formControl
|
||||||
|
*/
|
||||||
function expectCorrectEventMetaChoiceField(formControl) {
|
function expectCorrectEventMetaChoiceField(formControl) {
|
||||||
resetChoiceFieldToForceRepropagation(formControl);
|
resetChoiceFieldToForceRepropagation(formControl);
|
||||||
mimicUserInput(formControl, 'userValue', 'ChoiceField');
|
mimicUserInput(formControl, 'userValue');
|
||||||
expect(spy.firstCall.args[0].detail.isTriggeredByUser).to.be.true;
|
expect(spy.firstCall.args[0].detail.isTriggeredByUser).to.be.true;
|
||||||
|
|
||||||
resetChoiceFieldToForceRepropagation(formControl);
|
resetChoiceFieldToForceRepropagation(formControl);
|
||||||
|
|
|
||||||
|
|
@ -4,20 +4,28 @@ import { expect, html, fixture } from '@open-wc/testing';
|
||||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('@lion/input').LionInput} LionInput
|
||||||
|
* @typedef {import('@lion/fieldset').LionFieldset} LionFieldset
|
||||||
|
* @typedef {import('@lion/core').TemplateResult} TemplateResult
|
||||||
|
*/
|
||||||
|
const inputFixture = /** @type {(arg: TemplateResult) => Promise<LionInput>} */ (fixture);
|
||||||
|
const fieldsetFixture = /** @type {(arg: TemplateResult) => Promise<LionFieldset>} */ (fixture);
|
||||||
|
|
||||||
describe('model value event', () => {
|
describe('model value event', () => {
|
||||||
describe('form path', () => {
|
describe('form path', () => {
|
||||||
it('should be property', async () => {
|
it('should be property', async () => {
|
||||||
const spy = sinon.spy();
|
const spy = sinon.spy();
|
||||||
const input = await fixture(html`<lion-input></lion-input>`);
|
const input = await inputFixture(html`<lion-input></lion-input>`);
|
||||||
input.addEventListener('model-value-changed', spy);
|
input.addEventListener('model-value-changed', spy);
|
||||||
input.modelValue = 'woof';
|
input.modelValue = 'woof';
|
||||||
const e = spy.firstCall.args[0];
|
const e = spy.firstCall.args[0];
|
||||||
expect(e.detail).to.have.a.property('formPath');
|
expect(e.detail).to.have.property('formPath');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should contain dispatching field', async () => {
|
it('should contain dispatching field', async () => {
|
||||||
const spy = sinon.spy();
|
const spy = sinon.spy();
|
||||||
const input = await fixture(html`<lion-input></lion-input>`);
|
const input = await inputFixture(html`<lion-input></lion-input>`);
|
||||||
input.addEventListener('model-value-changed', spy);
|
input.addEventListener('model-value-changed', spy);
|
||||||
input.modelValue = 'foo';
|
input.modelValue = 'foo';
|
||||||
const e = spy.firstCall.args[0];
|
const e = spy.firstCall.args[0];
|
||||||
|
|
@ -26,14 +34,14 @@ describe('model value event', () => {
|
||||||
|
|
||||||
it('should contain field and group', async () => {
|
it('should contain field and group', async () => {
|
||||||
const spy = sinon.spy();
|
const spy = sinon.spy();
|
||||||
const fieldset = await fixture(html`
|
const fieldset = await fieldsetFixture(html`
|
||||||
<lion-fieldset name="fieldset">
|
<lion-fieldset name="fieldset">
|
||||||
<lion-input name="input"></lion-input>
|
<lion-input name="input"></lion-input>
|
||||||
</lion-fieldset>
|
</lion-fieldset>
|
||||||
`);
|
`);
|
||||||
await fieldset.registrationComplete;
|
await fieldset.registrationComplete;
|
||||||
fieldset.addEventListener('model-value-changed', spy);
|
fieldset.addEventListener('model-value-changed', spy);
|
||||||
const input = fieldset.querySelector('lion-input');
|
const input = /** @type {LionInput} */ (fieldset.querySelector('lion-input'));
|
||||||
input.modelValue = 'foo';
|
input.modelValue = 'foo';
|
||||||
const e = spy.firstCall.args[0];
|
const e = spy.firstCall.args[0];
|
||||||
expect(e.detail.formPath).to.eql([input, fieldset]);
|
expect(e.detail.formPath).to.eql([input, fieldset]);
|
||||||
|
|
@ -41,15 +49,15 @@ describe('model value event', () => {
|
||||||
|
|
||||||
it('should contain deep elements', async () => {
|
it('should contain deep elements', async () => {
|
||||||
const spy = sinon.spy();
|
const spy = sinon.spy();
|
||||||
const grandparent = await fixture(html`
|
const grandparent = await fieldsetFixture(html`
|
||||||
<lion-fieldset name="grandparent">
|
<lion-fieldset name="grandparent">
|
||||||
<lion-fieldset name="parent">
|
<lion-fieldset name="parent">
|
||||||
<lion-input name="input"></lion-input>
|
<lion-input name="input"></lion-input>
|
||||||
</lion-fieldset>
|
</lion-fieldset>
|
||||||
</lion-fieldset>
|
</lion-fieldset>
|
||||||
`);
|
`);
|
||||||
const parent = grandparent.querySelector('[name=parent]');
|
const parent = /** @type {LionFieldset} */ (grandparent.querySelector('[name=parent]'));
|
||||||
const input = grandparent.querySelector('[name=input]');
|
const input = /** @type {LionInput} */ (grandparent.querySelector('[name=input]'));
|
||||||
await grandparent.registrationComplete;
|
await grandparent.registrationComplete;
|
||||||
await parent.registrationComplete;
|
await parent.registrationComplete;
|
||||||
|
|
||||||
|
|
@ -61,7 +69,7 @@ describe('model value event', () => {
|
||||||
|
|
||||||
it('should ignore elements that are not fields or fieldsets', async () => {
|
it('should ignore elements that are not fields or fieldsets', async () => {
|
||||||
const spy = sinon.spy();
|
const spy = sinon.spy();
|
||||||
const grandparent = await fixture(html`
|
const grandparent = await fieldsetFixture(html`
|
||||||
<lion-fieldset name="grandparent">
|
<lion-fieldset name="grandparent">
|
||||||
<div>
|
<div>
|
||||||
<lion-fieldset name="parent">
|
<lion-fieldset name="parent">
|
||||||
|
|
@ -74,8 +82,8 @@ describe('model value event', () => {
|
||||||
</div>
|
</div>
|
||||||
</lion-fieldset>
|
</lion-fieldset>
|
||||||
`);
|
`);
|
||||||
const parent = grandparent.querySelector('[name=parent]');
|
const parent = /** @type {LionFieldset} */ (grandparent.querySelector('[name=parent]'));
|
||||||
const input = grandparent.querySelector('[name=input]');
|
const input = /** @type {LionInput} */ (grandparent.querySelector('[name=input]'));
|
||||||
await grandparent.registrationComplete;
|
await grandparent.registrationComplete;
|
||||||
await parent.registrationComplete;
|
await parent.registrationComplete;
|
||||||
|
|
||||||
|
|
@ -87,10 +95,11 @@ describe('model value event', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('signature', () => {
|
describe('signature', () => {
|
||||||
|
/** @type {?} */
|
||||||
let e;
|
let e;
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const spy = sinon.spy();
|
const spy = sinon.spy();
|
||||||
const el = await fixture(html`<lion-input></lion-input>`);
|
const el = await inputFixture(html`<lion-input></lion-input>`);
|
||||||
el.addEventListener('model-value-changed', spy);
|
el.addEventListener('model-value-changed', spy);
|
||||||
el.modelValue = 'foo';
|
el.modelValue = 'foo';
|
||||||
// eslint-disable-next-line prefer-destructuring
|
// eslint-disable-next-line prefer-destructuring
|
||||||
|
|
@ -112,18 +121,19 @@ describe('model value event', () => {
|
||||||
|
|
||||||
describe('propagation', () => {
|
describe('propagation', () => {
|
||||||
it('should dispatch different event at each level', async () => {
|
it('should dispatch different event at each level', async () => {
|
||||||
const grandparent = await fixture(html`
|
const grandparent = await fieldsetFixture(html`
|
||||||
<lion-fieldset name="grandparent">
|
<lion-fieldset name="grandparent">
|
||||||
<lion-fieldset name="parent">
|
<lion-fieldset name="parent">
|
||||||
<lion-input name="input"></lion-input>
|
<lion-input name="input"></lion-input>
|
||||||
</lion-fieldset>
|
</lion-fieldset>
|
||||||
</lion-fieldset>
|
</lion-fieldset>
|
||||||
`);
|
`);
|
||||||
const parent = grandparent.querySelector('[name="parent"]');
|
const parent = /** @type {LionFieldset} */ (grandparent.querySelector('[name="parent"]'));
|
||||||
const input = grandparent.querySelector('[name="input"]');
|
const input = /** @type {LionInput} */ (grandparent.querySelector('[name="input"]'));
|
||||||
await grandparent.registrationComplete;
|
await grandparent.registrationComplete;
|
||||||
await parent.registrationComplete;
|
await parent.registrationComplete;
|
||||||
|
|
||||||
|
/** @type {sinon.SinonSpy[]} */
|
||||||
const spies = [];
|
const spies = [];
|
||||||
[grandparent, parent, input].forEach(element => {
|
[grandparent, parent, input].forEach(element => {
|
||||||
const spy = sinon.spy();
|
const spy = sinon.spy();
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ function isValidDate(date) {
|
||||||
*
|
*
|
||||||
* @customElement lion-input-date
|
* @customElement lion-input-date
|
||||||
*/
|
*/
|
||||||
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/40110
|
// @ts-expect-error false positive for incompatible static get properties. Lit-element merges super properties already for you.
|
||||||
export class LionInputDate extends LocalizeMixin(LionInput) {
|
export class LionInputDate extends LocalizeMixin(LionInput) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { css, html, LitElement } from '@lion/core';
|
import { css, html, LitElement } from '@lion/core';
|
||||||
import { LocalizeMixin } from '@lion/localize';
|
import { LocalizeMixin } from '@lion/localize';
|
||||||
|
|
||||||
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/40110
|
|
||||||
export class LionCalendarOverlayFrame extends LocalizeMixin(LitElement) {
|
export class LionCalendarOverlayFrame extends LocalizeMixin(LitElement) {
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return [
|
return [
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import { LionCalendarOverlayFrame } from './LionCalendarOverlayFrame.js';
|
||||||
/**
|
/**
|
||||||
* @customElement lion-input-datepicker
|
* @customElement lion-input-datepicker
|
||||||
*/
|
*/
|
||||||
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/40110
|
// @ts-expect-error false positive for incompatible static get properties. Lit-element merges super properties already for you.
|
||||||
export class LionInputDatepicker extends ScopedElementsMixin(
|
export class LionInputDatepicker extends ScopedElementsMixin(
|
||||||
ArrowMixin(OverlayMixin(LionInputDate)),
|
ArrowMixin(OverlayMixin(LionInputDate)),
|
||||||
) {
|
) {
|
||||||
|
|
@ -199,9 +199,8 @@ export class LionInputDatepicker extends ScopedElementsMixin(
|
||||||
|
|
||||||
__toggleInvokerDisabled() {
|
__toggleInvokerDisabled() {
|
||||||
if (this._invokerNode) {
|
if (this._invokerNode) {
|
||||||
// @ts-expect-error even though disabled may not exist on the invoker node
|
const invokerNode = /** @type {HTMLElement & {disabled: boolean}} */ (this._invokerNode);
|
||||||
// set it anyway, it doesn't harm, and is needed in case of invoker elements that do have disabled prop
|
invokerNode.disabled = this.disabled || this.readOnly;
|
||||||
this._invokerNode.disabled = this.disabled || this.readOnly;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import { LocalizeMixin } from '@lion/localize';
|
||||||
*
|
*
|
||||||
* @customElement lion-input-email
|
* @customElement lion-input-email
|
||||||
*/
|
*/
|
||||||
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/40110
|
|
||||||
export class LionInputEmail extends LocalizeMixin(LionInput) {
|
export class LionInputEmail extends LocalizeMixin(LionInput) {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import { IsIBAN } from './validators.js';
|
||||||
* `LionInputIban` is a class for an IBAN custom form element (`<lion-input-iban>`).
|
* `LionInputIban` is a class for an IBAN custom form element (`<lion-input-iban>`).
|
||||||
* @customElement lion-input-iban
|
* @customElement lion-input-iban
|
||||||
*/
|
*/
|
||||||
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/40110
|
|
||||||
export class LionInputIban extends LocalizeMixin(LionInput) {
|
export class LionInputIban extends LocalizeMixin(LionInput) {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import { ListboxMixin } from './ListboxMixin.js';
|
||||||
* LionListbox: implements the wai-aria listbox design pattern and integrates it as a Lion
|
* LionListbox: implements the wai-aria listbox design pattern and integrates it as a Lion
|
||||||
* FormControl
|
* FormControl
|
||||||
*/
|
*/
|
||||||
// @ts-expect-error
|
|
||||||
export class LionListbox extends ListboxMixin(
|
export class LionListbox extends ListboxMixin(
|
||||||
FocusMixin(InteractionStateMixin(ValidateMixin(LitElement))),
|
FocusMixin(InteractionStateMixin(ValidateMixin(LitElement))),
|
||||||
) {}
|
) {}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ import { css, DisabledMixin, html, LitElement } from '@lion/core';
|
||||||
* Element gets state supplied externally, reflects this to attributes,
|
* Element gets state supplied externally, reflects this to attributes,
|
||||||
* enabling SubClassers to style based on those states
|
* enabling SubClassers to style based on those states
|
||||||
*/
|
*/
|
||||||
// @ts-expect-error
|
|
||||||
export class LionOption extends DisabledMixin(ChoiceInputMixin(FormRegisteringMixin(LitElement))) {
|
export class LionOption extends DisabledMixin(ChoiceInputMixin(FormRegisteringMixin(LitElement))) {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
1
packages/listbox/types/LionOption.d.ts
vendored
1
packages/listbox/types/LionOption.d.ts
vendored
|
|
@ -1,5 +1,6 @@
|
||||||
import { ChoiceGroupHost } from '@lion/form-core/types/choice-group/ChoiceGroupMixinTypes';
|
import { ChoiceGroupHost } from '@lion/form-core/types/choice-group/ChoiceGroupMixinTypes';
|
||||||
|
|
||||||
export declare class LionOptionHost {
|
export declare class LionOptionHost {
|
||||||
|
constructor(...args: any[]);
|
||||||
private __parentFormGroup: ChoiceGroupHost;
|
private __parentFormGroup: ChoiceGroupHost;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { LionOptions } from '../src/LionOptions.js';
|
||||||
import { LionOption } from '../src/LionOption.js';
|
import { LionOption } from '../src/LionOption.js';
|
||||||
|
|
||||||
export declare class ListboxHost {
|
export declare class ListboxHost {
|
||||||
|
constructor(...args: any[]);
|
||||||
/**
|
/**
|
||||||
* When true, will synchronize activedescendant and selected element on
|
* When true, will synchronize activedescendant and selected element on
|
||||||
* arrow key navigation.
|
* arrow key navigation.
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,6 @@ describe('LocalizeMixin', () => {
|
||||||
'child-element': loc => fakeImport(`./child-element/${loc}.js`),
|
'child-element': loc => fakeImport(`./child-element/${loc}.js`),
|
||||||
};
|
};
|
||||||
|
|
||||||
// @ts-expect-error
|
|
||||||
class ParentElement extends LocalizeMixin(LitElement) {
|
class ParentElement extends LocalizeMixin(LitElement) {
|
||||||
static get localizeNamespaces() {
|
static get localizeNamespaces() {
|
||||||
return [parentElementNs, defaultNs, ...super.localizeNamespaces];
|
return [parentElementNs, defaultNs, ...super.localizeNamespaces];
|
||||||
|
|
@ -79,7 +78,6 @@ describe('LocalizeMixin', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const tagString = defineCE(
|
const tagString = defineCE(
|
||||||
// @ts-expect-error
|
|
||||||
class ChildElement extends LocalizeMixin(ParentElement) {
|
class ChildElement extends LocalizeMixin(ParentElement) {
|
||||||
static get localizeNamespaces() {
|
static get localizeNamespaces() {
|
||||||
return [childElementNs, defaultNs, ...super.localizeNamespaces];
|
return [childElementNs, defaultNs, ...super.localizeNamespaces];
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ interface msgOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
declare class LocalizeMixinHost {
|
declare class LocalizeMixinHost {
|
||||||
// FIXME: return value type check doesn't seem to be `working!
|
constructor(...args: any[]);
|
||||||
static get localizeNamespaces(): StringToFunctionMap[];
|
static get localizeNamespaces(): StringToFunctionMap[];
|
||||||
|
|
||||||
static get waitForLocalizeNamespaces(): boolean;
|
static get waitForLocalizeNamespaces(): boolean;
|
||||||
|
|
|
||||||
|
|
@ -507,7 +507,7 @@ export class OverlayController extends EventTargetShim {
|
||||||
if (this.placementMode === 'local') {
|
if (this.placementMode === 'local') {
|
||||||
// Lazily load Popper if not done yet
|
// Lazily load Popper if not done yet
|
||||||
if (!OverlayController.popperModule) {
|
if (!OverlayController.popperModule) {
|
||||||
// @ts-expect-error
|
// @ts-expect-error FIXME: for some reason createPopper is missing here
|
||||||
OverlayController.popperModule = preloadPopper();
|
OverlayController.popperModule = preloadPopper();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -838,8 +838,8 @@ export class OverlayController extends EventTargetShim {
|
||||||
hideConfig.backdropNode.classList.remove(
|
hideConfig.backdropNode.classList.remove(
|
||||||
`${this.placementMode}-overlays__backdrop--animation-in`,
|
`${this.placementMode}-overlays__backdrop--animation-in`,
|
||||||
);
|
);
|
||||||
/** @type {(ev:AnimationEvent) => void} */
|
/** @type {() => void} */
|
||||||
let afterFadeOut;
|
let afterFadeOut = () => {};
|
||||||
hideConfig.backdropNode.classList.add(
|
hideConfig.backdropNode.classList.add(
|
||||||
`${this.placementMode}-overlays__backdrop--animation-out`,
|
`${this.placementMode}-overlays__backdrop--animation-out`,
|
||||||
);
|
);
|
||||||
|
|
@ -857,7 +857,7 @@ export class OverlayController extends EventTargetShim {
|
||||||
resolve(undefined);
|
resolve(undefined);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
// @ts-expect-error
|
|
||||||
hideConfig.backdropNode.addEventListener('animationend', afterFadeOut);
|
hideConfig.backdropNode.addEventListener('animationend', afterFadeOut);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* Whether first element contains the second element, also goes through shadow roots
|
* Whether first element contains the second element, also goes through shadow roots
|
||||||
* @param {HTMLElement|ShadowRoot} el
|
* @param {HTMLElement|ShadowRoot} el
|
||||||
* @param {HTMLElement} targetEl
|
* @param {HTMLElement|ShadowRoot} targetEl
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
export function deepContains(el, targetEl) {
|
export function deepContains(el, targetEl) {
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@
|
||||||
* Compares two OverlayConfigs to equivalence. Intended to prevent unnecessary resets.
|
* Compares two OverlayConfigs to equivalence. Intended to prevent unnecessary resets.
|
||||||
* Note that it doesn't cover as many use cases as common implementations, such as Lodash isEqual.
|
* Note that it doesn't cover as many use cases as common implementations, such as Lodash isEqual.
|
||||||
*
|
*
|
||||||
* @param {OverlayConfig} a
|
* @param {Partial<OverlayConfig>} a
|
||||||
* @param {OverlayConfig} b
|
* @param {Partial<OverlayConfig>} b
|
||||||
* @returns {boolean} Whether the configs are equivalent
|
* @returns {boolean} Whether the configs are equivalent
|
||||||
*/
|
*/
|
||||||
export function isEqualConfig(a, b) {
|
export function isEqualConfig(a, b) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { expect, fixture, html } from '@open-wc/testing';
|
import { expect, fixture, html } from '@open-wc/testing';
|
||||||
// @ts-expect-error
|
|
||||||
import { renderLitAsNode } from '@lion/helpers';
|
import { renderLitAsNode } from '@lion/helpers';
|
||||||
import { getDeepActiveElement } from '../../src/utils/get-deep-active-element.js';
|
import { getDeepActiveElement } from '../../src/utils/get-deep-active-element.js';
|
||||||
import { getFocusableElements } from '../../src/utils/get-focusable-elements.js';
|
import { getFocusableElements } from '../../src/utils/get-focusable-elements.js';
|
||||||
|
|
@ -72,9 +71,11 @@ function createShadowDomNode() {
|
||||||
<button id="outside-2">outside 2</button>
|
<button id="outside-2">outside 2</button>
|
||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
const rootElementShadow = shadowDomNode.querySelector('#rootElementShadow');
|
const rootElementShadow = shadowDomNode?.querySelector('#rootElementShadow');
|
||||||
rootElementShadow.attachShadow({ mode: 'open' });
|
rootElementShadow?.attachShadow({ mode: 'open' });
|
||||||
rootElementShadow.shadowRoot.appendChild(interactionElementsNode);
|
if (interactionElementsNode) {
|
||||||
|
rootElementShadow?.shadowRoot?.appendChild(interactionElementsNode);
|
||||||
|
}
|
||||||
return shadowDomNode;
|
return shadowDomNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,12 @@ describe('deepContains()', () => {
|
||||||
expect(deepContains(shadowRoot, shadowElementChild)).to.be.true;
|
expect(deepContains(shadowRoot, shadowElementChild)).to.be.true;
|
||||||
|
|
||||||
// Siblings
|
// Siblings
|
||||||
expect(deepContains(element.firstElementChild, element.lastElementChild)).to.be.false;
|
expect(
|
||||||
|
deepContains(
|
||||||
|
/** @type {HTMLElement} */ (element.firstElementChild),
|
||||||
|
/** @type {HTMLElement} */ (element.lastElementChild),
|
||||||
|
),
|
||||||
|
).to.be.false;
|
||||||
// Unrelated
|
// Unrelated
|
||||||
expect(deepContains(lightChildEl, shadowElementChild)).to.be.false;
|
expect(deepContains(lightChildEl, shadowElementChild)).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
@ -95,16 +100,19 @@ describe('deepContains()', () => {
|
||||||
</div>
|
</div>
|
||||||
`));
|
`));
|
||||||
|
|
||||||
expect(deepContains(element, element.firstElementChild)).to.be.true;
|
const elementFirstChild = /** @type {HTMLElement} */ (element.firstElementChild);
|
||||||
expect(deepContains(element, element.firstElementChild.shadowRoot)).to.be.true;
|
const elementFirstChildShadow = /** @type {ShadowRoot} */ (elementFirstChild.shadowRoot);
|
||||||
expect(deepContains(element, element.firstElementChild.shadowRoot.children[1])).to.be.true;
|
const elementFirstChildShadowChildren = /** @type {HTMLElement[]} */ (Array.from(
|
||||||
expect(deepContains(element, element.firstElementChild.shadowRoot.children[1].shadowRoot)).to.be
|
elementFirstChildShadow.children,
|
||||||
.true;
|
));
|
||||||
expect(
|
const elementFirstChildShadowChildShadow = /** @type {ShadowRoot} */ (elementFirstChildShadowChildren[1]
|
||||||
deepContains(
|
.shadowRoot);
|
||||||
element,
|
const elementFirstChildShadowChildShadowLastChild = /** @type {HTMLElement} */ (elementFirstChildShadowChildShadow.lastElementChild);
|
||||||
element.firstElementChild.shadowRoot.children[1].shadowRoot.lastElementChild,
|
|
||||||
),
|
expect(deepContains(element, elementFirstChild)).to.be.true;
|
||||||
).to.be.true;
|
expect(deepContains(element, elementFirstChildShadow)).to.be.true;
|
||||||
|
expect(deepContains(element, elementFirstChildShadowChildren[1])).to.be.true;
|
||||||
|
expect(deepContains(element, elementFirstChildShadowChildShadow)).to.be.true;
|
||||||
|
expect(deepContains(element, elementFirstChildShadowChildShadowLastChild)).to.be.true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,15 @@ import { isEqualConfig } from '../../src/utils/is-equal-config.js';
|
||||||
|
|
||||||
function TestConfig() {
|
function TestConfig() {
|
||||||
return {
|
return {
|
||||||
placementMode: 'local',
|
placementMode: /** @type {'local'|'global'} */ ('local'),
|
||||||
hidesOnOutsideClick: true,
|
hidesOnOutsideClick: true,
|
||||||
popperConfig: {
|
popperConfig: {
|
||||||
modifiers: {
|
modifiers: [
|
||||||
offset: {
|
{
|
||||||
|
name: 'offset',
|
||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
},
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -31,13 +32,13 @@ describe('isEqualConfig()', () => {
|
||||||
|
|
||||||
it('compares prop count', () => {
|
it('compares prop count', () => {
|
||||||
const config = TestConfig();
|
const config = TestConfig();
|
||||||
expect(isEqualConfig(config, { ...config, extra: 'value' })).eql(false);
|
expect(isEqualConfig(config, { ...config, isBlocking: true })).eql(false);
|
||||||
expect(isEqualConfig({ ...config, extra: 'value' }, config)).eql(false);
|
expect(isEqualConfig({ ...config, isBlocking: true }, config)).eql(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('regards missing props different from ones with undefined value', () => {
|
it('regards missing props different from ones with undefined value', () => {
|
||||||
const config = TestConfig();
|
const config = TestConfig();
|
||||||
expect(isEqualConfig(config, { ...config, extra: undefined })).eql(false);
|
expect(isEqualConfig(config, { ...config, referenceNode: undefined })).eql(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('compares nested props', () => {
|
it('compares nested props', () => {
|
||||||
|
|
@ -46,12 +47,7 @@ describe('isEqualConfig()', () => {
|
||||||
...config,
|
...config,
|
||||||
popperConfig: {
|
popperConfig: {
|
||||||
...config.popperConfig,
|
...config.popperConfig,
|
||||||
modifiers: {
|
modifiers: [...config.popperConfig.modifiers],
|
||||||
...config.popperConfig.modifiers,
|
|
||||||
offset: {
|
|
||||||
...config.popperConfig.modifiers.offset,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
expect(isEqualConfig(config, sameConfig)).eql(true);
|
expect(isEqualConfig(config, sameConfig)).eql(true);
|
||||||
|
|
@ -59,12 +55,13 @@ describe('isEqualConfig()', () => {
|
||||||
...config,
|
...config,
|
||||||
popperConfig: {
|
popperConfig: {
|
||||||
...config.popperConfig,
|
...config.popperConfig,
|
||||||
modifiers: {
|
modifiers: [
|
||||||
...config.popperConfig.modifiers,
|
...config.popperConfig.modifiers,
|
||||||
offset: {
|
{
|
||||||
enabled: !config.popperConfig.modifiers.offset.enabled,
|
name: 'offset',
|
||||||
|
enabled: !config.popperConfig.modifiers.find(mod => mod.name === 'offset')?.enabled,
|
||||||
},
|
},
|
||||||
},
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
expect(isEqualConfig(config, differentConfig)).eql(false);
|
expect(isEqualConfig(config, differentConfig)).eql(false);
|
||||||
|
|
|
||||||
1
packages/overlays/types/ArrowMixinTypes.d.ts
vendored
1
packages/overlays/types/ArrowMixinTypes.d.ts
vendored
|
|
@ -5,6 +5,7 @@ import { Options as PopperOptions, State } from '@popperjs/core/lib/popper';
|
||||||
import { OverlayConfig } from '../types/OverlayConfig';
|
import { OverlayConfig } from '../types/OverlayConfig';
|
||||||
|
|
||||||
export declare class ArrowHost {
|
export declare class ArrowHost {
|
||||||
|
constructor(...args: any[]);
|
||||||
static get properties(): {
|
static get properties(): {
|
||||||
hasArrow: {
|
hasArrow: {
|
||||||
type: BooleanConstructor;
|
type: BooleanConstructor;
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ export interface DefineOverlayConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare class OverlayHost {
|
export declare class OverlayHost {
|
||||||
|
constructor(...args: any[]);
|
||||||
public opened: Boolean;
|
public opened: Boolean;
|
||||||
|
|
||||||
public get config(): OverlayConfig;
|
public get config(): OverlayConfig;
|
||||||
|
|
@ -66,6 +67,6 @@ export declare class OverlayHost {
|
||||||
|
|
||||||
export declare function OverlayImplementation<T extends Constructor<LitElement>>(
|
export declare function OverlayImplementation<T extends Constructor<LitElement>>(
|
||||||
superclass: T,
|
superclass: T,
|
||||||
): T & Constructor<OverlayHost> & OverlayHost;
|
): T & Constructor<OverlayHost> & typeof OverlayHost;
|
||||||
|
|
||||||
export type OverlayMixin = typeof OverlayImplementation;
|
export type OverlayMixin = typeof OverlayImplementation;
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import { LocalizeMixin } from '@lion/localize';
|
||||||
*
|
*
|
||||||
* @customElement lion-pagination
|
* @customElement lion-pagination
|
||||||
*/
|
*/
|
||||||
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/40110
|
|
||||||
export class LionPagination extends LocalizeMixin(LitElement) {
|
export class LionPagination extends LocalizeMixin(LitElement) {
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return [
|
return [
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
import { nothing, LitElement } from '@lion/core';
|
import { nothing, LitElement } from '@lion/core';
|
||||||
import { localize, LocalizeMixin } from '@lion/localize';
|
import { localize, LocalizeMixin } from '@lion/localize';
|
||||||
|
|
||||||
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/40110
|
|
||||||
export class LionProgressIndicator extends LocalizeMixin(LitElement) {
|
export class LionProgressIndicator extends LocalizeMixin(LitElement) {
|
||||||
static get localizeNamespaces() {
|
static get localizeNamespaces() {
|
||||||
return [
|
return [
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import { ChoiceGroupMixin, FormGroupMixin } from '@lion/form-core';
|
||||||
/**
|
/**
|
||||||
* A wrapper around multiple radios.
|
* A wrapper around multiple radios.
|
||||||
*/
|
*/
|
||||||
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/40110
|
|
||||||
export class LionRadioGroup extends ChoiceGroupMixin(FormGroupMixin(LitElement)) {
|
export class LionRadioGroup extends ChoiceGroupMixin(FormGroupMixin(LitElement)) {
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ function detectInteractionMode() {
|
||||||
/**
|
/**
|
||||||
* LionSelectRich: wraps the <lion-listbox> element
|
* LionSelectRich: wraps the <lion-listbox> element
|
||||||
*/
|
*/
|
||||||
// @ts-expect-error
|
// @ts-expect-error false positive for incompatible static get properties. Lit-element merges super properties already for you.
|
||||||
export class LionSelectRich extends SlotMixin(ScopedElementsMixin(OverlayMixin(LionListbox))) {
|
export class LionSelectRich extends SlotMixin(ScopedElementsMixin(OverlayMixin(LionListbox))) {
|
||||||
static get scopedElements() {
|
static get scopedElements() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -88,7 +88,6 @@ export class LionSelectRich extends SlotMixin(ScopedElementsMixin(OverlayMixin(L
|
||||||
* @configure ListboxMixin
|
* @configure ListboxMixin
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
// @ts-ignore
|
|
||||||
get _scrollTargetNode() {
|
get _scrollTargetNode() {
|
||||||
// TODO: should this be defined here or in extension layer?
|
// TODO: should this be defined here or in extension layer?
|
||||||
// @ts-expect-error we allow the _overlayContentNode to define its own _scrollTargetNode
|
// @ts-expect-error we allow the _overlayContentNode to define its own _scrollTargetNode
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { LitElement } from '@lion/core';
|
import { LitElement } from '@lion/core';
|
||||||
// @ts-expect-error
|
|
||||||
import { renderLitAsNode } from '@lion/helpers';
|
import { renderLitAsNode } from '@lion/helpers';
|
||||||
import { OverlayController } from '@lion/overlays';
|
import { OverlayController } from '@lion/overlays';
|
||||||
import { LionOption } from '@lion/listbox';
|
import { LionOption } from '@lion/listbox';
|
||||||
|
|
@ -10,7 +9,7 @@ import {
|
||||||
html,
|
html,
|
||||||
nextFrame,
|
nextFrame,
|
||||||
unsafeStatic,
|
unsafeStatic,
|
||||||
fixture,
|
fixture as _fixture,
|
||||||
} from '@open-wc/testing';
|
} from '@open-wc/testing';
|
||||||
import { LionSelectInvoker, LionSelectRich } from '../index.js';
|
import { LionSelectInvoker, LionSelectRich } from '../index.js';
|
||||||
|
|
||||||
|
|
@ -19,6 +18,12 @@ import '@lion/listbox/lion-option.js';
|
||||||
import '@lion/listbox/lion-options.js';
|
import '@lion/listbox/lion-options.js';
|
||||||
import '../lion-select-rich.js';
|
import '../lion-select-rich.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('@lion/core').TemplateResult} TemplateResult
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fixture = /** @type {(arg: TemplateResult) => Promise<LionSelectRich>} */ (_fixture);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {LionSelectRich} lionSelectEl
|
* @param {LionSelectRich} lionSelectEl
|
||||||
*/
|
*/
|
||||||
|
|
@ -26,10 +31,16 @@ function getProtectedMembers(lionSelectEl) {
|
||||||
// @ts-ignore protected members allowed in test
|
// @ts-ignore protected members allowed in test
|
||||||
const {
|
const {
|
||||||
_invokerNode: invoker,
|
_invokerNode: invoker,
|
||||||
|
// @ts-ignore
|
||||||
_feedbackNode: feedback,
|
_feedbackNode: feedback,
|
||||||
|
// @ts-ignore
|
||||||
_labelNode: label,
|
_labelNode: label,
|
||||||
|
// @ts-ignore
|
||||||
_helpTextNode: helpText,
|
_helpTextNode: helpText,
|
||||||
|
// @ts-ignore
|
||||||
_listboxNode: listbox,
|
_listboxNode: listbox,
|
||||||
|
// @ts-ignore
|
||||||
|
_overlayCtrl: overlay,
|
||||||
} = lionSelectEl;
|
} = lionSelectEl;
|
||||||
return {
|
return {
|
||||||
invoker,
|
invoker,
|
||||||
|
|
@ -37,53 +48,53 @@ function getProtectedMembers(lionSelectEl) {
|
||||||
label,
|
label,
|
||||||
helpText,
|
helpText,
|
||||||
listbox,
|
listbox,
|
||||||
|
overlay,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('lion-select-rich', () => {
|
describe('lion-select-rich', () => {
|
||||||
it('clicking the label should focus the invoker', async () => {
|
it('clicking the label should focus the invoker', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(
|
const el = await fixture(html` <lion-select-rich label="foo"> </lion-select-rich> `);
|
||||||
html` <lion-select-rich label="foo"> </lion-select-rich> `,
|
|
||||||
));
|
|
||||||
expect(document.activeElement === document.body).to.be.true;
|
expect(document.activeElement === document.body).to.be.true;
|
||||||
el._labelNode.click();
|
const { label } = getProtectedMembers(el);
|
||||||
|
label.click();
|
||||||
|
|
||||||
// @ts-ignore allow protected access in tests
|
// @ts-ignore allow protected access in tests
|
||||||
expect(document.activeElement === el._invokerNode).to.be.true;
|
expect(document.activeElement === el._invokerNode).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('checks the first enabled option', async () => {
|
it('checks the first enabled option', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-select-rich>
|
<lion-select-rich>
|
||||||
<lion-option .choiceValue=${'Red'}></lion-option>
|
<lion-option .choiceValue=${'Red'}></lion-option>
|
||||||
<lion-option .choiceValue=${'Hotpink'}></lion-option>
|
<lion-option .choiceValue=${'Hotpink'}></lion-option>
|
||||||
<lion-option .choiceValue=${'Blue'}></lion-option>
|
<lion-option .choiceValue=${'Blue'}></lion-option>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`));
|
`);
|
||||||
expect(el.activeIndex).to.equal(0);
|
expect(el.activeIndex).to.equal(0);
|
||||||
expect(el.checkedIndex).to.equal(0);
|
expect(el.checkedIndex).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('still has a checked value while disabled', async () => {
|
it('still has a checked value while disabled', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-select-rich disabled>
|
<lion-select-rich disabled>
|
||||||
<lion-option .choiceValue=${'Red'}>Red</lion-option>
|
<lion-option .choiceValue=${'Red'}>Red</lion-option>
|
||||||
<lion-option .choiceValue=${'Hotpink'}>Hotpink</lion-option>
|
<lion-option .choiceValue=${'Hotpink'}>Hotpink</lion-option>
|
||||||
<lion-option .choiceValue=${'Blue'}>Blue</lion-option>
|
<lion-option .choiceValue=${'Blue'}>Blue</lion-option>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`));
|
`);
|
||||||
|
|
||||||
expect(el.modelValue).to.equal('Red');
|
expect(el.modelValue).to.equal('Red');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('supports having no default selection initially', async () => {
|
it('supports having no default selection initially', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-select-rich id="color" name="color" label="Favorite color" has-no-default-selected>
|
<lion-select-rich id="color" name="color" label="Favorite color" has-no-default-selected>
|
||||||
<lion-option .choiceValue=${'red'}>Red</lion-option>
|
<lion-option .choiceValue=${'red'}>Red</lion-option>
|
||||||
<lion-option .choiceValue=${'hotpink'}>Hotpink</lion-option>
|
<lion-option .choiceValue=${'hotpink'}>Hotpink</lion-option>
|
||||||
<lion-option .choiceValue=${'teal'}>Teal</lion-option>
|
<lion-option .choiceValue=${'teal'}>Teal</lion-option>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`));
|
`);
|
||||||
const { invoker } = getProtectedMembers(el);
|
const { invoker } = getProtectedMembers(el);
|
||||||
expect(invoker.selectedElement).to.be.undefined;
|
expect(invoker.selectedElement).to.be.undefined;
|
||||||
expect(el.modelValue).to.equal('');
|
expect(el.modelValue).to.equal('');
|
||||||
|
|
@ -91,9 +102,7 @@ describe('lion-select-rich', () => {
|
||||||
|
|
||||||
describe('Invoker', () => {
|
describe('Invoker', () => {
|
||||||
it('generates an lion-select-invoker if no invoker is provided', async () => {
|
it('generates an lion-select-invoker if no invoker is provided', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(
|
const el = await fixture(html` <lion-select-rich> </lion-select-rich> `);
|
||||||
html` <lion-select-rich> </lion-select-rich> `,
|
|
||||||
));
|
|
||||||
|
|
||||||
// @ts-ignore allow protected access in tests
|
// @ts-ignore allow protected access in tests
|
||||||
expect(el._invokerNode).to.exist;
|
expect(el._invokerNode).to.exist;
|
||||||
|
|
@ -102,24 +111,24 @@ describe('lion-select-rich', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets the first option as the selectedElement if no option is checked', async () => {
|
it('sets the first option as the selectedElement if no option is checked', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-select-rich name="foo">
|
<lion-select-rich name="foo">
|
||||||
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
||||||
<lion-option .choiceValue=${20}>Item 2</lion-option>
|
<lion-option .choiceValue=${20}>Item 2</lion-option>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`));
|
`);
|
||||||
const options = el.formElements;
|
const options = el.formElements;
|
||||||
// @ts-ignore allow protected access in tests
|
// @ts-ignore allow protected access in tests
|
||||||
expect(el._invokerNode.selectedElement).dom.to.equal(options[0]);
|
expect(el._invokerNode.selectedElement).dom.to.equal(options[0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('syncs the selected element to the invoker', async () => {
|
it('syncs the selected element to the invoker', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-select-rich name="foo">
|
<lion-select-rich name="foo">
|
||||||
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
||||||
<lion-option .choiceValue=${20} checked>Item 2</lion-option>
|
<lion-option .choiceValue=${20} checked>Item 2</lion-option>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`));
|
`);
|
||||||
const options = el.querySelectorAll('lion-option');
|
const options = el.querySelectorAll('lion-option');
|
||||||
// @ts-ignore allow protected access in tests
|
// @ts-ignore allow protected access in tests
|
||||||
expect(el._invokerNode.selectedElement).dom.to.equal(options[1]);
|
expect(el._invokerNode.selectedElement).dom.to.equal(options[1]);
|
||||||
|
|
@ -131,12 +140,12 @@ describe('lion-select-rich', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('delegates readonly to the invoker', async () => {
|
it('delegates readonly to the invoker', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-select-rich readonly>
|
<lion-select-rich readonly>
|
||||||
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
||||||
<lion-option .choiceValue=${20}>Item 2</lion-option>
|
<lion-option .choiceValue=${20}>Item 2</lion-option>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`));
|
`);
|
||||||
|
|
||||||
expect(el.hasAttribute('readonly')).to.be.true;
|
expect(el.hasAttribute('readonly')).to.be.true;
|
||||||
// @ts-ignore allow protected access in tests
|
// @ts-ignore allow protected access in tests
|
||||||
|
|
@ -144,11 +153,11 @@ describe('lion-select-rich', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('delegates singleOption to the invoker', async () => {
|
it('delegates singleOption to the invoker', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-select-rich>
|
<lion-select-rich>
|
||||||
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`));
|
`);
|
||||||
|
|
||||||
expect(el.singleOption).to.be.true;
|
expect(el.singleOption).to.be.true;
|
||||||
// @ts-ignore allow protected access in tests
|
// @ts-ignore allow protected access in tests
|
||||||
|
|
@ -169,16 +178,16 @@ describe('lion-select-rich', () => {
|
||||||
);
|
);
|
||||||
const tagString = unsafeStatic(tag);
|
const tagString = unsafeStatic(tag);
|
||||||
|
|
||||||
const firstOption = renderLitAsNode(
|
const firstOption = /** @type {LionOption} */ (renderLitAsNode(
|
||||||
html`<${tagString} checked .choiceValue=${10}></${tagString}>`,
|
html`<${tagString} checked .choiceValue=${10}></${tagString}>`,
|
||||||
);
|
));
|
||||||
|
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-select-rich>
|
<lion-select-rich>
|
||||||
${firstOption}
|
${firstOption}
|
||||||
<${tagString} .choiceValue=${20}></${tagString}>
|
<${tagString} .choiceValue=${20}></${tagString}>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`));
|
`);
|
||||||
|
|
||||||
// @ts-ignore allow protected access in tests
|
// @ts-ignore allow protected access in tests
|
||||||
expect(el._invokerNode.shadowRoot.firstElementChild.textContent).to.equal('10');
|
expect(el._invokerNode.shadowRoot.firstElementChild.textContent).to.equal('10');
|
||||||
|
|
@ -191,16 +200,17 @@ describe('lion-select-rich', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('inherits the content width including arrow width', async () => {
|
it('inherits the content width including arrow width', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-select-rich>
|
<lion-select-rich>
|
||||||
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
||||||
<lion-option .choiceValue=${10}>Item 2 with long label</lion-option>
|
<lion-option .choiceValue=${10}>Item 2 with long label</lion-option>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`));
|
`);
|
||||||
el.opened = true;
|
el.opened = true;
|
||||||
const options = el.formElements;
|
const options = el.formElements;
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
expect(el._invokerNode.clientWidth).to.equal(options[1].clientWidth);
|
const { invoker } = getProtectedMembers(el);
|
||||||
|
expect(invoker.clientWidth).to.equal(options[1].clientWidth);
|
||||||
|
|
||||||
const newOption = /** @type {LionOption} */ (document.createElement('lion-option'));
|
const newOption = /** @type {LionOption} */ (document.createElement('lion-option'));
|
||||||
newOption.choiceValue = 30;
|
newOption.choiceValue = 30;
|
||||||
|
|
@ -208,26 +218,22 @@ describe('lion-select-rich', () => {
|
||||||
|
|
||||||
el._inputNode.appendChild(newOption);
|
el._inputNode.appendChild(newOption);
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
expect(el._invokerNode.clientWidth).to.equal(options[2].clientWidth);
|
expect(invoker.clientWidth).to.equal(options[2].clientWidth);
|
||||||
|
|
||||||
el._inputNode.removeChild(newOption);
|
el._inputNode.removeChild(newOption);
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
expect(el._invokerNode.clientWidth).to.equal(options[1].clientWidth);
|
expect(invoker.clientWidth).to.equal(options[1].clientWidth);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('overlay', () => {
|
describe('overlay', () => {
|
||||||
it('should be closed by default', async () => {
|
it('should be closed by default', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(
|
const el = await fixture(html` <lion-select-rich></lion-select-rich> `);
|
||||||
html` <lion-select-rich></lion-select-rich> `,
|
|
||||||
));
|
|
||||||
expect(el.opened).to.be.false;
|
expect(el.opened).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows/hides the listbox via opened attribute', async () => {
|
it('shows/hides the listbox via opened attribute', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(
|
const el = await fixture(html` <lion-select-rich></lion-select-rich> `);
|
||||||
html` <lion-select-rich></lion-select-rich> `,
|
|
||||||
));
|
|
||||||
el.opened = true;
|
el.opened = true;
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
// @ts-ignore allow protected access in tests
|
// @ts-ignore allow protected access in tests
|
||||||
|
|
@ -241,10 +247,8 @@ describe('lion-select-rich', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('syncs opened state with overlay shown', async () => {
|
it('syncs opened state with overlay shown', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(
|
const el = await fixture(html` <lion-select-rich .opened=${true}></lion-select-rich> `);
|
||||||
html` <lion-select-rich .opened=${true}></lion-select-rich> `,
|
const outerEl = /** @type {HTMLButtonElement} */ (await _fixture(
|
||||||
));
|
|
||||||
const outerEl = /** @type {HTMLButtonElement} */ (await fixture(
|
|
||||||
'<button>somewhere</button>',
|
'<button>somewhere</button>',
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
@ -257,9 +261,7 @@ describe('lion-select-rich', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('will focus the listbox on open and invoker on close', async () => {
|
it('will focus the listbox on open and invoker on close', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(
|
const el = await fixture(html` <lion-select-rich></lion-select-rich> `);
|
||||||
html` <lion-select-rich></lion-select-rich> `,
|
|
||||||
));
|
|
||||||
// @ts-ignore allow protected access in tests
|
// @ts-ignore allow protected access in tests
|
||||||
await el._overlayCtrl.show();
|
await el._overlayCtrl.show();
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
|
|
@ -278,12 +280,12 @@ describe('lion-select-rich', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('opens the listbox with checked option as active', async () => {
|
it('opens the listbox with checked option as active', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-select-rich>
|
<lion-select-rich>
|
||||||
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
||||||
<lion-option .choiceValue=${20} checked>Item 2</lion-option>
|
<lion-option .choiceValue=${20} checked>Item 2</lion-option>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`));
|
`);
|
||||||
// @ts-ignore allow protected access in tests
|
// @ts-ignore allow protected access in tests
|
||||||
await el._overlayCtrl.show();
|
await el._overlayCtrl.show();
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
|
|
@ -294,25 +296,25 @@ describe('lion-select-rich', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('stays closed on click if it is disabled or readonly or has a single option', async () => {
|
it('stays closed on click if it is disabled or readonly or has a single option', async () => {
|
||||||
const elReadOnly = /** @type {LionSelectRich} */ (await fixture(html`
|
const elReadOnly = await fixture(html`
|
||||||
<lion-select-rich readonly>
|
<lion-select-rich readonly>
|
||||||
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
||||||
<lion-option .choiceValue=${20} checked>Item 2</lion-option>
|
<lion-option .choiceValue=${20} checked>Item 2</lion-option>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`));
|
`);
|
||||||
|
|
||||||
const elDisabled = /** @type {LionSelectRich} */ (await fixture(html`
|
const elDisabled = await fixture(html`
|
||||||
<lion-select-rich disabled>
|
<lion-select-rich disabled>
|
||||||
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
||||||
<lion-option .choiceValue=${20} checked>Item 2</lion-option>
|
<lion-option .choiceValue=${20} checked>Item 2</lion-option>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`));
|
`);
|
||||||
|
|
||||||
const elSingleoption = /** @type {LionSelectRich} */ (await fixture(html`
|
const elSingleoption = await fixture(html`
|
||||||
<lion-select-rich>
|
<lion-select-rich>
|
||||||
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`));
|
`);
|
||||||
|
|
||||||
// @ts-ignore allow protected access in tests
|
// @ts-ignore allow protected access in tests
|
||||||
elReadOnly._invokerNode.click();
|
elReadOnly._invokerNode.click();
|
||||||
|
|
@ -331,13 +333,13 @@ describe('lion-select-rich', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets inheritsReferenceWidth to min by default', async () => {
|
it('sets inheritsReferenceWidth to min by default', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-select-rich name="favoriteColor" label="Favorite color">
|
<lion-select-rich name="favoriteColor" label="Favorite color">
|
||||||
<lion-option .choiceValue=${'red'}>Red</lion-option>
|
<lion-option .choiceValue=${'red'}>Red</lion-option>
|
||||||
<lion-option .choiceValue=${'hotpink'}>Hotpink</lion-option>
|
<lion-option .choiceValue=${'hotpink'}>Hotpink</lion-option>
|
||||||
<lion-option .choiceValue=${'teal'}>Teal</lion-option>
|
<lion-option .choiceValue=${'teal'}>Teal</lion-option>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`));
|
`);
|
||||||
|
|
||||||
// @ts-ignore allow protected access in tests
|
// @ts-ignore allow protected access in tests
|
||||||
expect(el._overlayCtrl.inheritsReferenceWidth).to.equal('min');
|
expect(el._overlayCtrl.inheritsReferenceWidth).to.equal('min');
|
||||||
|
|
@ -349,26 +351,28 @@ describe('lion-select-rich', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should override the inheritsWidth prop when no default selected feature is used', async () => {
|
it('should override the inheritsWidth prop when no default selected feature is used', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-select-rich name="favoriteColor" label="Favorite color" has-no-default-selected>
|
<lion-select-rich name="favoriteColor" label="Favorite color" has-no-default-selected>
|
||||||
<lion-option .choiceValue=${'red'}>Red</lion-option>
|
<lion-option .choiceValue=${'red'}>Red</lion-option>
|
||||||
<lion-option .choiceValue=${'hotpink'}>Hotpink</lion-option>
|
<lion-option .choiceValue=${'hotpink'}>Hotpink</lion-option>
|
||||||
<lion-option .choiceValue=${'teal'}>Teal</lion-option>
|
<lion-option .choiceValue=${'teal'}>Teal</lion-option>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`));
|
`);
|
||||||
|
|
||||||
|
const { overlay } = getProtectedMembers(el);
|
||||||
|
|
||||||
// The default is min, so we override that behavior here
|
// The default is min, so we override that behavior here
|
||||||
// @ts-ignore allow protected access in tests
|
// @ts-ignore allow protected access in tests
|
||||||
el._overlayCtrl.updateConfig({ inheritsReferenceWidth: 'full' });
|
overlay.updateConfig({ inheritsReferenceWidth: 'full' });
|
||||||
el._initialInheritsReferenceWidth = 'full';
|
el._initialInheritsReferenceWidth = 'full';
|
||||||
|
|
||||||
// @ts-ignore allow protected access in tests
|
// @ts-ignore allow protected access in tests
|
||||||
expect(el._overlayCtrl.inheritsReferenceWidth).to.equal('full');
|
expect(overlay.inheritsReferenceWidth).to.equal('full');
|
||||||
el.opened = true;
|
el.opened = true;
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
// Opens while hasNoDefaultSelected = true, so we expect an override
|
// Opens while hasNoDefaultSelected = true, so we expect an override
|
||||||
// @ts-ignore allow protected access in tests
|
// @ts-ignore allow protected access in tests
|
||||||
expect(el._overlayCtrl.inheritsReferenceWidth).to.equal('min');
|
expect(overlay.inheritsReferenceWidth).to.equal('min');
|
||||||
|
|
||||||
// Emulate selecting hotpink, it closing, and opening it again
|
// Emulate selecting hotpink, it closing, and opening it again
|
||||||
el.modelValue = 'hotpink';
|
el.modelValue = 'hotpink';
|
||||||
|
|
@ -378,7 +382,7 @@ describe('lion-select-rich', () => {
|
||||||
el.opened = true;
|
el.opened = true;
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
await el.updateComplete; // safari takes a little longer
|
await el.updateComplete; // safari takes a little longer
|
||||||
await el._overlayCtrl._showComplete;
|
await overlay._showComplete;
|
||||||
|
|
||||||
// noDefaultSelected will now flip the override back to what was the initial reference width
|
// noDefaultSelected will now flip the override back to what was the initial reference width
|
||||||
// @ts-ignore allow protected access in tests
|
// @ts-ignore allow protected access in tests
|
||||||
|
|
@ -386,12 +390,12 @@ describe('lion-select-rich', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have singleOption only if there is exactly one option', async () => {
|
it('should have singleOption only if there is exactly one option', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-select-rich>
|
<lion-select-rich>
|
||||||
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
||||||
<lion-option .choiceValue=${20}>Item 2</lion-option>
|
<lion-option .choiceValue=${20}>Item 2</lion-option>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`));
|
`);
|
||||||
expect(el.singleOption).to.be.false;
|
expect(el.singleOption).to.be.false;
|
||||||
// @ts-ignore allow protected access in tests
|
// @ts-ignore allow protected access in tests
|
||||||
expect(el._invokerNode.singleOption).to.be.false;
|
expect(el._invokerNode.singleOption).to.be.false;
|
||||||
|
|
@ -418,18 +422,16 @@ describe('lion-select-rich', () => {
|
||||||
|
|
||||||
describe('interaction-mode', () => {
|
describe('interaction-mode', () => {
|
||||||
it('allows to specify an interaction-mode which determines other behaviors', async () => {
|
it('allows to specify an interaction-mode which determines other behaviors', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-select-rich interaction-mode="mac"> </lion-select-rich>
|
<lion-select-rich interaction-mode="mac"> </lion-select-rich>
|
||||||
`));
|
`);
|
||||||
expect(el.interactionMode).to.equal('mac');
|
expect(el.interactionMode).to.equal('mac');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Keyboard navigation', () => {
|
describe('Keyboard navigation', () => {
|
||||||
it('opens the listbox with [Enter] key via click handler', async () => {
|
it('opens the listbox with [Enter] key via click handler', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(
|
const el = await fixture(html` <lion-select-rich> </lion-select-rich> `);
|
||||||
html` <lion-select-rich> </lion-select-rich> `,
|
|
||||||
));
|
|
||||||
// @ts-ignore allow protected access in tests
|
// @ts-ignore allow protected access in tests
|
||||||
el._invokerNode.click();
|
el._invokerNode.click();
|
||||||
await aTimeout(0);
|
await aTimeout(0);
|
||||||
|
|
@ -437,9 +439,7 @@ describe('lion-select-rich', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('opens the listbox with [ ](Space) key via click handler', async () => {
|
it('opens the listbox with [ ](Space) key via click handler', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(
|
const el = await fixture(html` <lion-select-rich> </lion-select-rich> `);
|
||||||
html` <lion-select-rich> </lion-select-rich> `,
|
|
||||||
));
|
|
||||||
// @ts-ignore allow protected access in tests
|
// @ts-ignore allow protected access in tests
|
||||||
el._invokerNode.click();
|
el._invokerNode.click();
|
||||||
await aTimeout(0);
|
await aTimeout(0);
|
||||||
|
|
@ -447,18 +447,14 @@ describe('lion-select-rich', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('closes the listbox with [Escape] key once opened', async () => {
|
it('closes the listbox with [Escape] key once opened', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(
|
const el = await fixture(html` <lion-select-rich opened> </lion-select-rich> `);
|
||||||
html` <lion-select-rich opened> </lion-select-rich> `,
|
|
||||||
));
|
|
||||||
// @ts-ignore allow protected access in tests
|
// @ts-ignore allow protected access in tests
|
||||||
el._listboxNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
|
el._listboxNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
|
||||||
expect(el.opened).to.be.false;
|
expect(el.opened).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('closes the listbox with [Tab] key once opened', async () => {
|
it('closes the listbox with [Tab] key once opened', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(
|
const el = await fixture(html` <lion-select-rich opened> </lion-select-rich> `);
|
||||||
html` <lion-select-rich opened> </lion-select-rich> `,
|
|
||||||
));
|
|
||||||
// tab can only be caught via keydown
|
// tab can only be caught via keydown
|
||||||
// @ts-ignore allow protected access in tests
|
// @ts-ignore allow protected access in tests
|
||||||
el._listboxNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab' }));
|
el._listboxNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab' }));
|
||||||
|
|
@ -468,9 +464,7 @@ describe('lion-select-rich', () => {
|
||||||
|
|
||||||
describe('Mouse navigation', () => {
|
describe('Mouse navigation', () => {
|
||||||
it('opens the listbox via click on invoker', async () => {
|
it('opens the listbox via click on invoker', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(
|
const el = await fixture(html` <lion-select-rich> </lion-select-rich> `);
|
||||||
html` <lion-select-rich> </lion-select-rich> `,
|
|
||||||
));
|
|
||||||
expect(el.opened).to.be.false;
|
expect(el.opened).to.be.false;
|
||||||
// @ts-ignore allow protected access in tests
|
// @ts-ignore allow protected access in tests
|
||||||
el._invokerNode.click();
|
el._invokerNode.click();
|
||||||
|
|
@ -479,11 +473,11 @@ describe('lion-select-rich', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('closes the listbox when an option gets clicked', async () => {
|
it('closes the listbox when an option gets clicked', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-select-rich opened>
|
<lion-select-rich opened>
|
||||||
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`));
|
`);
|
||||||
expect(el.opened).to.be.true;
|
expect(el.opened).to.be.true;
|
||||||
el.formElements[0].click();
|
el.formElements[0].click();
|
||||||
expect(el.opened).to.be.false;
|
expect(el.opened).to.be.false;
|
||||||
|
|
@ -492,9 +486,7 @@ describe('lion-select-rich', () => {
|
||||||
|
|
||||||
describe('Keyboard navigation Windows', () => {
|
describe('Keyboard navigation Windows', () => {
|
||||||
it('closes the listbox with [Enter] key once opened', async () => {
|
it('closes the listbox with [Enter] key once opened', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(
|
const el = await fixture(html` <lion-select-rich opened> </lion-select-rich> `);
|
||||||
html` <lion-select-rich opened> </lion-select-rich> `,
|
|
||||||
));
|
|
||||||
// @ts-ignore allow protected access in tests
|
// @ts-ignore allow protected access in tests
|
||||||
el._listboxNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
|
el._listboxNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
|
||||||
expect(el.opened).to.be.false;
|
expect(el.opened).to.be.false;
|
||||||
|
|
@ -503,12 +495,12 @@ describe('lion-select-rich', () => {
|
||||||
|
|
||||||
describe('Keyboard navigation Mac', () => {
|
describe('Keyboard navigation Mac', () => {
|
||||||
it('checks active item and closes the listbox with [Enter] key via click handler once opened', async () => {
|
it('checks active item and closes the listbox with [Enter] key via click handler once opened', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-select-rich opened interaction-mode="mac">
|
<lion-select-rich opened interaction-mode="mac">
|
||||||
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
||||||
<lion-option .choiceValue=${20}>Item 2</lion-option>
|
<lion-option .choiceValue=${20}>Item 2</lion-option>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`));
|
`);
|
||||||
|
|
||||||
// changes active but not checked
|
// changes active but not checked
|
||||||
el.activeIndex = 1;
|
el.activeIndex = 1;
|
||||||
|
|
@ -520,18 +512,18 @@ describe('lion-select-rich', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('opens the listbox with [ArrowUp] key', async () => {
|
it('opens the listbox with [ArrowUp] key', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-select-rich interaction-mode="mac"> </lion-select-rich>
|
<lion-select-rich interaction-mode="mac"> </lion-select-rich>
|
||||||
`));
|
`);
|
||||||
el.dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowUp' }));
|
el.dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowUp' }));
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
expect(el.opened).to.be.true;
|
expect(el.opened).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('opens the listbox with [ArrowDown] key', async () => {
|
it('opens the listbox with [ArrowDown] key', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-select-rich interaction-mode="mac"> </lion-select-rich>
|
<lion-select-rich interaction-mode="mac"> </lion-select-rich>
|
||||||
`));
|
`);
|
||||||
el.dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowDown' }));
|
el.dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowDown' }));
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
expect(el.opened).to.be.true;
|
expect(el.opened).to.be.true;
|
||||||
|
|
@ -540,12 +532,12 @@ describe('lion-select-rich', () => {
|
||||||
|
|
||||||
describe('Accessibility', () => {
|
describe('Accessibility', () => {
|
||||||
it('has the right references to its inner elements', async () => {
|
it('has the right references to its inner elements', async () => {
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(html`
|
const el = await fixture(html`
|
||||||
<lion-select-rich label="age">
|
<lion-select-rich label="age">
|
||||||
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
<lion-option .choiceValue=${10}>Item 1</lion-option>
|
||||||
<lion-option .choiceValue=${20}>Item 2</lion-option>
|
<lion-option .choiceValue=${20}>Item 2</lion-option>
|
||||||
</lion-select-rich>
|
</lion-select-rich>
|
||||||
`));
|
`);
|
||||||
const { invoker, feedback, label, helpText } = getProtectedMembers(el);
|
const { invoker, feedback, label, helpText } = getProtectedMembers(el);
|
||||||
|
|
||||||
expect(invoker.getAttribute('aria-labelledby')).to.contain(label.id);
|
expect(invoker.getAttribute('aria-labelledby')).to.contain(label.id);
|
||||||
|
|
@ -557,9 +549,7 @@ describe('lion-select-rich', () => {
|
||||||
|
|
||||||
it('notifies when the listbox is expanded or not', async () => {
|
it('notifies when the listbox is expanded or not', async () => {
|
||||||
// smoke test for overlay functionality
|
// smoke test for overlay functionality
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(
|
const el = await fixture(html` <lion-select-rich> </lion-select-rich> `);
|
||||||
html` <lion-select-rich> </lion-select-rich> `,
|
|
||||||
));
|
|
||||||
const { invoker } = getProtectedMembers(el);
|
const { invoker } = getProtectedMembers(el);
|
||||||
|
|
||||||
expect(invoker.getAttribute('aria-expanded')).to.equal('false');
|
expect(invoker.getAttribute('aria-expanded')).to.equal('false');
|
||||||
|
|
@ -619,9 +609,9 @@ describe('lion-select-rich', () => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const mySelectContainerTag = unsafeStatic(mySelectContainerTagString);
|
const mySelectContainerTag = unsafeStatic(mySelectContainerTagString);
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(html`
|
const el = await fixture(html`
|
||||||
<${mySelectContainerTag}></${mySelectContainerTag}>
|
<${mySelectContainerTag}></${mySelectContainerTag}>
|
||||||
`));
|
`);
|
||||||
|
|
||||||
const selectRich = /** @type {LionSelectRich} */ (
|
const selectRich = /** @type {LionSelectRich} */ (
|
||||||
/** @type {ShadowRoot} */ (el.shadowRoot).querySelector('lion-select-rich')
|
/** @type {ShadowRoot} */ (el.shadowRoot).querySelector('lion-select-rich')
|
||||||
|
|
@ -667,7 +657,7 @@ describe('lion-select-rich', () => {
|
||||||
|
|
||||||
const mySelectTag = unsafeStatic(mySelectTagString);
|
const mySelectTag = unsafeStatic(mySelectTagString);
|
||||||
|
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(html`
|
const el = await fixture(html`
|
||||||
<${mySelectTag} label="Favorite color" name="color">
|
<${mySelectTag} label="Favorite color" name="color">
|
||||||
|
|
||||||
${Array(2).map(
|
${Array(2).map(
|
||||||
|
|
@ -677,7 +667,7 @@ describe('lion-select-rich', () => {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</${mySelectTag}>
|
</${mySelectTag}>
|
||||||
`));
|
`);
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
// @ts-ignore allow protected member access in tests
|
// @ts-ignore allow protected member access in tests
|
||||||
expect(el._overlayCtrl.placementMode).to.equal('global');
|
expect(el._overlayCtrl.placementMode).to.equal('global');
|
||||||
|
|
@ -708,7 +698,7 @@ describe('lion-select-rich', () => {
|
||||||
);
|
);
|
||||||
const selectTag = unsafeStatic(selectTagName);
|
const selectTag = unsafeStatic(selectTagName);
|
||||||
|
|
||||||
const el = /** @type {LionSelectRich} */ (await fixture(html`
|
const el = await fixture(html`
|
||||||
<${selectTag} id="color" name="color" label="Favorite color" has-no-default-selected>
|
<${selectTag} id="color" name="color" label="Favorite color" has-no-default-selected>
|
||||||
|
|
||||||
<lion-option .choiceValue=${'red'}>Red</lion-option>
|
<lion-option .choiceValue=${'red'}>Red</lion-option>
|
||||||
|
|
@ -716,7 +706,7 @@ describe('lion-select-rich', () => {
|
||||||
<lion-option .choiceValue=${'teal'}>Teal</lion-option>
|
<lion-option .choiceValue=${'teal'}>Teal</lion-option>
|
||||||
|
|
||||||
</${selectTag}>
|
</${selectTag}>
|
||||||
`));
|
`);
|
||||||
const { invoker } = getProtectedMembers(el);
|
const { invoker } = getProtectedMembers(el);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import { css, html, ScopedElementsMixin } from '@lion/core';
|
||||||
import { ChoiceInputMixin, LionField } from '@lion/form-core';
|
import { ChoiceInputMixin, LionField } from '@lion/form-core';
|
||||||
import { LionSwitchButton } from './LionSwitchButton.js';
|
import { LionSwitchButton } from './LionSwitchButton.js';
|
||||||
|
|
||||||
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/40110
|
|
||||||
export class LionSwitch extends ScopedElementsMixin(ChoiceInputMixin(LionField)) {
|
export class LionSwitch extends ScopedElementsMixin(ChoiceInputMixin(LionField)) {
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return [
|
return [
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,6 @@
|
||||||
"**/dist/**/*",
|
"**/dist/**/*",
|
||||||
"packages/**/test-helpers",
|
"packages/**/test-helpers",
|
||||||
"packages/**/docs/**/*",
|
"packages/**/docs/**/*",
|
||||||
"packages/combobox/test/**/*.js", // TODO: Needs to get typed!
|
|
||||||
"packages/form-integrations/test/**/*.js", // TODO: Needs to get typed!
|
|
||||||
"packages/overlays/test/utils-tests/**/*.js", // TODO: Needs to get typed!
|
|
||||||
"packages/select-rich/test/**/*.js", // TODO: Needs to get typed!
|
|
||||||
"packages/singleton-manager/demo/",
|
"packages/singleton-manager/demo/",
|
||||||
"packages/singleton-manager/test/",
|
"packages/singleton-manager/test/",
|
||||||
// ignore test/demos for singleton manager until overlays are typed as it's used in there
|
// ignore test/demos for singleton manager until overlays are typed as it's used in there
|
||||||
|
|
|
||||||
|
|
@ -1834,10 +1834,10 @@
|
||||||
"@open-wc/rollup-plugin-html" "^1.2.5"
|
"@open-wc/rollup-plugin-html" "^1.2.5"
|
||||||
polyfills-loader "^1.7.5"
|
polyfills-loader "^1.7.5"
|
||||||
|
|
||||||
"@open-wc/scoped-elements@^1.2.4", "@open-wc/scoped-elements@^1.3.2":
|
"@open-wc/scoped-elements@^1.2.4", "@open-wc/scoped-elements@^1.3.3":
|
||||||
version "1.3.2"
|
version "1.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/@open-wc/scoped-elements/-/scoped-elements-1.3.2.tgz#6ae54c49731bbe8c3e0b5383c989f983dcdfacf5"
|
resolved "https://registry.yarnpkg.com/@open-wc/scoped-elements/-/scoped-elements-1.3.3.tgz#fe008aef4d74fb00c553c900602960638fc1c7b0"
|
||||||
integrity sha512-DoP3XA8r03tGx+IrlJwP/voLuDFkyS56kvwhmXIhpESo7M5jMt5e0zScNrawj7EMe4b5gDaJjorx2Jza8FLaLw==
|
integrity sha512-vFIQVYYjFw67odUE4JzZOpctnF7S/2DX+S+clrL3bQPql7HvEnV0wMFwOWUavQTuCJi0rfU8GTcNMiUybio+Yg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@open-wc/dedupe-mixin" "^1.3.0"
|
"@open-wc/dedupe-mixin" "^1.3.0"
|
||||||
lit-html "^1.0.0"
|
lit-html "^1.0.0"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue