@lion/form-core
Advanced tools
Comparing version 0.6.14 to 0.7.0
# Change Log | ||
## 0.7.0 | ||
### Minor Changes | ||
- a8cf4215: Added `isTriggeredByUser` meta data in `model-value-changed` event | ||
Sometimes it can be helpful to detect whether a value change was caused by a user or via a programmatical change. | ||
This feature acts as a normalization layer: since we use `model-value-changed` as a single source of truth event for all FormControls, there should be no use cases for (inconsistently implemented (cross browser)) events like `input`/`change`/`user-input-changed` etc. | ||
### Patch Changes | ||
- 5302ec89: Minimise dependencies by removing integration demos to form-integrations and form-core packages. | ||
- 98f1bb7e: Ensure all lit imports are imported from @lion/core. Remove devDependencies in all subpackages and move to root package.json. Add demo dependencies as real dependencies for users that extend our docs/demos. | ||
- Updated dependencies [a8cf4215] | ||
- Updated dependencies [98f1bb7e] | ||
- @lion/localize@0.15.4 | ||
- @lion/core@0.13.7 | ||
## 0.6.14 | ||
@@ -4,0 +22,0 @@ |
@@ -8,3 +8,3 @@ [//]: # 'AUTO INSERT HEADER PREPUBLISH' | ||
import { LionInput } from '@lion/input'; | ||
import { Required, IsString, MaxLength, DefaultSuccess, Validator } from '@lion/form-core'; | ||
import { Required, IsString, MaxLength, DefaultSuccess, Validator } from '../../index.js'; | ||
@@ -16,4 +16,2 @@ import { loadDefaultFeedbackMessages } from '@lion/validate-messages'; | ||
}; | ||
loadDefaultFeedbackMessages(); | ||
``` | ||
@@ -114,1 +112,29 @@ | ||
``` | ||
## Default Validation Messages | ||
Lion comes with a set of localized messages for default Validators. | ||
Once the method `loadDefaultFeedbackMessages` is called, it will make sure that all validators provided in `@lion/form-core` will have a default error message. | ||
It uses the `@lion/localize` system to provide these translations and has support for more than 15 locales. | ||
```bash | ||
npm i --save @lion/validate-messages | ||
``` | ||
```js | ||
import { loadDefaultFeedbackMessages } from '@lion/validate-messages'; | ||
``` | ||
```js preview-story | ||
export const defaultValidationMessages = () => { | ||
loadDefaultFeedbackMessages(); | ||
return html` | ||
<lion-input | ||
name="value" | ||
label="Value" | ||
.fieldName="${'value'}" | ||
.validators="${[new Required()]}" | ||
></lion-input> | ||
`; | ||
}; | ||
``` |
@@ -9,2 +9,3 @@ export { FocusMixin } from "./src/FocusMixin.js"; | ||
export { FormRegistrarPortalMixin } from "./src/registration/FormRegistrarPortalMixin.js"; | ||
export { NativeTextFieldMixin } from "./src/NativeTextFieldMixin.js"; | ||
export { FormControlsCollection } from "./src/registration/FormControlsCollection.js"; | ||
@@ -11,0 +12,0 @@ export { ValidateMixin } from "./src/validate/ValidateMixin.js"; |
@@ -9,2 +9,3 @@ export { FocusMixin } from './src/FocusMixin.js'; | ||
export { FormRegistrarPortalMixin } from './src/registration/FormRegistrarPortalMixin.js'; | ||
export { NativeTextFieldMixin } from './src/NativeTextFieldMixin.js'; | ||
export { FormControlsCollection } from './src/registration/FormControlsCollection.js'; | ||
@@ -11,0 +12,0 @@ |
{ | ||
"name": "@lion/form-core", | ||
"version": "0.6.14", | ||
"version": "0.7.0", | ||
"description": "Form-core contains all essential building blocks for creating form fields and fieldsets", | ||
@@ -37,4 +37,4 @@ "license": "MIT", | ||
"dependencies": { | ||
"@lion/core": "0.13.6", | ||
"@lion/localize": "0.15.3" | ||
"@lion/core": "0.13.7", | ||
"@lion/localize": "0.15.4" | ||
}, | ||
@@ -41,0 +41,0 @@ "keywords": [ |
@@ -164,3 +164,3 @@ import { dedupeMixin } from '@lion/core'; | ||
/** @param {import('lit-element').PropertyValues} changedProperties */ | ||
/** @param {import('@lion/core').PropertyValues} changedProperties */ | ||
updated(changedProperties) { | ||
@@ -167,0 +167,0 @@ super.updated(changedProperties); |
@@ -95,3 +95,3 @@ /* eslint-disable class-methods-use-this */ | ||
/** | ||
* @param {import('lit-element').PropertyValues } changedProperties | ||
* @param {import('@lion/core').PropertyValues } changedProperties | ||
*/ | ||
@@ -108,3 +108,3 @@ firstUpdated(changedProperties) { | ||
/** | ||
* @param {import('lit-element').PropertyValues } changedProperties | ||
* @param {import('@lion/core').PropertyValues } changedProperties | ||
*/ | ||
@@ -180,2 +180,3 @@ updated(changedProperties) { | ||
</small> | ||
${this._afterTemplate()} | ||
`; | ||
@@ -188,2 +189,6 @@ } | ||
_afterTemplate() { | ||
return nothing; | ||
} | ||
connectedCallback() { | ||
@@ -203,3 +208,5 @@ super.connectedCallback(); | ||
} | ||
this.__isHandlingUserInput = true; | ||
this.checked = !this.checked; | ||
this.__isHandlingUserInput = false; | ||
} | ||
@@ -206,0 +213,0 @@ |
@@ -1,3 +0,2 @@ | ||
import { dedupeMixin, html, SlotMixin } from '@lion/core'; | ||
import { DisabledMixin } from '@lion/core/src/DisabledMixin.js'; | ||
import { dedupeMixin, html, SlotMixin, DisabledMixin } from '@lion/core'; | ||
import { FormControlMixin } from '../FormControlMixin.js'; | ||
@@ -210,3 +209,3 @@ import { FormControlsCollection } from '../registration/FormControlsCollection.js'; | ||
/** | ||
* @param {import('lit-element').PropertyValues } changedProperties | ||
* @param {import('@lion/core').PropertyValues } changedProperties | ||
*/ | ||
@@ -213,0 +212,0 @@ updated(changedProperties) { |
export type FormatMixin = typeof import("../types/FormatMixinTypes.js").FormatImplementation; | ||
export const FormatMixin: typeof import("../types/FormatMixinTypes.js").FormatImplementation; | ||
export type FormatOptions = import("@lion/localize/types/LocalizeMixinTypes").FormatNumberOptions; | ||
export type ModelValueEventDetails = { | ||
formPath: HTMLElement[]; | ||
isTriggeredByUser: boolean; | ||
initialize?: boolean | undefined; | ||
}; |
@@ -11,2 +11,3 @@ /* eslint-disable class-methods-use-this */ | ||
* @typedef {import('@lion/localize/types/LocalizeMixinTypes').FormatNumberOptions} FormatOptions | ||
* @typedef {import('../types/FormControlMixinTypes.js').ModelValueEventDetails} ModelValueEventDetails | ||
*/ | ||
@@ -320,3 +321,6 @@ | ||
bubbles: true, | ||
detail: { formPath: [this] }, | ||
detail: /** @type { ModelValueEventDetails } */ ({ | ||
formPath: [this], | ||
isTriggeredByUser: Boolean(this.__isHandlingUserInput), | ||
}), | ||
}), | ||
@@ -323,0 +327,0 @@ ); |
@@ -1,43 +0,14 @@ | ||
/** | ||
* #FormControlMixin : | ||
* | ||
* This Mixin is a shared fundament for all form components, it's applied on: | ||
* - LionField (which is extended to LionInput, LionTextarea, LionSelect etc. etc.) | ||
* - LionFieldset (which is extended to LionRadioGroup, LionCheckboxGroup, LionForm) | ||
*/ | ||
export type FormControlMixin = typeof import("../types/FormControlMixinTypes.js").FormControlImplementation; | ||
export const FormControlMixin: typeof import("../types/FormControlMixinTypes.js").FormControlImplementation; | ||
/** | ||
* #FormControlMixin : | ||
* | ||
* This Mixin is a shared fundament for all form components, it's applied on: | ||
* - LionField (which is extended to LionInput, LionTextarea, LionSelect etc. etc.) | ||
* - LionFieldset (which is extended to LionRadioGroup, LionCheckboxGroup, LionForm) | ||
*/ | ||
export type TemplateResult = import("lit-element").TemplateResult; | ||
/** | ||
* #FormControlMixin : | ||
* | ||
* This Mixin is a shared fundament for all form components, it's applied on: | ||
* - LionField (which is extended to LionInput, LionTextarea, LionSelect etc. etc.) | ||
* - LionFieldset (which is extended to LionRadioGroup, LionCheckboxGroup, LionForm) | ||
*/ | ||
export type CSSResult = import("lit-element").CSSResult; | ||
/** | ||
* #FormControlMixin : | ||
* | ||
* This Mixin is a shared fundament for all form components, it's applied on: | ||
* - LionField (which is extended to LionInput, LionTextarea, LionSelect etc. etc.) | ||
* - LionFieldset (which is extended to LionRadioGroup, LionCheckboxGroup, LionForm) | ||
*/ | ||
export type TemplateResult = import("@lion/core").TemplateResult; | ||
export type CSSResult = import("@lion/core").CSSResult; | ||
export type nothing = any; | ||
/** | ||
* #FormControlMixin : | ||
* | ||
* This Mixin is a shared fundament for all form components, it's applied on: | ||
* - LionField (which is extended to LionInput, LionTextarea, LionSelect etc. etc.) | ||
* - LionFieldset (which is extended to LionRadioGroup, LionCheckboxGroup, LionForm) | ||
*/ | ||
export type SlotsMap = { | ||
[key: string]: typeof import("@lion/core/types/SlotMixinTypes").slotFunction; | ||
}; | ||
export type FormControlHost = import("../types/FormControlMixinTypes.js").FormControlHost; | ||
export type ModelValueEventDetails = { | ||
formPath: HTMLElement[]; | ||
isTriggeredByUser: boolean; | ||
initialize?: boolean | undefined; | ||
}; |
@@ -8,2 +8,12 @@ import { css, dedupeMixin, html, nothing, SlotMixin } from '@lion/core'; | ||
/** | ||
* @typedef {import('@lion/core').TemplateResult} TemplateResult | ||
* @typedef {import('@lion/core').CSSResult} CSSResult | ||
* @typedef {import('@lion/core').nothing} nothing | ||
* @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').ModelValueEventDetails} ModelValueEventDetails | ||
*/ | ||
/** | ||
* Generates random unique identifier (for dom elements) | ||
@@ -22,7 +32,2 @@ * @param {string} prefix | ||
* - LionFieldset (which is extended to LionRadioGroup, LionCheckboxGroup, LionForm) | ||
* @typedef {import('@lion/core').TemplateResult} TemplateResult | ||
* @typedef {import('@lion/core').CSSResult} CSSResult | ||
* @typedef {import('@lion/core').nothing} nothing | ||
* @typedef {import('@lion/core/types/SlotMixinTypes').SlotsMap} SlotsMap | ||
* @typedef {import('../types/FormControlMixinTypes.js').FormControlMixin} FormControlMixin | ||
* @param {import('@open-wc/dedupe-mixin').Constructor<import('@lion/core').LitElement>} superclass | ||
@@ -236,3 +241,3 @@ * @type {FormControlMixin} | ||
/** @param {import('lit-element').PropertyValues } changedProperties */ | ||
/** @param {import('@lion/core').PropertyValues } changedProperties */ | ||
updated(changedProperties) { | ||
@@ -756,3 +761,7 @@ super.updated(changedProperties); | ||
bubbles: true, | ||
detail: { formPath: [this], initialize: true }, | ||
detail: /** @type {ModelValueEventDetails} */ ({ | ||
formPath: [this], | ||
initialize: true, | ||
isTriggeredByUser: false, | ||
}), | ||
}), | ||
@@ -829,3 +838,9 @@ ); | ||
this.dispatchEvent( | ||
new CustomEvent('model-value-changed', { bubbles: true, detail: { formPath } }), | ||
new CustomEvent('model-value-changed', { | ||
bubbles: true, | ||
detail: /** @type {ModelValueEventDetails} */ ({ | ||
formPath, | ||
isTriggeredByUser: Boolean(ev.detail?.isTriggeredByUser), | ||
}), | ||
}), | ||
); | ||
@@ -832,0 +847,0 @@ } |
@@ -49,3 +49,3 @@ import { LitElement, SlotMixin } from '@lion/core'; | ||
/** | ||
* @param {import('lit-element').PropertyValues } changedProperties | ||
* @param {import('@lion/core').PropertyValues } changedProperties | ||
*/ | ||
@@ -52,0 +52,0 @@ firstUpdated(changedProperties) { |
@@ -37,3 +37,3 @@ import { dedupeMixin } from '@lion/core'; | ||
/** @param {import('lit-element').PropertyValues } changedProperties */ | ||
/** @param {import('@lion/core').PropertyValues } changedProperties */ | ||
firstUpdated(changedProperties) { | ||
@@ -40,0 +40,0 @@ super.firstUpdated(changedProperties); |
@@ -30,3 +30,3 @@ /** | ||
validator: Validator; | ||
}): string | Node | import("lit-element").TemplateResult; | ||
}): string | Node | import("@lion/core").TemplateResult; | ||
currentType: string | undefined; | ||
@@ -38,3 +38,3 @@ removeMessage: number | undefined; | ||
export type Validator = import("./Validator").Validator; | ||
export type TemplateResult = import("lit-element").TemplateResult; | ||
export type TemplateResult = import("@lion/core").TemplateResult; | ||
export type messageMap = { | ||
@@ -41,0 +41,0 @@ message: string | Node; |
@@ -36,3 +36,3 @@ import { html, LitElement } from '@lion/core'; | ||
/** | ||
* @param {import('lit-element').PropertyValues } changedProperties | ||
* @param {import('@lion/core').PropertyValues } changedProperties | ||
*/ | ||
@@ -39,0 +39,0 @@ updated(changedProperties) { |
@@ -174,3 +174,3 @@ /* eslint-disable class-methods-use-this, camelcase, no-param-reassign, max-classes-per-file */ | ||
/** | ||
* @param {import('lit-element').PropertyValues} changedProperties | ||
* @param {import('@lion/core').PropertyValues} changedProperties | ||
*/ | ||
@@ -474,3 +474,3 @@ firstUpdated(changedProperties) { | ||
const errorType = Array.isArray(v) ? 'array' : typeof v; | ||
const errorMessage = `Validators array only accepts class instances of Validator. Type "${errorType}" found.`; | ||
const errorMessage = `Validators array only accepts class instances of Validator. Type "${errorType}" found. This may be caused by having multiple installations of @lion/form-core.`; | ||
// eslint-disable-next-line no-console | ||
@@ -612,3 +612,3 @@ console.error(errorMessage, this); | ||
/** @param {import('lit-element').PropertyValues} changedProperties */ | ||
/** @param {import('@lion/core').PropertyValues} changedProperties */ | ||
updated(changedProperties) { | ||
@@ -615,0 +615,0 @@ super.updated(changedProperties); |
@@ -241,2 +241,3 @@ import { expect, html, defineCE, unsafeStatic, fixture } from '@open-wc/testing'; | ||
expect(fieldsetEv.detail.formPath).to.eql([fieldsetEl]); | ||
expect(fieldsetEv.detail.initialize).to.be.true; | ||
@@ -247,5 +248,9 @@ expect(formSpy.callCount).to.equal(1); | ||
expect(formEv.detail.formPath).to.eql([formEl]); | ||
expect(formEv.detail.initialize).to.be.true; | ||
}); | ||
}); | ||
/** | ||
* After initialization means: events triggered programmatically or by user actions | ||
*/ | ||
describe('After initialization', () => { | ||
@@ -315,2 +320,3 @@ it('redispatches one event from host and keeps formPath history', async () => { | ||
expect(choiceGroupEv.detail.formPath).to.eql([choiceGroupEl]); | ||
expect(choiceGroupEv.detail.isTriggeredByUser).to.be.false; | ||
@@ -321,5 +327,38 @@ expect(formSpy.callCount).to.equal(1); | ||
expect(formEv.detail.formPath).to.eql([choiceGroupEl, formEl]); | ||
expect(formEv.detail.isTriggeredByUser).to.be.false; | ||
}); | ||
it('sets "isTriggeredByUser" event detail when event triggered by user', async () => { | ||
const formSpy = sinon.spy(); | ||
const fieldsetSpy = sinon.spy(); | ||
const fieldSpy = sinon.spy(); | ||
const formEl = await fixture(html` | ||
<${groupTag} name="form"> | ||
<${groupTag} name="fieldset"> | ||
<${tag} name="field"></${tag}> | ||
</${groupTag}> | ||
</${groupTag}> | ||
`); | ||
const fieldEl = formEl.querySelector('[name=field]'); | ||
const fieldsetEl = formEl.querySelector('[name=fieldset]'); | ||
formEl.addEventListener('model-value-changed', formSpy); | ||
fieldsetEl?.addEventListener('model-value-changed', fieldsetSpy); | ||
fieldEl?.addEventListener('model-value-changed', fieldSpy); | ||
fieldEl?.dispatchEvent( | ||
new CustomEvent('model-value-changed', { | ||
bubbles: true, | ||
detail: { isTriggeredByUser: true }, | ||
}), | ||
); | ||
const fieldsetEv = fieldsetSpy.firstCall.args[0]; | ||
expect(fieldsetEv.detail.isTriggeredByUser).to.be.true; | ||
const formEv = formSpy.firstCall.args[0]; | ||
expect(formEv.detail.isTriggeredByUser).to.be.true; | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -29,3 +29,3 @@ import { LitElement } from '@lion/core'; | ||
/** @param {import('lit-element').PropertyValues } changedProperties */ | ||
/** @param {import('@lion/core').PropertyValues } changedProperties */ | ||
firstUpdated(changedProperties) { | ||
@@ -32,0 +32,0 @@ super.firstUpdated(changedProperties); |
import { Constructor } from '@open-wc/dedupe-mixin'; | ||
import { LitElement, TemplateResult } from '@lion/core'; | ||
import { CSSResultArray } from 'lit-element'; | ||
import { CSSResultArray } from '@lion/core'; | ||
import { FormatHost } from '../FormatMixinTypes'; | ||
@@ -38,2 +38,3 @@ | ||
_choiceGraphicTemplate(): TemplateResult; | ||
_afterTemplate(): TemplateResult; | ||
@@ -51,2 +52,4 @@ connectedCallback(): void; | ||
__isHandlingUserInput: boolean; | ||
_proxyInputEvent(): void; | ||
@@ -53,0 +56,0 @@ |
@@ -1,3 +0,2 @@ | ||
import { CSSResultArray } from 'lit-element'; | ||
import { LitElement, nothing, TemplateResult } from '@lion/core'; | ||
import { LitElement, nothing, TemplateResult, CSSResultArray } from '@lion/core'; | ||
import { SlotsMap, SlotHost } from '@lion/core/types/SlotMixinTypes'; | ||
@@ -10,2 +9,27 @@ import { Constructor } from '@open-wc/dedupe-mixin'; | ||
export type ModelValueEventDetails = { | ||
/** | ||
* A list that represents the path of FormControls the model-value-changed event | ||
* 'traveled through'. | ||
* (every FormControl stops propagation of its child and sends a new event, hereby adding | ||
* itself to the beginning of formPath) | ||
*/ | ||
formPath: HTMLElement[]; | ||
/** | ||
* Sometimes it can be helpful to detect whether a value change was caused by a user or | ||
* via a programmatical change. | ||
* This feature acts as a normalization layer: since we use `model-value-changed` as a single | ||
* source of truth event for all FormControls, there should be no use cases for | ||
* (inconsistently implemented (cross browser)) events | ||
* like 'input'/'change'/'user-input-changed' etc.) | ||
*/ | ||
isTriggeredByUser: boolean; | ||
/** | ||
* Whether it is the first event sent on initialization of the form (other | ||
* model-value-changed events are triggered imperatively or via user input (in the latter | ||
* case `isTriggeredByUser` is true)) | ||
*/ | ||
initialize?: boolean; | ||
}; | ||
declare interface HTMLElementWithValue extends HTMLElement { | ||
@@ -45,8 +69,8 @@ value: string; | ||
*/ | ||
readOnly: boolean; | ||
public readOnly: boolean; | ||
/** | ||
* The name the element will be registered on to the .formElements collection | ||
* The name the element will be registered with to the .formElements collection | ||
* of the parent. | ||
*/ | ||
name: string; | ||
public name: string; | ||
/** | ||
@@ -64,3 +88,3 @@ * The model value is the result of the parser function(when available). | ||
*/ | ||
modelValue: unknown; | ||
public modelValue: unknown; | ||
/** | ||
@@ -70,4 +94,4 @@ * The label text for the input node. | ||
*/ | ||
get label(): string; | ||
set label(arg: string); | ||
public get label(): string; | ||
public set label(arg: string); | ||
__label: string | undefined; | ||
@@ -78,9 +102,9 @@ /** | ||
*/ | ||
get helpText(): string; | ||
set helpText(arg: string); | ||
public get helpText(): string; | ||
public set helpText(arg: string); | ||
__helpText: string | undefined; | ||
set fieldName(arg: string); | ||
get fieldName(): string; | ||
public set fieldName(arg: string); | ||
public get fieldName(): string; | ||
__fieldName: string | undefined; | ||
get slots(): SlotsMap; | ||
public get slots(): SlotsMap; | ||
get _inputNode(): HTMLElementWithValue; | ||
@@ -110,3 +134,3 @@ get _labelNode(): HTMLElement; | ||
connectedCallback(): void; | ||
updated(changedProperties: import('lit-element').PropertyValues): void; | ||
updated(changedProperties: import('@lion/core').PropertyValues): void; | ||
@@ -133,3 +157,3 @@ render(): TemplateResult; | ||
_getAriaDescriptionElements(): HTMLElement[]; | ||
addToAriaLabelledBy( | ||
public addToAriaLabelledBy( | ||
element: HTMLElement, | ||
@@ -142,3 +166,3 @@ customConfig?: { | ||
__reorderAriaLabelledNodes: boolean | undefined; | ||
addToAriaDescribedBy( | ||
public addToAriaDescribedBy( | ||
element: HTMLElement, | ||
@@ -145,0 +169,0 @@ customConfig?: { |
import { LitElement } from '@lion/core'; | ||
import { Constructor } from '@open-wc/dedupe-mixin'; | ||
import { PropertyValues } from 'lit-element'; | ||
import { PropertyValues } from '@lion/core'; | ||
@@ -5,0 +5,0 @@ export declare interface SyncUpdatableNamespace { |
@@ -44,5 +44,5 @@ import { LitElement } from '@lion/core'; | ||
disconnectedCallback(): void; | ||
firstUpdated(changedProperties: import('lit-element').PropertyValues): void; | ||
firstUpdated(changedProperties: import('@lion/core').PropertyValues): void; | ||
updateSync(name: string, oldValue: unknown): void; | ||
updated(changedProperties: import('lit-element').PropertyValues): void; | ||
updated(changedProperties: import('@lion/core').PropertyValues): void; | ||
@@ -49,0 +49,0 @@ validate(opts?: { clearCurrentResult?: boolean }): void; |
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
308380
7387
+ Added@lion/core@0.13.7(transitive)
+ Added@lion/localize@0.15.4(transitive)
+ Addedsingleton-manager@1.2.1(transitive)
- Removed@lion/core@0.13.6(transitive)
- Removed@lion/localize@0.15.3(transitive)
- Removedsingleton-manager@1.2.0(transitive)
Updated@lion/core@0.13.7
Updated@lion/localize@0.15.4