fix(form-core): type enhancements

This commit is contained in:
Thijs Louisse 2020-09-07 12:36:05 +02:00 committed by Thomas Allmer
parent 0ec72ac330
commit b83ef4eeeb
10 changed files with 84 additions and 40 deletions

View file

@ -1,7 +0,0 @@
---
'@lion/button': patch
'@lion/overlays': patch
'@lion/tooltip': patch
---
Types for overlays, tooltip, button

View file

@ -1,9 +0,0 @@
---
'@lion/core': minor
---
EventTargetShim
#### Features
EventTargetShim is a base class that works like EventTarget, on all browsers.

View file

@ -40,11 +40,26 @@ const FormControlMixinImplementation = superclass =>
reflect: true, reflect: true,
}, },
/** /**
* When no light dom defined and prop set * A Boolean attribute which, if present, indicates that the user should not be able to edit
* the value of the input. The difference between disabled and readonly is that read-only
* controls can still function, whereas disabled controls generally do not function as
* controls until they are enabled.
*
* (From: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-readonly)
*/
readOnly: {
type: Boolean,
attribute: 'readonly',
reflect: true,
},
/**
* The label text for the input node.
* When no light dom defined via [slot=label], this value will be used
*/ */
label: String, // FIXME: { attribute: false } breaks a bunch of tests, but shouldn't... label: String, // FIXME: { attribute: false } breaks a bunch of tests, but shouldn't...
/** /**
* When no light dom defined and prop set * The helpt text for the input node.
* When no light dom defined via [slot=help-text], this value will be used
*/ */
helpText: { helpText: {
type: String, type: String,

View file

@ -7,6 +7,7 @@ import { InteractionStateMixin } from '../InteractionStateMixin.js';
* @typedef {import('../../types/FormControlMixinTypes').FormControlHost} FormControlHost * @typedef {import('../../types/FormControlMixinTypes').FormControlHost} FormControlHost
* @typedef {import('../../types/registration/FormRegistrarMixinTypes').ElementWithParentFormGroup} ElementWithParentFormGroup * @typedef {import('../../types/registration/FormRegistrarMixinTypes').ElementWithParentFormGroup} ElementWithParentFormGroup
* @typedef {FormControlHost & HTMLElement & {__parentFormGroup?:HTMLElement, checked?:boolean}} FormControl * @typedef {FormControlHost & HTMLElement & {__parentFormGroup?:HTMLElement, checked?:boolean}} FormControl
* @typedef {import('../../types/choice-group/ChoiceInputMixinTypes').ChoiceInputHost} ChoiceInputHost
*/ */
/** /**
@ -14,6 +15,7 @@ import { InteractionStateMixin } from '../InteractionStateMixin.js';
* @param {import('@open-wc/dedupe-mixin').Constructor<import('@lion/core').LitElement>} superclass * @param {import('@open-wc/dedupe-mixin').Constructor<import('@lion/core').LitElement>} superclass
*/ */
const ChoiceGroupMixinImplementation = superclass => const ChoiceGroupMixinImplementation = superclass =>
// @ts-expect-error
class ChoiceGroupMixin extends FormRegistrarMixin(InteractionStateMixin(superclass)) { class ChoiceGroupMixin extends FormRegistrarMixin(InteractionStateMixin(superclass)) {
static get properties() { static get properties() {
return { return {
@ -32,25 +34,27 @@ const ChoiceGroupMixinImplementation = superclass =>
get modelValue() { get modelValue() {
const elems = this._getCheckedElements(); const elems = this._getCheckedElements();
if (this.multipleChoice) { if (this.multipleChoice) {
return elems.map(el => el.modelValue.value); return elems.map(el => el.choiceValue);
} }
return elems[0] ? elems[0].modelValue.value : ''; return elems[0] ? elems[0].choiceValue : '';
} }
set modelValue(value) { set modelValue(value) {
/** /**
* @param {{ modelValue: { value: any; }; }} el * @param {ChoiceInputHost} el
* @param {any} val * @param {any} val
*/ */
const checkCondition = (el, val) => el.modelValue.value === val; const checkCondition = (el, val) => el.choiceValue === val;
if (this.__isInitialModelValue) { if (this.__isInitialModelValue) {
this.__isInitialModelValue = false; this.__isInitialModelValue = false;
this.registrationComplete.then(() => { this.registrationComplete.then(() => {
this._setCheckedElements(value, checkCondition); this._setCheckedElements(value, checkCondition);
}); });
this.requestUpdate('modelValue');
} else { } else {
this._setCheckedElements(value, checkCondition); this._setCheckedElements(value, checkCondition);
this.requestUpdate('modelValue');
} }
} }
@ -72,7 +76,7 @@ const ChoiceGroupMixinImplementation = superclass =>
set serializedValue(value) { set serializedValue(value) {
/** /**
* @param {{ serializedValue: { value: any; }; }} el * @param {ChoiceInputHost} el
* @param {string} val * @param {string} val
*/ */
const checkCondition = (el, val) => el.serializedValue.value === val; const checkCondition = (el, val) => el.serializedValue.value === val;
@ -81,9 +85,11 @@ const ChoiceGroupMixinImplementation = superclass =>
this.__isInitialSerializedValue = false; this.__isInitialSerializedValue = false;
this.registrationComplete.then(() => { this.registrationComplete.then(() => {
this._setCheckedElements(value, checkCondition); this._setCheckedElements(value, checkCondition);
this.requestUpdate('serializedValue');
}); });
} else { } else {
this._setCheckedElements(value, checkCondition); this._setCheckedElements(value, checkCondition);
this.requestUpdate('serializedValue');
} }
} }

View file

@ -6,9 +6,6 @@ import { FormatMixin } from '../FormatMixin.js';
/** /**
* @typedef {import('../../types/FormControlMixinTypes').FormControlHost} FormControlHost * @typedef {import('../../types/FormControlMixinTypes').FormControlHost} FormControlHost
* @typedef {FormControlHost & HTMLElement & {__parentFormGroup?:HTMLElement, checked?:boolean}} FormControl * @typedef {FormControlHost & HTMLElement & {__parentFormGroup?:HTMLElement, checked?:boolean}} FormControl
*/
/**
* @typedef {import('../../types/choice-group/ChoiceInputMixinTypes').ChoiceInputMixin} ChoiceInputMixin * @typedef {import('../../types/choice-group/ChoiceInputMixinTypes').ChoiceInputMixin} ChoiceInputMixin
* @typedef {import('../../types/choice-group/ChoiceInputMixinTypes').ChoiceInputModelValue} ChoiceInputModelValue * @typedef {import('../../types/choice-group/ChoiceInputMixinTypes').ChoiceInputModelValue} ChoiceInputModelValue
*/ */

View file

@ -32,7 +32,6 @@ function arrayDiff(array1 = [], array2 = []) {
* @param {import('@open-wc/dedupe-mixin').Constructor<import('@lion/core').LitElement>} superclass * @param {import('@open-wc/dedupe-mixin').Constructor<import('@lion/core').LitElement>} superclass
*/ */
export const ValidateMixinImplementation = superclass => export const ValidateMixinImplementation = superclass =>
// eslint-disable-next-line no-unused-vars, no-shadow
class extends FormControlMixin( class extends FormControlMixin(
SyncUpdatableMixin(DisabledMixin(SlotMixin(ScopedElementsMixin(superclass)))), SyncUpdatableMixin(DisabledMixin(SlotMixin(ScopedElementsMixin(superclass)))),
) { ) {

View file

@ -10,15 +10,6 @@ import { ChoiceInputMixin } from '../../src/choice-group/ChoiceInputMixin.js';
* @typedef {import('../../types/choice-group/ChoiceInputMixinTypes').ChoiceInputHost} ChoiceInputHost * @typedef {import('../../types/choice-group/ChoiceInputMixinTypes').ChoiceInputHost} ChoiceInputHost
*/ */
// class InputField extends LionField {
// get slots() {
// return {
// ...super.slots,
// input: () => document.createElement('input'),
// };
// }
// }
describe('ChoiceInputMixin', () => { describe('ChoiceInputMixin', () => {
/** @typedef {Element & ChoiceClass} ChoiceInput */ /** @typedef {Element & ChoiceClass} ChoiceInput */
class ChoiceClass extends ChoiceInputMixin(LionInput) { class ChoiceClass extends ChoiceInputMixin(LionInput) {

View file

@ -8,14 +8,45 @@ import { FormRegisteringHost } from './registration/FormRegisteringMixinTypes';
export class FormControlHost { export class FormControlHost {
static get styles(): CSSResult | CSSResult[]; static get styles(): CSSResult | CSSResult[];
/**
* A Boolean attribute which, if present, indicates that the user should not be able to edit
* the value of the input. The difference between disabled and readonly is that read-only
* controls can still function, whereas disabled controls generally do not function as
* controls until they are enabled.
* (From: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-readonly)
*/
readOnly: boolean;
/**
* The name the element will be registered on to the .formElements collection
* of the parent.
*/
name: string; name: string;
/**
* The model value is the result of the parser function(when available).
* It should be considered as the internal value used for validation and reasoning/logic.
* The model value is 'ready for consumption' by the outside world (think of a Date
* object or a float). The modelValue can(and is recommended to) be used as both input
* value and output value of the `LionField`.
*
* Examples:
* - For a date input: a String '20/01/1999' will be converted to new Date('1999/01/20')
* - For a number input: a formatted String '1.234,56' will be converted to a Number:
* 1234.56
*/
modelValue: unknown; modelValue: unknown;
set label(arg: string); /**
* The label text for the input node.
* When no light dom defined via [slot=label], this value will be used
*/
get label(): string; get label(): string;
set label(arg: string);
__label: string | undefined; __label: string | undefined;
set helpText(arg: string); /**
* The helpt text for the input node.
* When no light dom defined via [slot=help-text], this value will be used
*/
get helpText(): string; get helpText(): string;
set helpText(arg: string);
__helpText: string | undefined; __helpText: string | undefined;
set fieldName(arg: string); set fieldName(arg: string);
get fieldName(): string; get fieldName(): string;
@ -28,7 +59,20 @@ export class FormControlHost {
_inputId: string; _inputId: string;
_ariaLabelledNodes: HTMLElement[]; _ariaLabelledNodes: HTMLElement[];
_ariaDescribedNodes: HTMLElement[]; _ariaDescribedNodes: HTMLElement[];
_repropagationRole: string; // 'child' | 'choice-group' | 'fieldset'; /**
* Based on the role, details of handling model-value-changed repropagation differ.
*/
_repropagationRole: 'child' | 'choice-group' | 'fieldset';
/**
* By default, a field with _repropagationRole 'choice-group' will act as an
* 'endpoint'. This means it will be considered as an individual field: for
* a select, individual options will not be part of the formPath. They
* will.
* Similarly, components that (a11y wise) need to be fieldsets, but 'interaction wise'
* (from Application Developer perspective) need to be more like fields
* (think of an amount-input with a currency select box next to it), can set this
* to true to hide private internals in the formPath.
*/
_isRepropagationEndpoint: boolean; _isRepropagationEndpoint: boolean;
connectedCallback(): void; connectedCallback(): void;

View file

@ -7,8 +7,15 @@ export interface ChoiceInputModelValue {
value: any; value: any;
} }
/** TODO: legacy code: serialization should happen on choice-group level */
export interface ChoiceInputSerializedValue {
checked: boolean;
value: string;
}
export declare class ChoiceInputHost { export declare class ChoiceInputHost {
modelValue: ChoiceInputModelValue; modelValue: ChoiceInputModelValue;
serializedValue: ChoiceInputSerializedValue;
get choiceValue(): any; get choiceValue(): any;

View file

@ -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 {
registrationTarget: HTMLElement;
__redispatchEventForFormRegistrarPortalMixin(ev: CustomEvent): void; __redispatchEventForFormRegistrarPortalMixin(ev: CustomEvent): void;
} }