@lion/form-core
Advanced tools
Comparing version 0.6.3 to 0.6.4
# Change Log | ||
## 0.6.4 | ||
### Patch Changes | ||
- 2907649b: filter feedback messages according feedback conditions | ||
- 68e3e749: Add missing interaction states that could act as feedback conditions. Fix the interactive demo that showcases dynamic feedback conditions. | ||
- fd297a28: Properly update formElements when the name attribute changes, in order to get an updated serializedValue. | ||
- 9fcb67f0: Allow flexibility for extending the repropagation prevention conditions, which is needed for combobox, so that a model-value-changed event is propagated when no option matches after an input change. This allows validation to work properly e.g. for Required. | ||
- 247e64a3: Ensure that the name of a child choice field is always synced with the parent choice field(set) when it changes. No longer error when a child is added with a different name than the parent, simply sync it. | ||
- Updated dependencies [7682e520] | ||
- Updated dependencies [e92b98a4] | ||
- @lion/localize@0.14.6 | ||
- @lion/core@0.13.1 | ||
## 0.6.3 | ||
@@ -4,0 +18,0 @@ |
{ | ||
"name": "@lion/form-core", | ||
"version": "0.6.3", | ||
"version": "0.6.4", | ||
"description": "Form-core contains all essential building blocks for creating form fields and fieldsets", | ||
@@ -26,5 +26,7 @@ "license": "MIT", | ||
"scripts": { | ||
"debug": "cd ../../ && yarn debug --group form-core", | ||
"debug:firefox": "cd ../../ && yarn debug:firefox --group form-core", | ||
"debug:webkit": "cd ../../ && yarn debug:webkit --group form-core", | ||
"prepublishOnly": "../../scripts/npm-prepublish.js", | ||
"test": "cd ../../ && yarn test:browser --grep \"packages/form-core/test/**/*.test.js\"", | ||
"test:watch": "cd ../../ && yarn test:browser:watch --grep \"packages/form-core/test/**/*.test.js\"" | ||
"test": "cd ../../ && yarn test:browser --group form-core" | ||
}, | ||
@@ -36,4 +38,4 @@ "sideEffects": [ | ||
"dependencies": { | ||
"@lion/core": "0.13.0", | ||
"@lion/localize": "0.14.5" | ||
"@lion/core": "0.13.1", | ||
"@lion/localize": "0.14.6" | ||
}, | ||
@@ -40,0 +42,0 @@ "keywords": [ |
@@ -18,3 +18,3 @@ import { dedupeMixin } from '@lion/core'; | ||
const ChoiceGroupMixinImplementation = superclass => | ||
// @ts-expect-error | ||
// @ts-expect-error false positive for incompatible static get properties. Lit-element merges super properties already for you. | ||
class ChoiceGroupMixin extends FormRegistrarMixin(InteractionStateMixin(superclass)) { | ||
@@ -54,4 +54,4 @@ static get properties() { | ||
this._setCheckedElements(value, checkCondition); | ||
this.requestUpdate('modelValue'); | ||
}); | ||
this.requestUpdate('modelValue'); | ||
} else { | ||
@@ -126,2 +126,3 @@ this._setCheckedElements(value, checkCondition); | ||
this.multipleChoice = false; | ||
/** @type {'child'|'choice-group'|'fieldset'} */ | ||
this._repropagationRole = 'choice-group'; // configures event propagation logic of FormControlMixin | ||
@@ -162,2 +163,13 @@ | ||
/** @param {import('lit-element').PropertyValues} changedProperties */ | ||
updated(changedProperties) { | ||
super.updated(changedProperties); | ||
if (changedProperties.has('name') && this.name !== changedProperties.get('name')) { | ||
this.formElements.forEach(child => { | ||
// eslint-disable-next-line no-param-reassign | ||
child.name = this.name; | ||
}); | ||
} | ||
} | ||
disconnectedCallback() { | ||
@@ -178,3 +190,4 @@ super.disconnectedCallback(); | ||
this._throwWhenInvalidChildModelValue(child); | ||
this.__delegateNameAttribute(child); | ||
// eslint-disable-next-line no-param-reassign | ||
child.name = this.name; | ||
super.addFormElement(child, indexToInsertAt); | ||
@@ -292,20 +305,2 @@ } | ||
/** | ||
* @param {FormControl} child | ||
*/ | ||
__delegateNameAttribute(child) { | ||
if (!child.name || child.name === this.name) { | ||
// eslint-disable-next-line no-param-reassign | ||
child.name = this.name; | ||
} else { | ||
throw new Error( | ||
`The ${this.tagName.toLowerCase()} name="${ | ||
this.name | ||
}" does not allow to register ${child.tagName.toLowerCase()} with custom names (name="${ | ||
child.name | ||
}" given)`, | ||
); | ||
} | ||
} | ||
/** | ||
* @override FormControlMixin | ||
@@ -312,0 +307,0 @@ * @param {CustomEvent} ev |
@@ -24,2 +24,3 @@ /* eslint-disable class-methods-use-this */ | ||
const ChoiceInputMixinImplementation = superclass => | ||
// @ts-expect-error false positive for incompatible static get properties. Lit-element merges super properties already for you. | ||
class ChoiceInputMixin extends FormatMixin(superclass) { | ||
@@ -114,2 +115,13 @@ static get properties() { | ||
} | ||
if ( | ||
changedProperties.has('name') && | ||
// @ts-expect-error not all choice inputs have a parent form group, since this mixin does not have a strict contract with the registration system | ||
this.__parentFormGroup && | ||
// @ts-expect-error | ||
this.__parentFormGroup.name !== this.name | ||
) { | ||
// @ts-expect-error not all choice inputs have a name prop, because this mixin does not have a strict contract with form control mixin | ||
this.name = changedProperties.get('name'); | ||
} | ||
} | ||
@@ -116,0 +128,0 @@ |
@@ -9,2 +9,3 @@ import { dedupeMixin } from '@lion/core'; | ||
const FocusMixinImplementation = superclass => | ||
// @ts-expect-error false positive for incompatible static get properties. Lit-element merges super properties already for you. | ||
// eslint-disable-next-line no-unused-vars, max-len, no-shadow | ||
@@ -11,0 +12,0 @@ class FocusMixin extends FormControlMixin(superclass) { |
@@ -32,2 +32,3 @@ import { dedupeMixin, html, SlotMixin } from '@lion/core'; | ||
const FormGroupMixinImplementation = superclass => | ||
// @ts-expect-error false positive for incompatible static get properties. Lit-element merges super properties already for you. | ||
class FormGroupMixin extends FormRegistrarMixin( | ||
@@ -34,0 +35,0 @@ FormControlMixin(ValidateMixin(DisabledMixin(SlotMixin(superclass)))), |
@@ -60,2 +60,3 @@ /* eslint-disable class-methods-use-this */ | ||
const FormatMixinImplementation = superclass => | ||
// @ts-expect-error false positive for incompatible static get properties. Lit-element merges super properties already for you. | ||
class FormatMixin extends ValidateMixin(FormControlMixin(superclass)) { | ||
@@ -62,0 +63,0 @@ static get properties() { |
@@ -26,4 +26,4 @@ import { css, dedupeMixin, html, nothing, SlotMixin } from '@lion/core'; | ||
* @typedef {import('../types/FormControlMixinTypes.js').FormControlMixin} FormControlMixin | ||
* @param {import('@open-wc/dedupe-mixin').Constructor<import('@lion/core').LitElement>} superclass | ||
* @type {FormControlMixin} | ||
* @param {import('@open-wc/dedupe-mixin').Constructor<import('@lion/core').LitElement>} superclass | ||
*/ | ||
@@ -148,4 +148,3 @@ const FormControlMixinImplementation = superclass => | ||
get fieldName() { | ||
// @ts-expect-error | ||
return this.__fieldName || this.label || this.name; // FIXME: when LionField is typed we can inherit this prop | ||
return this.__fieldName || this.label || this.name || ''; | ||
} | ||
@@ -200,2 +199,4 @@ | ||
super(); | ||
/** @type {string | undefined} */ | ||
this.name = undefined; | ||
/** @type {string} */ | ||
@@ -263,2 +264,11 @@ this._inputId = uuid(this.localName); | ||
} | ||
if (changedProperties.has('name')) { | ||
this.dispatchEvent( | ||
new CustomEvent('form-element-name-changed', { | ||
detail: { oldName: changedProperties.get('name'), newName: this.name }, | ||
bubbles: true, | ||
}), | ||
); | ||
} | ||
} | ||
@@ -633,3 +643,3 @@ | ||
:host([disabled]) .form-field__help-text ::slotted(*) { | ||
color: var(--disabled-text-color, #adadad); | ||
color: var(--disabled-text-color, #767676); | ||
} | ||
@@ -652,3 +662,3 @@ | ||
:host([disabled]) .input-group ::slotted(slot='input') { | ||
color: var(--disabled-text-color, #adadad); | ||
color: var(--disabled-text-color, #767676); | ||
} | ||
@@ -793,3 +803,5 @@ | ||
// B2. Are we a single choice choice-group? If so, halt when unchecked | ||
// B2. Are we a single choice choice-group? If so, halt when target unchecked | ||
// and something else is checked, meaning we will get | ||
// another model-value-changed dispatch for the checked target | ||
// | ||
@@ -799,4 +811,3 @@ // We only send the checked changed up (not the unchecked). In this way a choice group | ||
// just like the native <select> | ||
// @ts-expect-error multipleChoice is not directly available but only as side effect | ||
if (this._repropagationRole === 'choice-group' && !this.multipleChoice && !target.checked) { | ||
if (!this._repropagationCondition(target)) { | ||
return; | ||
@@ -824,2 +835,16 @@ } | ||
/** | ||
* TODO: Extend this in choice group so that target is always a choice input and multipleChoice exists. | ||
* This will fix the types and reduce the need for ignores/expect-errors | ||
* @param {EventTarget & import('../types/choice-group/ChoiceInputMixinTypes').ChoiceInputHost} target | ||
*/ | ||
_repropagationCondition(target) { | ||
return !( | ||
this._repropagationRole === 'choice-group' && | ||
// @ts-expect-error multipleChoice is not directly available but only as side effect | ||
!this.multipleChoice && | ||
!target.checked | ||
); | ||
} | ||
/** | ||
* @overridable | ||
@@ -826,0 +851,0 @@ * A Subclasser should only override this method if the interactive element |
@@ -21,2 +21,3 @@ import { dedupeMixin } from '@lion/core'; | ||
const InteractionStateMixinImplementation = superclass => | ||
// @ts-expect-error false positive for incompatible static get properties. Lit-element merges super properties already for you. | ||
class InteractionStateMixin extends FormControlMixin(superclass) { | ||
@@ -23,0 +24,0 @@ static get properties() { |
@@ -1,2 +0,2 @@ | ||
declare const LionField_base: typeof LitElement & import("@open-wc/dedupe-mixin").Constructor<import("@lion/core/types/SlotMixinTypes").SlotHost> & import("@open-wc/dedupe-mixin").Constructor<import("../types/validate/ValidateMixinTypes.js").ValidateHost> & typeof import("../types/validate/ValidateMixinTypes.js").ValidateHost & import("@open-wc/dedupe-mixin").Constructor<import("../types/FormControlMixinTypes.js").FormControlHost> & typeof import("../types/FormControlMixinTypes.js").FormControlHost & import("@open-wc/dedupe-mixin").Constructor<import("../types/utils/SyncUpdatableMixinTypes.js").SyncUpdatableHost> & typeof import("../types/utils/SyncUpdatableMixinTypes.js").SyncUpdatableHost & import("@open-wc/dedupe-mixin").Constructor<import("@lion/core/types/DisabledMixinTypes").DisabledHost> & typeof import("@lion/core/types/DisabledMixinTypes").DisabledHost & typeof import("@lion/core/types/SlotMixinTypes").SlotHost & import("@open-wc/dedupe-mixin").Constructor<import("@open-wc/scoped-elements/src/types").ScopedElementsHost> & typeof import("@open-wc/scoped-elements/src/types").ScopedElementsHost & import("@open-wc/dedupe-mixin").Constructor<import("../types/FormatMixinTypes.js").FormatHost> & import("../types/FormatMixinTypes.js").FormatHost & import("@open-wc/dedupe-mixin").Constructor<import("../types/FocusMixinTypes.js").FocusHost> & import("../types/FocusMixinTypes.js").FocusHost & import("@open-wc/dedupe-mixin").Constructor<import("../types/InteractionStateMixinTypes.js").InteractionStateHost> & import("../types/InteractionStateMixinTypes.js").InteractionStateHost & import("../types/FormControlMixinTypes.js").FormControlHost & import("@open-wc/dedupe-mixin").Constructor<import("../types/registration/FormRegisteringMixinTypes.js").FormRegisteringHost> & typeof import("../types/registration/FormRegisteringMixinTypes.js").FormRegisteringHost; | ||
declare const LionField_base: typeof LitElement & import("@open-wc/dedupe-mixin").Constructor<import("@lion/core/types/SlotMixinTypes").SlotHost> & import("@open-wc/dedupe-mixin").Constructor<import("../types/validate/ValidateMixinTypes.js").ValidateHost> & typeof import("../types/validate/ValidateMixinTypes.js").ValidateHost & import("@open-wc/dedupe-mixin").Constructor<import("../types/FormControlMixinTypes.js").FormControlHost> & typeof import("../types/FormControlMixinTypes.js").FormControlHost & import("@open-wc/dedupe-mixin").Constructor<import("../types/utils/SyncUpdatableMixinTypes.js").SyncUpdatableHost> & typeof import("../types/utils/SyncUpdatableMixinTypes.js").SyncUpdatableHost & import("@open-wc/dedupe-mixin").Constructor<import("@lion/core/types/DisabledMixinTypes").DisabledHost> & typeof import("@lion/core/types/DisabledMixinTypes").DisabledHost & typeof import("@lion/core/types/SlotMixinTypes").SlotHost & import("@open-wc/dedupe-mixin").Constructor<import("@open-wc/scoped-elements/src/types").ScopedElementsHost> & typeof import("@open-wc/scoped-elements/src/types").ScopedElementsHost & import("@open-wc/dedupe-mixin").Constructor<import("../types/FormatMixinTypes.js").FormatHost> & import("../types/FormatMixinTypes.js").FormatHost & import("@open-wc/dedupe-mixin").Constructor<import("../types/FocusMixinTypes.js").FocusHost> & import("../types/FocusMixinTypes.js").FocusHost & import("@open-wc/dedupe-mixin").Constructor<import("../types/InteractionStateMixinTypes.js").InteractionStateHost> & import("../types/InteractionStateMixinTypes.js").InteractionStateHost & import("@open-wc/dedupe-mixin").Constructor<import("../types/registration/FormRegisteringMixinTypes.js").FormRegisteringHost> & typeof import("../types/registration/FormRegisteringMixinTypes.js").FormRegisteringHost; | ||
/** | ||
@@ -3,0 +3,0 @@ * `LionField`: wraps <input>, <textarea>, <select> and other interactable elements. |
@@ -53,2 +53,3 @@ // eslint-disable-next-line max-classes-per-file | ||
this._onRequestToAddFormElement = this._onRequestToAddFormElement.bind(this); | ||
this._onRequestToChangeFormElementName = this._onRequestToChangeFormElementName.bind(this); | ||
@@ -59,2 +60,6 @@ this.addEventListener( | ||
); | ||
this.addEventListener( | ||
'form-element-name-changed', | ||
/** @type {EventListenerOrEventListenerObject} */ (this._onRequestToChangeFormElementName), | ||
); | ||
} | ||
@@ -171,2 +176,13 @@ | ||
*/ | ||
_onRequestToChangeFormElementName(ev) { | ||
const element = this.formElements[ev.detail.oldName]; | ||
if (element) { | ||
this.formElements[ev.detail.newName] = element; | ||
delete this.formElements[ev.detail.oldName]; | ||
} | ||
} | ||
/** | ||
* @param {CustomEvent} ev | ||
*/ | ||
_onRequestToRemoveFormElement(ev) { | ||
@@ -173,0 +189,0 @@ const child = ev.detail.element; |
@@ -0,3 +1,10 @@ | ||
/* eslint-disable no-bitwise */ | ||
import { browserDetection } from '@lion/core'; | ||
const moveDownConditions = [ | ||
Node.DOCUMENT_POSITION_PRECEDING, | ||
Node.DOCUMENT_POSITION_CONTAINS, | ||
Node.DOCUMENT_POSITION_CONTAINS | Node.DOCUMENT_POSITION_PRECEDING, | ||
]; | ||
/** | ||
@@ -22,4 +29,5 @@ * @desc Let the order of adding ids to aria element by DOM order, so that the screen reader | ||
const pos = a.compareDocumentPosition(b); | ||
// Unfortunately, for IE, we have to switch the order (?) | ||
if (pos === Node.DOCUMENT_POSITION_PRECEDING || pos === Node.DOCUMENT_POSITION_CONTAINED_BY) { | ||
if (moveDownConditions.includes(pos)) { | ||
return browserDetection.isIE11 ? -1 : 1; | ||
@@ -26,0 +34,0 @@ } |
@@ -35,2 +35,3 @@ /* eslint-disable class-methods-use-this, camelcase, no-param-reassign, max-classes-per-file */ | ||
export const ValidateMixinImplementation = superclass => | ||
// @ts-expect-error false positive for incompatible static get properties. Lit-element merges super properties already for you. | ||
class extends FormControlMixin( | ||
@@ -196,3 +197,13 @@ SyncUpdatableMixin(DisabledMixin(SlotMixin(ScopedElementsMixin(superclass)))), | ||
if (['touched', 'dirty', 'prefilled', 'submitted', 'hasFeedbackFor'].includes(name)) { | ||
if ( | ||
[ | ||
'touched', | ||
'dirty', | ||
'prefilled', | ||
'focused', | ||
'submitted', | ||
'hasFeedbackFor', | ||
'filled', | ||
].includes(name) | ||
) { | ||
this._updateShouldShowFeedbackFor(); | ||
@@ -641,3 +652,5 @@ } | ||
// Sort all validators based on the type provided. | ||
const res = validationResult.sort((a, b) => types.indexOf(a.type) - types.indexOf(b.type)); | ||
const res = validationResult | ||
.filter(v => this._showFeedbackConditionFor(v.type)) | ||
.sort((a, b) => types.indexOf(a.type) - types.indexOf(b.type)); | ||
return res.slice(0, this._visibleMessagesAmount); | ||
@@ -644,0 +657,0 @@ } |
@@ -15,2 +15,3 @@ import { expect, fixture, html } from '@open-wc/testing'; | ||
<div id="b-child"></div> | ||
<div id="b-child2"></div> | ||
</div> | ||
@@ -23,6 +24,6 @@ <div></div> | ||
// eslint-disable-next-line no-unused-vars | ||
const [a, _1, b, _2, bChild, _3, c, _4] = Array.from(el.querySelectorAll('div')); | ||
const unorderedNodes = [bChild, c, a, b]; | ||
const [a, _1, b, _2, bChild, child2, _3, c, _4] = Array.from(el.querySelectorAll('div')); | ||
const unorderedNodes = [bChild, c, a, b, child2]; | ||
const result = getAriaElementsInRightDomOrder(unorderedNodes); | ||
expect(result).to.eql([a, b, bChild, c]); | ||
expect(result).to.eql([a, b, bChild, child2, c]); | ||
}); | ||
@@ -29,0 +30,0 @@ |
@@ -18,3 +18,3 @@ import { Constructor } from '@open-wc/dedupe-mixin'; | ||
submitted: boolean; | ||
serializedValue: string; | ||
serializedValue: { [key: string]: any }; | ||
modelValue: { [x: string]: any }; | ||
@@ -21,0 +21,0 @@ formattedValue: string; |
@@ -13,4 +13,25 @@ import { CSSResult, LitElement, nothing, TemplateResult } from '@lion/core'; | ||
export class FormControlHost { | ||
export declare class FormControlHost { | ||
static get styles(): CSSResult | CSSResult[]; | ||
static get properties(): { | ||
name: { | ||
type: StringConstructor; | ||
reflect: boolean; | ||
}; | ||
readOnly: { | ||
type: BooleanConstructor; | ||
attribute: string; | ||
reflect: boolean; | ||
}; | ||
label: StringConstructor; | ||
helpText: { | ||
type: StringConstructor; | ||
attribute: string; | ||
}; | ||
modelValue: { attribute: boolean }; | ||
_ariaLabelledNodes: { attribute: boolean }; | ||
_ariaDescribedNodes: { attribute: boolean }; | ||
_repropagationRole: { attribute: boolean }; | ||
_isRepropagationEndpoint: { attribute: boolean }; | ||
}; | ||
/** | ||
@@ -133,3 +154,3 @@ * A Boolean attribute which, if present, indicates that the user should not be able to edit | ||
Constructor<FormControlHost> & | ||
FormControlHost & | ||
typeof FormControlHost & | ||
Constructor<FormRegisteringHost> & | ||
@@ -136,0 +157,0 @@ typeof FormRegisteringHost & |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
310919
7536
+ Added@lion/core@0.13.1(transitive)
+ Added@lion/localize@0.14.6(transitive)
- Removed@lion/core@0.13.0(transitive)
- Removed@lion/localize@0.14.5(transitive)
Updated@lion/core@0.13.1
Updated@lion/localize@0.14.6