From 76b12c85f96abc65277602e0a9d0d30fe4df1ca2 Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 27 Aug 2021 04:26:54 +0200 Subject: [PATCH 1/2] Make user action type dynamic --- .../src/scripts/react/components/Field.tsx | 20 ++++++++-------- .../react/components/__tests__/Field.test.tsx | 3 +++ library/src/scripts/vue/components/Field.vue | 24 +++++++++---------- .../vue/components/__tests__/Field.test.ts | 17 +++++++++---- 4 files changed, 38 insertions(+), 26 deletions(-) diff --git a/library/src/scripts/react/components/Field.tsx b/library/src/scripts/react/components/Field.tsx index 8324af1..b89fe23 100644 --- a/library/src/scripts/react/components/Field.tsx +++ b/library/src/scripts/react/components/Field.tsx @@ -23,7 +23,7 @@ import PropTypes, { InferProps } from 'prop-types'; import Message from 'scripts/react/components/Message'; import fieldPropType, { Field as FormField } from 'scripts/propTypes/field'; -type OUA = (newValue: FormValue) => void; +type OUA = (type: 'click' | 'input', newValue: FormValue) => void; type I18n = (label: string, values?: Record) => string; type Option = { value?: string; label?: string; type?: string; disabled?: boolean; }; @@ -59,7 +59,7 @@ const builtInComponents: Components = { icon={field.options.icon} type={field.options.type} iconPosition={field.options.iconPosition} - onClick={(): void => onUserAction(true)} + onClick={(): void => onUserAction('input', true)} modifiers={`${field.status} ${field.options.modifiers || ''}`} /> ), @@ -72,7 +72,6 @@ const builtInComponents: Components = { helper={field.message} min={field.options.min} max={field.options.max} - onChange={onUserAction} step={field.options.step} icon={field.options.icon} size={field.options.size} @@ -87,6 +86,7 @@ const builtInComponents: Components = { autocomplete={field.options.autocomplete} iconPosition={field.options.iconPosition} debounceTimeout={field.options.debounceTimeout || 100} + onChange={(value): void => onUserAction('input', value)} readonly={field.options.readonly || field.active === false} modifiers={`${field.status} ${field.options.modifiers || ''}`} placeholder={(field.options.placeholder !== undefined && field.options.placeholder !== null) @@ -101,7 +101,6 @@ const builtInComponents: Components = { value={field.value} label={field.label} helper={field.message} - onChange={onUserAction} cols={field.options.cols} rows={field.options.rows} onBlur={field.options.onBlur} @@ -112,6 +111,7 @@ const builtInComponents: Components = { transform={field.options.transform} autocomplete={field.options.autocomplete} debounceTimeout={field.options.debounceTimeout || 100} + onChange={(value): void => onUserAction('input', value)} readonly={field.options.readonly || field.active === false} modifiers={`${field.status} ${field.options.modifiers || ''}`} placeholder={(field.options.placeholder !== undefined && field.options.placeholder !== null) @@ -126,12 +126,12 @@ const builtInComponents: Components = { label={field.label} value={field.value} helper={field.message} - onChange={onUserAction} icon={field.options.icon} accept={field.options.accept} onFocus={field.options.onFocus} multiple={field.options.multiple} iconPosition={field.options.iconPosition} + onChange={(value): void => onUserAction('input', value)} modifiers={`${field.status} ${field.options.modifiers || ''}`} placeholder={(field.options.placeholder !== undefined && field.options.placeholder !== null) ? field.i18n(field.options.placeholder, field.options.formValues) @@ -145,9 +145,9 @@ const builtInComponents: Components = { label={field.label} value={field.value} helper={field.message} - onChange={onUserAction} icon={field.options.icon} onFocus={field.options.onFocus} + onChange={(value): void => onUserAction('input', value)} options={field.options.options.map((option: Option) => ((option.label !== undefined) ? ({ ...option, label: field.i18n(option.label, field.options.formValues) }) : option))} @@ -162,8 +162,8 @@ const builtInComponents: Components = { label={field.label} value={field.value} helper={field.message} - onChange={onUserAction} onFocus={field.options.onFocus} + onChange={(value): void => onUserAction('input', value)} options={field.options.options.map((option: Option) => ((option.label !== undefined) ? ({ ...option, label: field.i18n(option.label, field.options.formValues) }) : option))} @@ -177,8 +177,8 @@ const builtInComponents: Components = { label={field.label} value={field.value} helper={field.message} - onChange={onUserAction} onFocus={field.options.onFocus} + onChange={(value): void => onUserAction('input', value)} options={field.options.options.map((option: Option) => ((option.label !== undefined) ? ({ ...option, label: field.i18n(option.label, field.options.formValues) }) : option))} @@ -221,8 +221,8 @@ export default function Field(props: InferProps): JSX.Element } }; - const onUserAction = (newValue: FormValue): void => { - props.onUserAction({ type: 'input', value: newValue, fieldId: id }); + const onUserAction: OUA = (actionType, newValue) => { + props.onUserAction({ type: actionType, value: newValue, fieldId: id }); }; // Unknown field type... diff --git a/library/src/scripts/react/components/__tests__/Field.test.tsx b/library/src/scripts/react/components/__tests__/Field.test.tsx index a3ac8e6..b2ca8ea 100644 --- a/library/src/scripts/react/components/__tests__/Field.test.tsx +++ b/library/src/scripts/react/components/__tests__/Field.test.tsx @@ -16,6 +16,9 @@ type Any = any; // eslint-disable-line @typescript-eslint/no-explicit-any jest.mock('sonar-ui/react', () => { /* eslint-disable react/destructuring-assignment, jsx-a11y/no-static-element-interactions */ function Component(props: Any): JSX.Element { + if (props.onChange !== undefined) { + props.onChange(); + } return (
; type Components = { [type: string]: Component; }; -type Component = (field: Field, onUserAction: (newValue: FormValue) => void) => FormValue; +type Component = (field: Field, onUserAction: (type: 'click' | 'input', newValue: FormValue) => void) => FormValue; interface Props { id: string; @@ -127,7 +127,7 @@ const builtInComponents: Components = { iconPosition: field.options.iconPosition, }, events: { - click: (): void => onUserAction(true), + click: (): void => onUserAction('input', true), }, }), Textfield: (field, onUserAction) => ({ @@ -155,10 +155,10 @@ const builtInComponents: Components = { modifiers: `${field.status} ${field.options.modifiers || ''} `, }, events: { - change: onUserAction, - focus: field.options.onFocus, blur: field.options.onBlur, + focus: field.options.onFocus, iconClick: field.options.onIconClick, + change: (value): void => onUserAction('input', value), }, }), Textarea: (field, onUserAction) => ({ @@ -182,9 +182,9 @@ const builtInComponents: Components = { modifiers: `${field.status} ${field.options.modifiers || ''}`, }, events: { - change: onUserAction, - focus: field.options.onFocus, blur: field.options.onBlur, + focus: field.options.onFocus, + change: (value): void => onUserAction('input', value), }, }), FileUploader: (field, onUserAction) => ({ @@ -203,8 +203,8 @@ const builtInComponents: Components = { modifiers: `${field.status} ${field.options.modifiers || ''}`, }, events: { - change: onUserAction, focus: field.options.onFocus, + change: (value): void => onUserAction('input', value), }, }), Dropdown: (field, onUserAction) => ({ @@ -223,8 +223,8 @@ const builtInComponents: Components = { modifiers: `${field.status} ${field.options.modifiers || ''}`, }, events: { - change: onUserAction, focus: field.options.onFocus, + change: (value): void => onUserAction('input', value), }, }), Checkbox: (field, onUserAction) => ({ @@ -241,8 +241,8 @@ const builtInComponents: Components = { modifiers: `${field.status} ${field.options.modifiers || ''}`, }, events: { - change: onUserAction, focus: field.options.onFocus, + change: (value): void => onUserAction('input', value), }, }), Radio: (field, onUserAction) => ({ @@ -259,8 +259,8 @@ const builtInComponents: Components = { modifiers: `${field.status} ${field.options.modifiers || ''}`, }, events: { - change: onUserAction, focus: field.options.onFocus, + change: (value): void => onUserAction('input', value), }, }), }; @@ -369,8 +369,8 @@ export default Vue.extend({ }, }, methods: { - onUserAction(newValue: FormValue): void { - this.$emit('userAction', { fieldId: this.id, type: 'input', value: newValue }); + onUserAction(type: 'click' | 'input', newValue: FormValue): void { + this.$emit('userAction', { fieldId: this.id, type, value: newValue }); }, focusField(focusedValue: FormValue): void { this.isActive = true; diff --git a/library/src/scripts/vue/components/__tests__/Field.test.ts b/library/src/scripts/vue/components/__tests__/Field.test.ts index e0d4340..57baeeb 100644 --- a/library/src/scripts/vue/components/__tests__/Field.test.ts +++ b/library/src/scripts/vue/components/__tests__/Field.test.ts @@ -31,6 +31,9 @@ jest.mock('sonar-ui/vue', () => { click(): void { self.$emit('click'); }, + change(): void { + self.$emit('change'); + }, keyDown(): void { self.$emit('focus'); }, @@ -225,6 +228,7 @@ describe('vue/components/Field', () => { userAction: onUserAction, }, }); + await wrapper.trigger('change'); await wrapper.trigger('keyDown'); await wrapper.vm.$nextTick(); expect(wrapper.html()).toMatchSnapshot(); @@ -290,6 +294,7 @@ describe('vue/components/Field', () => { }); expect(wrapper.html()).toMatchSnapshot(); expect(onFocus).toHaveBeenCalledTimes(0); + await wrapper.trigger('change'); await wrapper.trigger('keyDown'); await wrapper.vm.$nextTick(); expect(wrapper.html()).toMatchSnapshot(); @@ -317,7 +322,7 @@ describe('vue/components/Field', () => { expect(wrapper.html()).toMatchSnapshot(); }); - test('FileUploader', () => { + test('FileUploader', async () => { const wrapper = mount(Field, { propsData: { i18n, @@ -333,10 +338,11 @@ describe('vue/components/Field', () => { userAction: onUserAction, }, }); + await wrapper.trigger('change'); expect(wrapper.html()).toMatchSnapshot(); }); - test('Dropdown', () => { + test('Dropdown', async () => { const wrapper = mount(Field, { propsData: { i18n, @@ -357,10 +363,11 @@ describe('vue/components/Field', () => { userAction: onUserAction, }, }); + await wrapper.trigger('change'); expect(wrapper.html()).toMatchSnapshot(); }); - test('Checkbox', () => { + test('Checkbox', async () => { const wrapper = mount(Field, { propsData: { i18n, @@ -381,10 +388,11 @@ describe('vue/components/Field', () => { userAction: onUserAction, }, }); + await wrapper.trigger('change'); expect(wrapper.html()).toMatchSnapshot(); }); - test('Radio', () => { + test('Radio', async () => { const wrapper = mount(Field, { propsData: { i18n, @@ -405,6 +413,7 @@ describe('vue/components/Field', () => { userAction: onUserAction, }, }); + await wrapper.trigger('change'); expect(wrapper.html()).toMatchSnapshot(); }); }); From fb6565ff7c0cf810e7762ad93395bba16f0a934a Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 27 Aug 2021 04:30:45 +0200 Subject: [PATCH 2/2] Update types definitions --- library/src/scripts/types.d.ts | 6 ++++-- library/src/scripts/vue/components/Field.vue | 12 ++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/library/src/scripts/types.d.ts b/library/src/scripts/types.d.ts index 0b9b9ae..c431def 100644 --- a/library/src/scripts/types.d.ts +++ b/library/src/scripts/types.d.ts @@ -370,7 +370,7 @@ declare module 'gincko' { } declare module 'gincko/react' { - type OUA = (newValue: FormValue) => void; + type OUA = (type: 'click' | 'input', newValue: FormValue) => void; /** Custom React component. */ export type Component = (field: Field & { i18n: I18n; }, onUserAction: OUA) => JSX.Element; @@ -402,6 +402,8 @@ declare module 'gincko/vue' { import Vue from 'vue'; import { ExtendedVue } from 'vue/types/vue.d'; + type OUA = (type: 'click' | 'input', newValue: FormValue) => void; + /** * Dynamic form. */ @@ -417,7 +419,7 @@ declare module 'gincko/vue' { /** List of form's custom components. */ customComponents: { - [type: string]: (field: Field, onUserAction: (newValue: FormValue) => void) => { + [type: string]: (field: Field, onUserAction: OUA) => { component: Vue.Component; props: any; events: any; diff --git a/library/src/scripts/vue/components/Field.vue b/library/src/scripts/vue/components/Field.vue index 128608a..5d7b1b6 100644 --- a/library/src/scripts/vue/components/Field.vue +++ b/library/src/scripts/vue/components/Field.vue @@ -158,7 +158,7 @@ const builtInComponents: Components = { blur: field.options.onBlur, focus: field.options.onFocus, iconClick: field.options.onIconClick, - change: (value): void => onUserAction('input', value), + change: (value: FormValue): void => onUserAction('input', value), }, }), Textarea: (field, onUserAction) => ({ @@ -184,7 +184,7 @@ const builtInComponents: Components = { events: { blur: field.options.onBlur, focus: field.options.onFocus, - change: (value): void => onUserAction('input', value), + change: (value: FormValue): void => onUserAction('input', value), }, }), FileUploader: (field, onUserAction) => ({ @@ -204,7 +204,7 @@ const builtInComponents: Components = { }, events: { focus: field.options.onFocus, - change: (value): void => onUserAction('input', value), + change: (value: FormValue): void => onUserAction('input', value), }, }), Dropdown: (field, onUserAction) => ({ @@ -224,7 +224,7 @@ const builtInComponents: Components = { }, events: { focus: field.options.onFocus, - change: (value): void => onUserAction('input', value), + change: (value: FormValue): void => onUserAction('input', value), }, }), Checkbox: (field, onUserAction) => ({ @@ -242,7 +242,7 @@ const builtInComponents: Components = { }, events: { focus: field.options.onFocus, - change: (value): void => onUserAction('input', value), + change: (value: FormValue): void => onUserAction('input', value), }, }), Radio: (field, onUserAction) => ({ @@ -260,7 +260,7 @@ const builtInComponents: Components = { }, events: { focus: field.options.onFocus, - change: (value): void => onUserAction('input', value), + change: (value: FormValue): void => onUserAction('input', value), }, }), };