diff --git a/.vscode/settings.json b/.vscode/settings.json
index 80b2b7412..b9e668415 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -33,6 +33,9 @@
"package-lock.json": true
},
"typescript.tsdk": "node_modules/typescript/lib",
+ "editor.rulers": [
+ 120 // prettier print width
+ ],
"lit-plugin.rules.no-missing-import": "off",
"lit-plugin.strict": true
}
diff --git a/packages/js-example-app/src/index.js b/packages/js-example-app/src/index.js
index 8a13c1c5e..e7218a24d 100644
--- a/packages/js-example-app/src/index.js
+++ b/packages/js-example-app/src/index.js
@@ -9,6 +9,7 @@ const blrButton = document.getElementsByTagName('blr-button-text')[0];
const blrCheckbox = document.getElementsByTagName('blr-checkbox')[0];
const blrSelect = document.getElementsByTagName('blr-select')[0];
const blrInputFieldText = document.getElementsByTagName('blr-input-field-text')[0];
+const blrInputFieldNumber = document.getElementsByTagName('blr-input-field-number')[0];
const blrTextArea = document.getElementsByTagName('blr-textarea')[0];
const addLog = (log) => {
@@ -81,6 +82,18 @@ blrInputFieldText.addEventListener('blrTextValueChange', () => {
addLog('blr-input-field-text changed');
});
+blrInputFieldNumber.addEventListener('blrFocus', () => {
+ addLog('blr-number-input focused');
+});
+
+blrInputFieldNumber.addEventListener('blrBlur', () => {
+ addLog('blr-number-input blurred');
+});
+
+blrInputFieldNumber.addEventListener('blrNumberValueChange', () => {
+ addLog('blr-number-input value changed');
+});
+
blrTextArea.addEventListener('blrFocus', () => {
addLog('blr-textarea focused');
});
diff --git a/packages/ui-library/src/components/input-field-number/index.stories.ts b/packages/ui-library/src/components/input-field-number/index.stories.ts
index d744209b7..fdab3e8ca 100644
--- a/packages/ui-library/src/components/input-field-number/index.stories.ts
+++ b/packages/ui-library/src/components/input-field-number/index.stories.ts
@@ -5,7 +5,6 @@ import { FormSizes, Units } from '../../globals/constants';
import { Themes } from '../../foundation/_tokens-generated/index.themes';
import { PureIconKeys } from '@boiler/icons/icons-optimized';
import { html } from 'lit-html';
-import { action } from '@storybook/addon-actions';
// this loads the all components instances and registers their html tags
import '../../index';
@@ -20,7 +19,7 @@ const sharedStyles = html`
const defaultParams: BlrInputFieldNumberType = {
theme: 'Light',
- size: 'md',
+ sizeVariant: 'md',
stepperVariant: 'vertical',
placeholder: 'Placeholder-text',
value: undefined,
@@ -51,10 +50,6 @@ export default {
args: {
...defaultParams,
name: 'InputFieldNumber',
- onChange: () => action('onChange'),
- onSelect: () => action('onSelect'),
- onFocus: () => action('onFocus'),
- onBlur: () => action('onBlur'),
},
argTypes: {
theme: {
@@ -64,9 +59,8 @@ export default {
category: 'Appearance',
},
},
- size: {
- name: 'sizeVariant',
- description: ' Choose size of the component. ',
+ sizeVariant: {
+ description: ' Choose sizeVariant of the component. ',
options: FormSizes,
control: { type: 'radio' },
table: {
@@ -74,7 +68,6 @@ export default {
},
},
stepperVariant: {
- name: 'stepperVariant',
description: 'Choose stepperVariant of the stepper that is used in the component.',
options: ['vertical', 'horizontal', 'split'],
control: {
@@ -90,7 +83,6 @@ export default {
},
},
placeholder: {
- name: 'placeholder',
description: 'Enter string used as placeholder text.',
defaultValue: 'Placeholder-text',
table: {
@@ -98,7 +90,6 @@ export default {
},
},
value: {
- name: 'value',
description: 'Enter the value the component should have.',
table: {
category: 'Content / Settings',
@@ -109,7 +100,6 @@ export default {
},
},
decimals: {
- name: 'decimals',
description: 'Enter how many decimals the value of the component has.',
table: {
category: 'Content / Settings',
@@ -119,7 +109,6 @@ export default {
},
},
leadingZeros: {
- name: 'leadingZeros',
description: 'Enter how many leading zeros the value of the component has.',
table: {
category: 'Content / Settings',
@@ -129,14 +118,12 @@ export default {
},
},
prependUnit: {
- name: 'prependUnit',
description: 'Choose if unit is displayed as a prefix or suffix.',
table: {
category: 'Content / Settings',
},
},
unit: {
- name: 'unit',
description: 'Select a unit which is displayed next to the input.',
options: [undefined, ...Units],
control: {
@@ -147,7 +134,6 @@ export default {
},
},
step: {
- name: 'step',
description: 'Enter how much the value should change when the stepper buttons are used.',
control: {
type: 'number',
@@ -158,7 +144,6 @@ export default {
},
hasLabel: {
- name: 'hasLabel',
description: 'Choose if component has a label.',
defaultValue: true,
control: { type: 'boolean' },
@@ -167,7 +152,6 @@ export default {
},
},
label: {
- name: 'label',
description: 'Enter string used as label text.',
control: {
type: 'text',
@@ -178,7 +162,6 @@ export default {
if: { arg: 'hasLabel', eq: true },
},
labelAppendix: {
- name: 'labelAppendix',
description:
'Enter string used as an appendix to the label. Use this to inform the user in case this field is required.',
control: {
@@ -190,7 +173,6 @@ export default {
if: { arg: 'hasLabel', eq: true },
},
hasHint: {
- name: 'hasHint',
description: ' Choose if component has a hint message. ',
defaultValue: true,
control: {
@@ -218,7 +200,6 @@ export default {
},
},
disabled: {
- name: 'disabled',
description:
'Choose if component is disabled. Prevents the user to select or change the value of this component. ',
defaultValue: false,
@@ -227,7 +208,6 @@ export default {
},
},
readonly: {
- name: 'readonly',
description: 'Choose if component is readonly. The user can select but not change the value of this component.',
defaultValue: false,
table: {
@@ -236,7 +216,6 @@ export default {
},
// Validation
required: {
- name: 'required',
description: 'Choose if the component must hold a value after an interaction or a submit.',
defaultValue: false,
table: {
@@ -244,7 +223,6 @@ export default {
},
},
hasError: {
- name: 'hasError',
description: 'Choose if component has an error.',
defaultValue: false,
table: {
@@ -252,7 +230,6 @@ export default {
},
},
errorMessage: {
- name: 'errorMessage',
description: 'Enter string used used as error message.',
table: {
category: 'Validations',
@@ -271,59 +248,60 @@ export default {
},
//Technical attributes
inputFieldNumberId: {
- name: 'inputFieldNumberId',
description: 'Unique identifier for this component.',
table: {
category: 'Technical Attributes',
},
},
name: {
- name: 'name',
description: 'For a < form > element, the name attribute is used as a reference when the data is submitted.',
table: {
category: 'Technical Attributes',
},
control: { type: 'text', label: 'Input Field Number' },
},
- onChange: {
- name: 'onChange',
+ //Events
+ blrNumberValueChange: {
description: 'Fires when the value changes.',
- action: 'onChange',
+ action: 'blrNumberValueChange',
table: {
- disable: false,
category: 'Events',
},
},
- onSelect: {
- name: 'onSelect',
+ blrSelect: {
+ name: 'blrSelect',
description: 'Fires when some text is selected.',
- action: 'onSelect',
+ action: 'blrSelect',
table: {
- disable: false,
category: 'Events',
},
},
- onFocus: {
- name: 'onFocus',
+ blrFocus: {
+ name: 'blrFocus',
description: 'Fires when the component is focused.',
- action: 'onFocus',
+ action: 'blrFocus',
table: {
- disable: false,
category: 'Events',
},
},
- onBlur: {
- name: 'onBlur',
+ blrBlur: {
+ name: 'blrBlur',
description: 'Fires when the component lost focus.',
- action: 'onBlur',
+ action: 'blrBlur',
+ table: {
+ category: 'Events',
+ },
+ },
+ blrNumberStepperClick: {
+ name: 'blrNumberStepperClick',
+ description: 'Fires when one of the stepper buttons is clicked.',
+ action: 'blrNumberStepperClick',
table: {
- disable: false,
category: 'Events',
},
},
// Accessibility attributes
stepIncreaseAriaLabel: {
- name: 'stepIncreaseAriaLabel',
description: 'Labels the "up" or increase stepper button to assistive technologies, such as screen readers.',
table: {
category: 'Accessibility',
@@ -331,7 +309,6 @@ export default {
control: { type: 'text', defaultValue: '+' },
},
stepDecreaseAriaLabel: {
- name: 'stepDecreaseAriaLabel',
description:
'Labels the "down" or decrease stepper button to assistive technologies, such as screen readers.\nNote that the default value is not a hyphen (-) but the minus sign \\u2212 (\u2212).',
table: {
@@ -387,7 +364,7 @@ export const SizeVariant = (params: BlrInputFieldNumberType) => {
${BlrInputFieldNumberRenderFunction({
...params,
labelAppendix: undefined,
- size: 'sm',
+ sizeVariant: 'sm',
label: 'Input field number SM',
value: undefined,
inputFieldNumberId: 'test-sm',
@@ -397,7 +374,7 @@ export const SizeVariant = (params: BlrInputFieldNumberType) => {
${BlrInputFieldNumberRenderFunction({
...params,
labelAppendix: undefined,
- size: 'md',
+ sizeVariant: 'md',
label: 'Input field number MD',
value: undefined,
inputFieldNumberId: 'test-md',
@@ -407,7 +384,7 @@ export const SizeVariant = (params: BlrInputFieldNumberType) => {
${BlrInputFieldNumberRenderFunction({
...params,
labelAppendix: undefined,
- size: 'lg',
+ sizeVariant: 'lg',
label: 'Input field number LG',
value: undefined,
inputFieldNumberId: 'test-lg',
@@ -470,7 +447,7 @@ export const Placeholder = (params: BlrInputFieldNumberType) => {
${BlrInputFieldNumberRenderFunction({
...params,
- size: 'md',
+ sizeVariant: 'md',
label: 'With placeholder',
labelAppendix: undefined,
value: undefined,
@@ -480,7 +457,7 @@ export const Placeholder = (params: BlrInputFieldNumberType) => {
${BlrInputFieldNumberRenderFunction({
...params,
- size: 'md',
+ sizeVariant: 'md',
label: 'Without placeholder',
labelAppendix: undefined,
placeholder: '',
diff --git a/packages/ui-library/src/components/input-field-number/index.test.ts b/packages/ui-library/src/components/input-field-number/index.test.ts
index 7d9c3b944..191a9013d 100644
--- a/packages/ui-library/src/components/input-field-number/index.test.ts
+++ b/packages/ui-library/src/components/input-field-number/index.test.ts
@@ -3,9 +3,10 @@ import '@boiler/ui-library/dist/';
import { BlrInputFieldNumberRenderFunction } from './renderFunction';
import type { BlrInputFieldNumberType } from '.';
-import { fixture, expect, nextFrame } from '@open-wc/testing';
+import { fixture, expect, nextFrame, oneEvent } from '@open-wc/testing';
import { querySelectorAllDeep, querySelectorDeep } from 'query-selector-shadow-dom';
import { getRandomString } from '../../utils/get-random.string';
+import { BlrFocusEvent } from '../../globals/events';
const sampleParams: BlrInputFieldNumberType = {
placeholder: 'Type your message here ..',
@@ -95,7 +96,7 @@ describe('blr-input-field-number', () => {
expect(errorClassName).to.contain('error');
});
- it('has a size md by default', async () => {
+ it('has a sizeVariant md by default', async () => {
const element = await fixture(BlrInputFieldNumberRenderFunction(sampleParams));
const inputFieldNumberWrapper = querySelectorDeep('.input-wrapper', element.getRootNode() as HTMLElement);
@@ -104,8 +105,8 @@ describe('blr-input-field-number', () => {
expect(className).to.contain('md');
});
- it('has a size sm when "size" is set to "sm" ', async () => {
- const element = await fixture(BlrInputFieldNumberRenderFunction({ ...sampleParams, size: 'sm' }));
+ it('has a sizeVariant sm when "sizeVariant" is set to "sm" ', async () => {
+ const element = await fixture(BlrInputFieldNumberRenderFunction({ ...sampleParams, sizeVariant: 'sm' }));
const inputFieldNumberWrapper = querySelectorDeep('.input-wrapper', element.getRootNode() as HTMLElement);
const className = inputFieldNumberWrapper?.className;
@@ -176,4 +177,29 @@ describe('blr-input-field-number', () => {
// Clicking the stepper button with the decrease label decreases the value by our chosen step (6 - 5 = 1)
});
}
+
+ it('fires blrFocus and blrBlur events', async () => {
+ const element = await fixture(
+ BlrInputFieldNumberRenderFunction({
+ ...sampleParams,
+ })
+ );
+
+ const input = querySelectorDeep('input', element.getRootNode() as HTMLElement);
+
+ setTimeout(() => {
+ input!.focus();
+ });
+ const focusEvent: BlrFocusEvent = await oneEvent(element, 'blrFocus');
+ expect(focusEvent.detail.originalEvent).to.exist;
+
+ // just finding something else to focus instead, it's not important what
+ const stepper = querySelectorDeep('button', element.getRootNode() as HTMLElement);
+
+ setTimeout(() => {
+ stepper!.focus();
+ });
+ const blurEvent: BlrFocusEvent = await oneEvent(element, 'blrBlur');
+ expect(blurEvent.detail.originalEvent).to.exist;
+ });
});
diff --git a/packages/ui-library/src/components/input-field-number/index.ts b/packages/ui-library/src/components/input-field-number/index.ts
index bb939f67c..8b4343482 100644
--- a/packages/ui-library/src/components/input-field-number/index.ts
+++ b/packages/ui-library/src/components/input-field-number/index.ts
@@ -14,7 +14,34 @@ import { BlrFormCaptionGroupRenderFunction } from '../form-caption-group/renderF
import { BlrFormCaptionRenderFunction } from '../form-caption/renderFunction';
import { BlrFormLabelRenderFunction } from '../form-label/renderFunction';
import { BlrIconRenderFunction } from '../icon/renderFunction';
+import {
+ BlrBlurEvent,
+ BlrFocusEvent,
+ BlrNumberStepperClickEvent,
+ BlrNumberValueChangeEvent,
+ BlrSelectEvent,
+ createBlrBlurEvent,
+ createBlrFocusEvent,
+ createBlrNumberStepperClickEvent,
+ createBlrNumberValueChangeEvent,
+ createBlrSelectEvent,
+} from '../../globals/events';
+export type BlrNumberInputEventListeners = {
+ blrFocus?: (event: BlrFocusEvent) => void;
+ blrBlur?: (event: BlrBlurEvent) => void;
+ blrNumberValueChange?: (event: BlrNumberValueChangeEvent) => void;
+ blrSelect?: (event: BlrSelectEvent) => void;
+ blrNumberStepperClick?: (event: BlrNumberStepperClickEvent) => void;
+};
+
+/**
+ * @fires blrFocus NumberInput received focus
+ * @fires blrBlur NumberInput lost focus
+ * @fires blrNumberValueChange NumberInput value changed
+ * @fires blrSelect Text in NumberInput was selected
+ * @fires blrNumberStepperClick Step button was clicked
+ */
export class BlrInputFieldNumber extends LitElement {
static styles = [baseStyle];
@@ -29,7 +56,7 @@ export class BlrInputFieldNumber extends LitElement {
@property() readonly?: boolean;
@property() required?: boolean;
@property() hasLabel?: boolean;
- @property() size?: FormSizesType = 'md';
+ @property() sizeVariant?: FormSizesType = 'md';
@property() labelAppendix?: string;
@property() hasError?: boolean;
@property() errorMessage?: string;
@@ -49,17 +76,28 @@ export class BlrInputFieldNumber extends LitElement {
@property() theme: ThemeType = 'Light';
@state() protected currentValue = 0;
+ @state() protected isFocused = false;
- protected stepperUp() {
+ protected stepperUp(event: MouseEvent) {
if (this.currentValue !== undefined && this.step !== undefined) {
- this.currentValue += Number(this.step);
+ const oldValue = Number(this.currentValue);
+ const step = Number(this.step ?? 1);
+ const newValue = oldValue + step;
+ this.currentValue = newValue;
+ this.dispatchEvent(createBlrNumberValueChangeEvent({ originalEvent: event, oldValue, newValue }));
+ this.dispatchEvent(createBlrNumberStepperClickEvent({ originalEvent: event, direction: 'increase', step }));
this.requestUpdate('currentValue');
}
}
- protected stepperDown() {
+ protected stepperDown(event: MouseEvent) {
if (this.currentValue !== undefined && this.step !== undefined) {
- this.currentValue -= Number(this.step);
+ const oldValue = Number(this.currentValue);
+ const step = Number(this.step ?? 1);
+ const newValue = oldValue - step;
+ this.currentValue = newValue;
+ this.dispatchEvent(createBlrNumberValueChangeEvent({ originalEvent: event, oldValue, newValue }));
+ this.dispatchEvent(createBlrNumberStepperClickEvent({ originalEvent: event, direction: 'decrease', step }));
this.requestUpdate('currentValue');
}
}
@@ -69,8 +107,31 @@ export class BlrInputFieldNumber extends LitElement {
this.currentValue = Number(this.currentValue) || Number(this.value) || 0;
}
- protected handleChange() {
- this.currentValue = Number(this._numberFieldNode.value) || 0;
+ protected handleFocus = (event: FocusEvent) => {
+ if (!this.disabled) {
+ this.isFocused = true;
+ this.dispatchEvent(createBlrFocusEvent({ originalEvent: event }));
+ }
+ };
+
+ protected handleBlur = (event: FocusEvent) => {
+ if (!this.disabled) {
+ this.isFocused = false;
+ this.dispatchEvent(createBlrBlurEvent({ originalEvent: event }));
+ }
+ };
+
+ protected handleSelect = (event: Event) => {
+ if (!this.disabled) {
+ this.dispatchEvent(createBlrSelectEvent({ originalEvent: event }));
+ }
+ };
+
+ protected handleChange(event: Event) {
+ const oldValue = Number(this.currentValue);
+ const newValue = Number(this._numberFieldNode.value) || 0;
+ this.currentValue = newValue;
+ this.dispatchEvent(createBlrNumberValueChangeEvent({ originalEvent: event, oldValue, newValue }));
}
protected customFormat(cur: number, fractions: number, digits: number): string {
@@ -85,7 +146,7 @@ export class BlrInputFieldNumber extends LitElement {
}
protected getStepperButtonTemplate(direction: 'increase' | 'decrease', icon: SizelessIconType): TemplateResult<1> {
- if (!this.size) {
+ if (!this.sizeVariant) {
return html``;
}
@@ -97,7 +158,7 @@ export class BlrInputFieldNumber extends LitElement {
'StepperButton',
'Icon',
'SizeVariant',
- this.size.toUpperCase(),
+ this.sizeVariant.toUpperCase(),
]).toLowerCase() as FormSizesType;
const buttonClass = classMap({
@@ -144,7 +205,7 @@ export class BlrInputFieldNumber extends LitElement {
}
case 'horizontal': {
return html`
-
+
${this.getStepperButtonTemplate('decrease', 'blrMinus')}
${BlrDividerRenderFunction({
direction: 'vertical',
@@ -156,7 +217,7 @@ export class BlrInputFieldNumber extends LitElement {
}
case 'vertical': {
return html`
-
+
${this.getStepperButtonTemplate('increase', 'blrChevronUp')}
${BlrDividerRenderFunction({
direction: 'horizontal',
@@ -172,28 +233,28 @@ export class BlrInputFieldNumber extends LitElement {
protected render() {
const hasUnit = this.unit !== undefined && this.unit.length > 0;
- if (this.size) {
+ if (this.sizeVariant) {
const dynamicStyles =
this.theme === 'Light'
? [wrapperLight, actionLight, StepperComboLight]
: [wrapperDark, actionDark, StepperComboDark];
const inputClasses = classMap({
- [this.size]: this.size,
+ [this.sizeVariant]: this.sizeVariant,
prepend: hasUnit && !!this.prependUnit,
});
const unitClasses = classMap({
unit: true,
prepend: hasUnit && !!this.prependUnit,
- [`${this.size}`]: this.size,
+ [`${this.sizeVariant}`]: this.sizeVariant,
[this.stepperVariant || 'split']: this.stepperVariant || 'split',
});
const wrapperClasses = classMap({
'input-wrapper': true,
'disabled': this.disabled || false,
- [`${this.size}`]: this.size,
+ [`${this.sizeVariant}`]: this.sizeVariant,
[this.stepperVariant || 'split']: this.stepperVariant || 'split',
'error-input': this.hasError || false,
'prepend': hasUnit && !!this.prependUnit,
@@ -203,7 +264,7 @@ export class BlrInputFieldNumber extends LitElement {
const inputAndUnitContainer = classMap({
'input-unit-container': true,
'prepend': hasUnit && !!this.prependUnit,
- [`${this.size}`]: this.size,
+ [`${this.sizeVariant}`]: this.sizeVariant,
[this.stepperVariant || 'split']: this.stepperVariant || 'split',
});
@@ -214,7 +275,7 @@ export class BlrInputFieldNumber extends LitElement {
${BlrFormCaptionRenderFunction({
variant: 'hint',
theme: this.theme,
- size: this.size,
+ size: this.sizeVariant,
message: this.hintMessage,
icon: this.hintIcon,
})}
@@ -227,7 +288,7 @@ export class BlrInputFieldNumber extends LitElement {
${BlrFormCaptionRenderFunction({
variant: 'error',
theme: this.theme,
- size: this.size,
+ size: this.sizeVariant,
message: this.errorMessage,
icon: this.errorIcon,
})}
@@ -240,13 +301,13 @@ export class BlrInputFieldNumber extends LitElement {
-