From 9b6832177e4fc34a3c9a99c89cff24bc42adef3e Mon Sep 17 00:00:00 2001 From: Andrey Yamanov Date: Mon, 15 Aug 2022 15:28:41 +0200 Subject: [PATCH 01/13] fix(Field): respect onSelectionChange event of ComboBox --- src/components/forms/Form/Field.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/forms/Form/Field.tsx b/src/components/forms/Form/Field.tsx index c382f2f9..da0bd116 100644 --- a/src/components/forms/Form/Field.tsx +++ b/src/components/forms/Form/Field.tsx @@ -84,7 +84,8 @@ function getValueProps(type, value?, onChange?) { } else if (type === 'ComboBox') { return { inputValue: value != null ? value : '', - onInputChange: onChange, + onInputChange: (val) => onChange(val, true), + onSelectionChange: onChange, }; } else if (type === 'Select') { return { @@ -300,7 +301,7 @@ export function Field(allProps: CubeFieldProps) { validateTrigger = defaultValidateTrigger; } - function onChangeHandler(val) { + function onChangeHandler(val, dontTouch) { const field = form.getFieldInstance(fieldName); if (shouldUpdate) { @@ -323,8 +324,9 @@ export function Field(allProps: CubeFieldProps) { form.setFieldValue(fieldName, val, true); if ( - validateTrigger === 'onChange' || - (field && field.errors && field.errors.length) + !dontTouch && + (validateTrigger === 'onChange' || + (field && field.errors && field.errors.length)) ) { form.validateField(fieldName).catch(() => {}); // do nothing on fail } From 0fada22e3e48c9eee4ce20508afcff985ea1f79f Mon Sep 17 00:00:00 2001 From: Andrey Yamanov Date: Mon, 15 Aug 2022 15:44:34 +0200 Subject: [PATCH 02/13] fix(Field): respect onSelectionChange event of ComboBox * 2 --- src/components/forms/Form/Field.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/forms/Form/Field.tsx b/src/components/forms/Form/Field.tsx index da0bd116..77eecd5d 100644 --- a/src/components/forms/Form/Field.tsx +++ b/src/components/forms/Form/Field.tsx @@ -111,7 +111,7 @@ export interface CubeFieldProps /** The id prefix for the field to avoid collisions between forms */ idPrefix?: string; children?: ReactElement | ((CubeFormInstance) => ReactElement); - /** Function that check whether to perform update of the form state. */ + /** Function that checks whether to perform update of the form state. */ shouldUpdate?: boolean | ((prevValues, nextValues) => boolean); /** Validation rules */ rules?: ValidationRule[]; @@ -321,7 +321,7 @@ export function Field(allProps: CubeFieldProps) { } } - form.setFieldValue(fieldName, val, true); + form.setFieldValue(fieldName, val, !dontTouch); if ( !dontTouch && From cc91ed67e87cfc6f172537dece05f28cc3b6fb98 Mon Sep 17 00:00:00 2001 From: Andrey Yamanov Date: Mon, 15 Aug 2022 16:29:31 +0200 Subject: [PATCH 03/13] fix(Field): respect onSelectionChange event of ComboBox * 3 --- src/components/forms/Form/useForm.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/components/forms/Form/useForm.tsx b/src/components/forms/Form/useForm.tsx index 4f7be7cc..312bde92 100644 --- a/src/components/forms/Form/useForm.tsx +++ b/src/components/forms/Form/useForm.tsx @@ -1,7 +1,7 @@ import { useRef, useState } from 'react'; import { dotize } from '../../../tasty'; import { applyRules } from './validation'; -import { FieldTypes, CubeFieldData, SetFieldsArrType } from './types'; +import { CubeFieldData, FieldTypes, SetFieldsArrType } from './types'; export type CubeFormData = { [K in keyof T]?: CubeFieldData; @@ -28,7 +28,7 @@ export class CubeFormInstance< TFormData extends CubeFormData = CubeFormData, > { public forceReRender: () => void = () => {}; - private initialFields = {}; + private initialFields: Partial = {}; private fields: TFormData = {} as TFormData; public ref = {}; public isSubmitting = false; @@ -105,12 +105,16 @@ export class CubeFormInstance< }; getFieldValue(name: Name): T[Name] | undefined { - return this.fields[name]?.value; + return this.fields[name]?.touched + ? this.fields[name]?.value + : this.initialFields[name]; } getFieldsValue(): Partial { return Object.values(this.fields).reduce((map, field) => { - map[field.name as keyof T] = field.value as T[typeof field.name]; + map[field.name as keyof T] = ( + field.touched ? field.value : this.initialFields[field.name] + ) as T[typeof field.name]; return map; }, {} as T); @@ -195,7 +199,7 @@ export class CubeFormInstance< if (!field || !field.rules) return Promise.resolve(); - return applyRules(field.value, field.rules, this) + return applyRules(this.getFieldValue(name), field.rules, this) .then(() => { if (!field.errors || field.errors.length) { field.errors = []; From 4006b865efd891e8112d2109afca340e282c00d7 Mon Sep 17 00:00:00 2001 From: Andrey Yamanov Date: Mon, 15 Aug 2022 17:43:21 +0200 Subject: [PATCH 04/13] fix(Field): respect onSelectionChange event of ComboBox * 4 --- src/components/forms/Form/Field.tsx | 1 + src/components/pickers/ComboBox/ComboBox.tsx | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/components/forms/Form/Field.tsx b/src/components/forms/Form/Field.tsx index 77eecd5d..3a61638f 100644 --- a/src/components/forms/Form/Field.tsx +++ b/src/components/forms/Form/Field.tsx @@ -291,6 +291,7 @@ export function Field(allProps: CubeFieldProps) { child, mergeProps(child.props, { ...getValueProps(inputType), + label: fieldName, name: fieldName, id: fieldId, }), diff --git a/src/components/pickers/ComboBox/ComboBox.tsx b/src/components/pickers/ComboBox/ComboBox.tsx index 9900f6cf..74c4bdbd 100644 --- a/src/components/pickers/ComboBox/ComboBox.tsx +++ b/src/components/pickers/ComboBox/ComboBox.tsx @@ -159,6 +159,7 @@ function ComboBox(props: CubeComboBoxProps, ref) { styles, ...otherProps } = props; + let isAsync = loadingState != null; let { contains } = useFilter({ sensitivity: 'base' }); let [suffixWidth, setSuffixWidth] = useState(0); @@ -293,6 +294,7 @@ function ComboBox(props: CubeComboBoxProps, ref) { {suffix} {!hideTrigger ? ( Date: Mon, 15 Aug 2022 17:56:10 +0200 Subject: [PATCH 05/13] fix(Field): respect onSelectionChange event of ComboBox * 5 --- src/components/actions/Button/Button.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/actions/Button/Button.tsx b/src/components/actions/Button/Button.tsx index 2f3e22e2..d9105289 100644 --- a/src/components/actions/Button/Button.tsx +++ b/src/components/actions/Button/Button.tsx @@ -346,7 +346,7 @@ export const Button = forwardRef( 'single-icon-only': singleIcon, ...mods, }), - [mods], + [mods, isDisabled, isLoading, isSelected, singleIcon], ); return ( From 6ece86a0e74eb8fb8d4b6bb3deffbf63a91a3179 Mon Sep 17 00:00:00 2001 From: Andrey Yamanov Date: Mon, 15 Aug 2022 19:45:25 +0200 Subject: [PATCH 06/13] fix(Field): respect onSelectionChange event of ComboBox * 6 --- src/components/forms/Form/Field.tsx | 4 +-- src/components/forms/Form/types.ts | 1 + src/components/forms/Form/useForm.tsx | 37 ++++++++++++++++++--------- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/components/forms/Form/Field.tsx b/src/components/forms/Form/Field.tsx index 3a61638f..9698aab0 100644 --- a/src/components/forms/Form/Field.tsx +++ b/src/components/forms/Form/Field.tsx @@ -322,7 +322,7 @@ export function Field(allProps: CubeFieldProps) { } } - form.setFieldValue(fieldName, val, !dontTouch); + form.setFieldValue(fieldName, val, !dontTouch, false, dontTouch); if ( !dontTouch && @@ -402,7 +402,7 @@ export function Field(allProps: CubeFieldProps) { Object.assign( newProps, - getValueProps(inputType, field?.value, onChangeHandler), + getValueProps(inputType, field?.inputValue, onChangeHandler), ); const { onChange, onSelectionChange, ...childProps } = child.props; diff --git a/src/components/forms/Form/types.ts b/src/components/forms/Form/types.ts index d1ea934f..b663603d 100644 --- a/src/components/forms/Form/types.ts +++ b/src/components/forms/Form/types.ts @@ -6,6 +6,7 @@ export type CubeFieldData = { readonly name: Name; errors: string[]; value?: Value; + inputValue?: Value; touched?: boolean; rules?: any[]; validating?: boolean; diff --git a/src/components/forms/Form/useForm.tsx b/src/components/forms/Form/useForm.tsx index 312bde92..ea10d99f 100644 --- a/src/components/forms/Form/useForm.tsx +++ b/src/components/forms/Form/useForm.tsx @@ -64,6 +64,7 @@ export class CubeFormInstance< touched?: boolean, skipRender?: boolean, createFields = false, + inputOnly = false, ) => { let flag = false; @@ -85,7 +86,11 @@ export class CubeFormInstance< flag = true; - field.value = newData[name]; + if (!inputOnly) { + field.value = newData[name]; + } + + field.inputValue = newData[name]; if (touched === true) { field.touched = touched; @@ -98,23 +103,19 @@ export class CubeFormInstance< if (flag && !skipRender) { this.forceReRender(); - if (touched) { + if (touched && !inputOnly) { this.onValuesChange && this.onValuesChange(this.getFormData()); } } }; getFieldValue(name: Name): T[Name] | undefined { - return this.fields[name]?.touched - ? this.fields[name]?.value - : this.initialFields[name]; + return this.fields[name]?.value; } getFieldsValue(): Partial { return Object.values(this.fields).reduce((map, field) => { - map[field.name as keyof T] = ( - field.touched ? field.value : this.initialFields[field.name] - ) as T[typeof field.name]; + map[field.name as keyof T] = field.value as T[typeof field.name]; return map; }, {} as T); @@ -142,6 +143,7 @@ export class CubeFormInstance< value: T[Name], touched = false, skipRender = false, + inputOnly = false, ) { const field = this.fields[name]; @@ -149,7 +151,11 @@ export class CubeFormInstance< return; } - field.value = value; + if (inputOnly) { + field.value = value; + } + + field.inputValue = value; if (touched) { field.touched = touched; @@ -161,7 +167,7 @@ export class CubeFormInstance< this.forceReRender(); } - if (touched) { + if (touched && !inputOnly) { this.onValuesChange && this.onValuesChange(this.getFormData()); } } @@ -199,7 +205,7 @@ export class CubeFormInstance< if (!field || !field.rules) return Promise.resolve(); - return applyRules(this.getFieldValue(name), field.rules, this) + return applyRules(field.value, field.rules, this) .then(() => { if (!field.errors || field.errors.length) { field.errors = []; @@ -313,13 +319,20 @@ export class CubeFormInstance< name: Name, data?: Data, ): Data { - return { + let obj = { name, validating: false, touched: false, errors: [], ...data, } as unknown as Data; + + if (obj) { + // condition is here to avoid typing issues + obj.inputValue = obj.value; + } + + return obj; } } From b190dba3002204b2fa35042b5dce0d7924bfb9fe Mon Sep 17 00:00:00 2001 From: Andrey Yamanov Date: Mon, 15 Aug 2022 19:53:34 +0200 Subject: [PATCH 07/13] fix(Field): respect onSelectionChange event of ComboBox * 7 --- src/components/forms/Form/useForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/forms/Form/useForm.tsx b/src/components/forms/Form/useForm.tsx index ea10d99f..56642d8a 100644 --- a/src/components/forms/Form/useForm.tsx +++ b/src/components/forms/Form/useForm.tsx @@ -86,7 +86,7 @@ export class CubeFormInstance< flag = true; - if (!inputOnly) { + if (inputOnly) { field.value = newData[name]; } From 34bced032a437371eda0f69c09b36849d83e3f16 Mon Sep 17 00:00:00 2001 From: Andrey Yamanov Date: Mon, 15 Aug 2022 20:00:36 +0200 Subject: [PATCH 08/13] fix(Field): respect onSelectionChange event of ComboBox * 8 --- src/components/forms/Form/useForm.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/forms/Form/useForm.tsx b/src/components/forms/Form/useForm.tsx index 56642d8a..179baa6d 100644 --- a/src/components/forms/Form/useForm.tsx +++ b/src/components/forms/Form/useForm.tsx @@ -86,7 +86,7 @@ export class CubeFormInstance< flag = true; - if (inputOnly) { + if (!inputOnly) { field.value = newData[name]; } @@ -151,7 +151,7 @@ export class CubeFormInstance< return; } - if (inputOnly) { + if (!inputOnly) { field.value = value; } From 7c7227072c1b00513fbdc74f0fa5efc8e5aead93 Mon Sep 17 00:00:00 2001 From: Andrey Yamanov Date: Wed, 17 Aug 2022 17:51:57 +0200 Subject: [PATCH 09/13] fix(Field): respect onSelectionChange event of ComboBox * 9 --- src/components/forms/Form/Field.tsx | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/components/forms/Form/Field.tsx b/src/components/forms/Form/Field.tsx index 9698aab0..21450040 100644 --- a/src/components/forms/Form/Field.tsx +++ b/src/components/forms/Form/Field.tsx @@ -54,7 +54,12 @@ function getDefaultValidateTrigger(type) { return type === 'Number' || type.includes('Text') ? 'onBlur' : 'onChange'; } -function getValueProps(type, value?, onChange?) { +function getValueProps( + type?: string, + value?, + onChange?, + allowsCustomValue?: boolean, +) { type = type || ''; if (type === 'Number') { @@ -84,7 +89,7 @@ function getValueProps(type, value?, onChange?) { } else if (type === 'ComboBox') { return { inputValue: value != null ? value : '', - onInputChange: (val) => onChange(val, true), + onInputChange: (val) => onChange(val, !allowsCustomValue), onSelectionChange: onChange, }; } else if (type === 'Select') { @@ -402,7 +407,12 @@ export function Field(allProps: CubeFieldProps) { Object.assign( newProps, - getValueProps(inputType, field?.inputValue, onChangeHandler), + getValueProps( + inputType, + field?.inputValue, + onChangeHandler, + child.props.allowsCustomValue, + ), ); const { onChange, onSelectionChange, ...childProps } = child.props; From 959b42fd534afdd6d434cb0d3b5d5d0c3d15d979 Mon Sep 17 00:00:00 2001 From: Andrey Yamanov Date: Thu, 18 Aug 2022 15:12:53 +0300 Subject: [PATCH 10/13] Create orange-ties-itch.md --- .changeset/orange-ties-itch.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/orange-ties-itch.md diff --git a/.changeset/orange-ties-itch.md b/.changeset/orange-ties-itch.md new file mode 100644 index 00000000..778fa44a --- /dev/null +++ b/.changeset/orange-ties-itch.md @@ -0,0 +1,5 @@ +--- +"@cube-dev/ui-kit": patch +--- + +ComboBox now respects `onSelectionChange` event while working inside a form. From 788f5862c8d5057e0dea2e5415ad519a227a4c81 Mon Sep 17 00:00:00 2001 From: Andrey Yamanov Date: Thu, 18 Aug 2022 18:27:32 +0200 Subject: [PATCH 11/13] fix: size limit --- .size-limit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.size-limit.js b/.size-limit.js index 0e03c830..34e36458 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -18,7 +18,7 @@ module.exports = [ }), ); }, - limit: '250kB', + limit: '200kB', }, { name: 'Tree shaking (just a Button)', From e2755882a0775c2afc14842cd0d5b063f3cb96f5 Mon Sep 17 00:00:00 2001 From: Andrey Yamanov Date: Thu, 18 Aug 2022 19:47:31 +0200 Subject: [PATCH 12/13] fix: qa attributes --- .../NewNotifications/Bar/FloatingNotification.tsx | 2 +- .../overlays/NewNotifications/Bar/NotificationsBar.tsx | 2 +- .../NotificationView/NotificationCloseButton.tsx | 2 +- .../NotificationView/notification.test.tsx | 4 +--- .../NewNotifications/Notifications.stories.tsx | 4 ++-- .../overlays/NewNotifications/notification.test.tsx | 4 ++-- src/components/overlays/Toasts/Toasts.stories.tsx | 2 +- src/components/overlays/Toasts/toast.test.tsx | 10 +++++----- src/components/pickers/ComboBox/ComboBox.tsx | 2 +- src/components/pickers/Menu/Menu.stories.tsx | 4 ++-- src/components/pickers/Menu/Menu.tsx | 5 ++++- 11 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/components/overlays/NewNotifications/Bar/FloatingNotification.tsx b/src/components/overlays/NewNotifications/Bar/FloatingNotification.tsx index 7f3d4b92..b0d548eb 100644 --- a/src/components/overlays/NewNotifications/Bar/FloatingNotification.tsx +++ b/src/components/overlays/NewNotifications/Bar/FloatingNotification.tsx @@ -81,7 +81,7 @@ export const FloatingNotification = memo(function FloatingNotification( }} onDismiss={chainedOnDismiss} onClose={onCloseEvent} - qa="floating-notification" + qa="FloatingNotification" /> ); diff --git a/src/components/overlays/NewNotifications/Bar/NotificationsBar.tsx b/src/components/overlays/NewNotifications/Bar/NotificationsBar.tsx index c24ab4fb..7c1e50bd 100644 --- a/src/components/overlays/NewNotifications/Bar/NotificationsBar.tsx +++ b/src/components/overlays/NewNotifications/Bar/NotificationsBar.tsx @@ -71,7 +71,7 @@ export function NotificationsBar(props: NotificationsBarProps): JSX.Element { return ( ', () => { const notification = screen.getByTestId('notification'); - await userEvent.click( - getByTestId(notification, 'notification-close-button'), - ); + await userEvent.click(getByTestId(notification, 'NotificationCloseButton')); expect(onClose).toBeCalledTimes(1); }); diff --git a/src/components/overlays/NewNotifications/Notifications.stories.tsx b/src/components/overlays/NewNotifications/Notifications.stories.tsx index f2515427..03cbc89c 100644 --- a/src/components/overlays/NewNotifications/Notifications.stories.tsx +++ b/src/components/overlays/NewNotifications/Notifications.stories.tsx @@ -42,7 +42,7 @@ DefaultAction.play = async ({ canvasElement }) => { const button = getByRole('button'); await userEvent.click(button); - const notification = getByTestId('floating-notification'); + const notification = getByTestId('FloatingNotification'); await expect(notification).toBeInTheDocument(); }; @@ -389,7 +389,7 @@ WithLongActions.play = async ({ canvasElement }) => { const { getByRole, getByTestId } = within(canvasElement); await userEvent.click(getByRole('button')); - const notification = getByTestId('floating-notification'); + const notification = getByTestId('FloatingNotification'); await expect(notification).toBeInTheDocument(); }; diff --git a/src/components/overlays/NewNotifications/notification.test.tsx b/src/components/overlays/NewNotifications/notification.test.tsx index eedf6f8d..ce40eac6 100644 --- a/src/components/overlays/NewNotifications/notification.test.tsx +++ b/src/components/overlays/NewNotifications/notification.test.tsx @@ -13,7 +13,7 @@ describe('', () => { it('should unmount component by default', () => { const { getByTestId, rerender } = renderWithRoot(); - const notification = getByTestId('floating-notification'); + const notification = getByTestId('FloatingNotification'); rerender( , @@ -27,7 +27,7 @@ describe('', () => { , ); - const notification = getByTestId('floating-notification'); + const notification = getByTestId('FloatingNotification'); rerender( , diff --git a/src/components/overlays/Toasts/Toasts.stories.tsx b/src/components/overlays/Toasts/Toasts.stories.tsx index d3b38cbf..ec0c5415 100644 --- a/src/components/overlays/Toasts/Toasts.stories.tsx +++ b/src/components/overlays/Toasts/Toasts.stories.tsx @@ -28,7 +28,7 @@ UseToast.play = async ({ canvasElement }) => { const button = getByRole('button'); await userEvent.click(button); - const notification = getByTestId('floating-notification'); + const notification = getByTestId('FloatingNotification'); await expect(notification).toBeInTheDocument(); }; diff --git a/src/components/overlays/Toasts/toast.test.tsx b/src/components/overlays/Toasts/toast.test.tsx index 422d5e9b..53386b80 100644 --- a/src/components/overlays/Toasts/toast.test.tsx +++ b/src/components/overlays/Toasts/toast.test.tsx @@ -20,7 +20,7 @@ describe('useToastsApi', () => { }); expect( - screen.queryByTestId('floating-notification'), + screen.queryByTestId('FloatingNotification'), ).not.toBeInTheDocument(); expect(dismiss).toBeCalledTimes(1); @@ -37,7 +37,7 @@ describe('useToastsApi', () => { jest.runAllTimers(); }); - expect(screen.getByTestId('floating-notification')).toBeInTheDocument(); + expect(screen.getByTestId('FloatingNotification')).toBeInTheDocument(); expect(dismiss).not.toBeCalled(); }); @@ -55,14 +55,14 @@ describe('useToastsApi', () => { expect(dismiss).toBeCalled(); expect( - screen.queryByTestId('floating-notification'), + screen.queryByTestId('FloatingNotification'), ).not.toBeInTheDocument(); }); it('should unmount component by default', () => { const { getByTestId, rerender } = renderWithRoot(); - const notification = getByTestId('floating-notification'); + const notification = getByTestId('FloatingNotification'); rerender( , @@ -76,7 +76,7 @@ describe('useToastsApi', () => { , ); - const notification = getByTestId('floating-notification'); + const notification = getByTestId('FloatingNotification'); rerender( , diff --git a/src/components/pickers/ComboBox/ComboBox.tsx b/src/components/pickers/ComboBox/ComboBox.tsx index 5542920f..6b8f1587 100644 --- a/src/components/pickers/ComboBox/ComboBox.tsx +++ b/src/components/pickers/ComboBox/ComboBox.tsx @@ -295,7 +295,7 @@ function ComboBox(props: CubeComboBoxProps, ref) { {suffix} {!hideTrigger ? ( {