diff --git a/app/src/demo/tables/filteredTable/FilteredTable.module.scss b/app/src/demo/tables/filteredTable/FilteredTable.module.scss index 093aea6287..58cf8ef5d8 100644 --- a/app/src/demo/tables/filteredTable/FilteredTable.module.scss +++ b/app/src/demo/tables/filteredTable/FilteredTable.module.scss @@ -32,10 +32,6 @@ min-width: 0; flex-grow: 1; font-family: var(--uui-font); - - .manager-cell { - font-family: var(--uui-font); - } } .icon-container { diff --git a/app/src/demo/tables/filteredTable/columns.tsx b/app/src/demo/tables/filteredTable/columns.tsx index 5852315716..d2ec1d418a 100644 --- a/app/src/demo/tables/filteredTable/columns.tsx +++ b/app/src/demo/tables/filteredTable/columns.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import { Text, Badge, FlexRow, LinkButton, BadgeProps } from '@epam/uui'; import { DataColumnProps, getSeparatedValue } from '@epam/uui-core'; import { Person } from '@epam/uui-docs'; -import css from './FilteredTable.module.scss'; export const personColumns: DataColumnProps[] = [ { @@ -72,7 +71,7 @@ export const personColumns: DataColumnProps[] = [ { key: 'managerName', caption: 'Manager', - render: (p) => , + render: (p) => , grow: 0, width: 150, isSortable: true, diff --git a/app/src/demo/tables/masterDetailedTable/DemoTable.module.scss b/app/src/demo/tables/masterDetailedTable/DemoTable.module.scss index 31e6edcd62..86c11551cd 100644 --- a/app/src/demo/tables/masterDetailedTable/DemoTable.module.scss +++ b/app/src/demo/tables/masterDetailedTable/DemoTable.module.scss @@ -35,10 +35,6 @@ body { } } } - - .manager-cell { - font-family: $font-sans; - } } .icon-container { diff --git a/app/src/demo/tables/masterDetailedTable/columns.tsx b/app/src/demo/tables/masterDetailedTable/columns.tsx index f6ccf985d0..ed7a34e516 100644 --- a/app/src/demo/tables/masterDetailedTable/columns.tsx +++ b/app/src/demo/tables/masterDetailedTable/columns.tsx @@ -53,7 +53,7 @@ export const personColumns: DataColumnProps , + render: (p) => , width: 150, isSortable: true, isFilterActive: (f) => !!f.managerId, diff --git a/app/src/docs/_examples/pickerInput/ArrayPickerInput.example.tsx b/app/src/docs/_examples/pickerInput/ArrayPickerInput.example.tsx index 79f69afce1..1cd08c203d 100644 --- a/app/src/docs/_examples/pickerInput/ArrayPickerInput.example.tsx +++ b/app/src/docs/_examples/pickerInput/ArrayPickerInput.example.tsx @@ -6,7 +6,7 @@ const languageLevels = [ { id: 2, level: 'A1' }, { id: 3, level: 'A1+' }, { id: 4, level: 'A2' }, { id: 5, level: 'A2+' }, { id: 6, level: 'B1' }, { id: 7, level: 'B1+' }, { id: 8, level: 'B2' }, { id: 9, level: 'B2+' }, { id: 10, level: 'C1' }, { id: 11, level: 'C1+' }, { id: 12, level: 'C2' }, ]; -export default function LanguagesMultiPicker() { +export default function ArrayPickerInputExample() { const [singlePickerValue, singleOnValueChange] = useState(null); const [multiPickerValue, multiOnValueChange] = useState(null); diff --git a/app/src/sandbox/tablePaged/columns.tsx b/app/src/sandbox/tablePaged/columns.tsx index 0113e188b2..87a4e6a899 100644 --- a/app/src/sandbox/tablePaged/columns.tsx +++ b/app/src/sandbox/tablePaged/columns.tsx @@ -59,7 +59,7 @@ export const personColumns = [ }, { key: 'managerName', caption: 'Manager', - render: (p) => , + render: (p) => , grow: 0, shrink: 0, width: 150, diff --git a/changelog.md b/changelog.md index cf4149d750..ffef6d0d4f 100644 --- a/changelog.md +++ b/changelog.md @@ -2,27 +2,15 @@ **What's New** -* [uui-core]: helpers cleanup - * Deprecated: - * `LazyLoadedMap` and related: - * `LazyLoadedMapLoadCallback` - * `UNKNOWN` - * `LOADING` - * `LOADED` - * `PENDING` - * `FAILED` - * `LoadingStatus` - * `browser` helper: - * `Browser` - * `getBrowser` +* [uui-core]: helpers cleanup, removed following helpers: + * `LazyLoadedMap` + * `browser' * `Debouncer` * `parseIconViewbox` * `parseStringToCSSProperties` * `getScreenSize` * `urlParser` - * `batch` and related: - * `batch` - * `BatchPromiseOptions` + * `batch` * [useTree]: useTree hook is added. * [Features]: @@ -71,6 +59,9 @@ * [PickerInput]: added property `renderTag` it's a callback for rendering custom Tags in selectionMode: `multi`. * [PickerTogglerTag]: it's a new component, and we recommend it to use in the `renderTag` callback in the PickerInput. +**What's Fixed** +[PickerInput]: fixed setting emptyValue in case of unselecting all picker items + # 5.7.2 - 12.04.2024 **What's Fixed** diff --git a/public/docs/content/examples-pickerInput-AsyncPickerInput.json b/public/docs/content/examples-pickerInput-AsyncPickerInput.json index 622e4d0094..8f312cd1a4 100644 --- a/public/docs/content/examples-pickerInput-AsyncPickerInput.json +++ b/public/docs/content/examples-pickerInput-AsyncPickerInput.json @@ -17,7 +17,7 @@ ] }, { - "text": " ." + "text": "." } ] } diff --git a/uui-components/src/pickers/bindingHelpers.ts b/uui-components/src/pickers/bindingHelpers.ts index ae3b379d41..079ac2117a 100644 --- a/uui-components/src/pickers/bindingHelpers.ts +++ b/uui-components/src/pickers/bindingHelpers.ts @@ -26,10 +26,14 @@ interface PickerBindingHelper { class ArrayBindingHelper implements PickerBindingHelper { emptyValueArray: any[] = []; dataSourceStateToValue(dsState: DataSourceState, props: PickerBaseProps, dataSource: IDataSource) { - if (props.valueType === 'entity') { - return dsState.checked?.map((id) => dataSource && dataSource.getById(id)); + if (dsState && Array.isArray(dsState.checked) && dsState.checked && dsState.checked.length > 0) { + if (props.valueType === 'entity') { + return dsState.checked.map((id) => dataSource && dataSource.getById(id)); + } + return dsState.checked; + } else { + return props.emptyValue; } - return dsState.checked; } applyValueToDataSourceState( @@ -48,7 +52,6 @@ class ArrayBindingHelper implements PickerBindingHelper return { ...dsState, - selectedId: null, checked: checked, filter: props.filter || dsState.filter, sorting: props.sorting ? [props.sorting] : dsState.sorting, @@ -81,7 +84,6 @@ class ScalarBindingHelper implements PickerBindingHelper return { ...dsState, selectedId: selectedId, - checked: null, filter: props.filter || dsState.filter, sorting: props.sorting ? [props.sorting] : dsState.sorting, }; diff --git a/uui-components/src/pickers/hooks/usePicker.ts b/uui-components/src/pickers/hooks/usePicker.ts index d7f42a3e1e..0930119b36 100644 --- a/uui-components/src/pickers/hooks/usePicker.ts +++ b/uui-components/src/pickers/hooks/usePicker.ts @@ -63,7 +63,7 @@ export function usePicker if ((!prevDataSourceState && (dataSourceState.checked?.length || dataSourceState.selectedId != null)) || (prevDataSourceState && ( - prevDataSourceState.checked !== dataSourceState.checked + !isEqual(prevDataSourceState.checked, dataSourceState.checked) || dataSourceState.selectedId !== prevDataSourceState.selectedId )) ) { diff --git a/uui-components/src/pickers/hooks/usePickerInput.ts b/uui-components/src/pickers/hooks/usePickerInput.ts index ae39303d1a..4009178a47 100644 --- a/uui-components/src/pickers/hooks/usePickerInput.ts +++ b/uui-components/src/pickers/hooks/usePickerInput.ts @@ -23,7 +23,7 @@ export function usePickerInput(props: UsePickerInputProps(param searchPosition: 'input', getName: (item: TestItemType) => item.level, value: params.value as TId, - selectionMode: 'single', + emptyValue: [], searchDebounceDelay: 0, }, params) as PickerInputComponentProps; } @@ -1362,7 +1362,33 @@ describe('PickerInput', () => { }); }); - it.each<[undefined | null | []]>([[[]]]) + it.each<[undefined | null | []]>([[[]], [undefined], [null]]) + ('should not call onValueChange on edit search with emptyValue = %s; and return emptyValue = %s on check -> uncheck', async (emptyValue) => { + const { dom, mocks } = await setupPickerInputForTest({ + emptyValue: emptyValue, + value: emptyValue, + selectionMode: 'multi', + searchPosition: 'body', + }); + + fireEvent.click(dom.input); + + const dialog = await screen.findByRole('dialog'); + const bodyInput = await within(dialog).findByPlaceholderText('Search'); + fireEvent.change(bodyInput, { target: { value: 'A' } }); + + expect(mocks.onValueChange).toHaveBeenCalledTimes(0); + + // Test value after check -> uncheck + await PickerInputTestObject.clickOptionCheckbox('A1'); // check + await PickerInputTestObject.clickOptionCheckbox('A1'); // uncheck + + await waitFor(async () => { + expect(mocks.onValueChange).toHaveBeenLastCalledWith(emptyValue); + }); + }); + + it.each<[undefined | null | []]>([[[]], [undefined], [null]]) ('should not call onValueChange on edit search if emptyValue = %s does not equal to the initial value', async (emptyValue) => { const { dom, mocks } = await setupPickerInputForTest({ emptyValue: emptyValue, @@ -1379,5 +1405,24 @@ describe('PickerInput', () => { expect(mocks.onValueChange).toHaveBeenCalledTimes(0); }); + + it('should not call onValueChange on edit search if emptyValue on clear button', async () => { + const { dom, mocks } = await setupPickerInputForTest({ + emptyValue: [], + value: undefined, + selectionMode: 'multi', + searchPosition: 'body', + }); + + fireEvent.click(dom.input); + + await PickerInputTestObject.clickOptionCheckbox('A1'); // check + + await PickerInputTestObject.clickClearAllOptions(); + + await waitFor(async () => { + expect(mocks.onValueChange).toHaveBeenLastCalledWith([]); + }); + }); }); });