From 7c02a20545fc830025ed2b19b8e62cccaa65074e Mon Sep 17 00:00:00 2001 From: Johan Lahti Date: Tue, 5 Sep 2023 09:18:45 +0200 Subject: [PATCH] refactor: replicate old behaviour --- .../__tests__/distribute-resources.spec.ts | 127 +++++++++++++----- .../ListboxGrid/distribute-resources.ts | 64 ++++----- 2 files changed, 123 insertions(+), 68 deletions(-) diff --git a/packages/sn-filter-pane/src/components/ListboxGrid/__tests__/distribute-resources.spec.ts b/packages/sn-filter-pane/src/components/ListboxGrid/__tests__/distribute-resources.spec.ts index 87525c2f..25c01a8f 100644 --- a/packages/sn-filter-pane/src/components/ListboxGrid/__tests__/distribute-resources.spec.ts +++ b/packages/sn-filter-pane/src/components/ListboxGrid/__tests__/distribute-resources.spec.ts @@ -1,59 +1,77 @@ import { IListboxResource } from '../../../hooks/types'; import { - mergeColumnsAndResources, balanceColumns, calculateColumns, calculateExpandPriority, setDefaultValues, hasHeader, + assignListboxesToColumns, balanceColumns, calculateColumns, calculateExpandPriority, setDefaultValues, hasHeader, } from '../distribute-resources'; import { ExpandProps, IColumn, ISize } from '../interfaces'; +const createItem = (overrides = {}) => ({ + expand: false, + fullyExpanded: false, + alwaysExpanded: false, + fits: false, + dense: false, + layout: { + layoutOptions: {}, + qListObject: { qDimensionInfo: { qCardinal: 5 } }, + }, + ...overrides, +}); + const generateColumns = ({ collapsed, expanded }: { collapsed?: number[], expanded?: number[] }) => { const c: IColumn[] = []; + const e: IColumn[] = []; if (collapsed) { collapsed.forEach((itemCount: number) => { - c.push({ itemCount, expand: false }); + c.push({ itemCount, expand: false, items: Array(itemCount).fill(createItem({ expand: false })) }); }); } if (expanded) { - const e: IColumn[] = []; expanded.forEach((itemCount: number) => { - e.push({ itemCount, expand: true }); + e.push({ itemCount, expand: true, items: Array(itemCount).fill(createItem({ expand: true })) }); }); - return [...c, ...e]; } - return c; + return [...c, ...e]; }; const isSmallDevice = false; const expandProps: ExpandProps = { hasHeader: true, isSingleGridLayout: false, + alwaysExpanded: false, }; +const getColumns = (cols: IColumn[]) => cols.map((col) => { + const { expand, itemCount } = col; + return { expand, itemCount }; +}); + describe('distribute resources', () => { describe('balance columns', () => { - it('should balance collpased columns 3-3-3-1 as 3-3-2-2 when last column cant expand one', () => { - const size: ISize = { height: 50, width: 1000, dimensionCount: 10 }; + it('should balance collpased columns 3-3-3-1 as 3-3-2-2 when last column cant expand one, which should only happen when the item is smaller than the available height.', () => { + const size: ISize = { height: 30, width: 1000, dimensionCount: 10 }; const columns = generateColumns({ collapsed: [3, 3, 3, 1] }); const balancedColumns = balanceColumns(size, columns, isSmallDevice, expandProps); - expect(balancedColumns).toEqual(generateColumns({ collapsed: [3, 3, 2, 2] })); + expect(getColumns(balancedColumns)).toEqual(getColumns(generateColumns({ collapsed: [3, 3, 2, 2] }))); }); it('should balance collapsed columns 4-4-1 as 3-3-3 when last column cant expand one', () => { - const size: ISize = { height: 50, width: 1000, dimensionCount: 9 }; + const size: ISize = { height: 30, width: 1000, dimensionCount: 9 }; const columns = generateColumns({ collapsed: [4, 4, 1] }); const balancedColumns = balanceColumns(size, columns, isSmallDevice, expandProps); - expect(balancedColumns).toEqual(generateColumns({ collapsed: [3, 3, 3] })); + expect(getColumns(balancedColumns)).toEqual(getColumns(generateColumns({ collapsed: [3, 3, 3] }))); }); it('should expand last collapsed column when possible and all columns are collapsed', () => { const size: ISize = { height: 400, width: 1000, dimensionCount: 9 }; const columns = generateColumns({ collapsed: [4, 4, 1] }); const balancedColumns = balanceColumns(size, columns, isSmallDevice, expandProps); - expect(balancedColumns).toEqual(generateColumns({ collapsed: [4, 4], expanded: [1] })); + expect(getColumns(balancedColumns)).toEqual(getColumns(generateColumns({ collapsed: [4, 4], expanded: [1] }))); }); it('should expand last collapsed column when possible and mix of collapsed and expanded columns', () => { const size: ISize = { height: 400, width: 1000, dimensionCount: 9 }; const columns = generateColumns({ collapsed: [4, 4, 1], expanded: [1, 1] }); const balancedColumns = balanceColumns(size, columns, isSmallDevice, expandProps); - expect(balancedColumns).toEqual(generateColumns({ collapsed: [4, 4], expanded: [1, 1, 1] })); + expect(getColumns(balancedColumns)).toEqual(getColumns(generateColumns({ collapsed: [4, 4], expanded: [1, 1, 1] }))); }); }); @@ -62,7 +80,7 @@ describe('distribute resources', () => { const columns: IColumn[] = new Array(4).fill({ itemCount: 2 }); const resources: IListboxResource[] = new Array(8).fill({ id: '123' }); expect(columns[0].items).toBeNull; - const { columns: resultColumns } = mergeColumnsAndResources(columns, resources); + const { columns: resultColumns } = assignListboxesToColumns(columns, resources, isSmallDevice); expect(resultColumns[0].items?.length).toBe(2); expect(resultColumns[0].items?.[0].id).toBe('123'); }); @@ -70,7 +88,7 @@ describe('distribute resources', () => { it('should drop dimensions from the end when all wont fit', () => { const columns = generateColumns({ collapsed: [2, 3] }); const resources: IListboxResource[] = new Array(6).fill({ id: '123' }); - const { columns: resultColumns } = mergeColumnsAndResources(columns, resources); + const { columns: resultColumns } = assignListboxesToColumns(columns, resources, isSmallDevice); expect(resultColumns[0].items?.length).toEqual(2); expect(resultColumns[1].items?.length).toEqual(3); }); @@ -78,12 +96,20 @@ describe('distribute resources', () => { describe('calculate columns', () => { it('should handle the case when not all items can be rendered with max one column', () => { + const dimensionCount = 10; + const size: ISize = { height: 400, - dimensionCount: 10, + dimensionCount, width: 5, }; - const result = calculateColumns(size, [], isSmallDevice, expandProps); + const resources = Array(dimensionCount).fill(createItem({ + itemCount: dimensionCount, + expand: false, + })); + + // const columns = [{ itemCount: dimensionCount, items: Array(dimensionCount).fill(createItem({ expand: false })) }]; + const result = calculateColumns(size, [], isSmallDevice, expandProps, resources); expect(result).toEqual([ { expand: false, @@ -94,12 +120,20 @@ describe('distribute resources', () => { }); it('should handle the case when not all items can be rendered with max two columns', () => { + const dimensionCount = 20; const size: ISize = { height: 220, - dimensionCount: 20, + dimensionCount, width: 500, }; - const result = calculateColumns(size, [], isSmallDevice, expandProps); + const resources = Array(dimensionCount).fill(createItem({ + dense: false, + alwaysExpanded: false, + expand: false, + fits: false, + fullyExpanded: false, + })); + const result = calculateColumns(size, [], isSmallDevice, expandProps, resources); expect(result).toEqual([ { expand: false, @@ -114,12 +148,21 @@ describe('distribute resources', () => { }); it('should handle the case when all items will fit and the second column can be expanded', () => { + const dimensionCount = 10; const size: ISize = { height: 400, - dimensionCount: 10, + dimensionCount, width: 500, }; - const result = calculateColumns(size, [], isSmallDevice, expandProps); + + const resources = Array(dimensionCount).fill(createItem({ + dense: false, + alwaysExpanded: false, + expand: false, + fits: false, + fullyExpanded: false, + })); + const result = calculateColumns(size, [], isSmallDevice, expandProps, resources); expect(result).toEqual([ { expand: true, @@ -138,7 +181,7 @@ describe('distribute resources', () => { dimensionCount: 2, width: 500, }; - const result = calculateColumns(size, [], isSmallDevice, expandProps); + const result = calculateColumns(size, [], isSmallDevice, expandProps, []); expect(result).toEqual([ { expand: true, @@ -154,14 +197,15 @@ describe('distribute resources', () => { describe('expand priority', () => { it('should set the expand', () => { + const dimensionCount = 4; const columns = generateColumns({ collapsed: [2], expanded: [1, 1] }); const size: ISize = { height: 400, width: 500, - dimensionCount: 4, + dimensionCount, }; - const { columns: result } = calculateExpandPriority(columns, size, expandProps); + const { columns: result } = calculateExpandPriority(columns, size, expandProps, isSmallDevice); expect(result[0].expand).toBe(false); expect(result[1].expand).toBe(true); expect(result[2].expand).toBe(true); @@ -171,6 +215,8 @@ describe('distribute resources', () => { const resource = { id: '123', expand: false, + fits: false, + fullyExpanded: false, cardinal: 26, layout: { qListObject: { @@ -180,7 +226,11 @@ describe('distribute resources', () => { }, }, }; - const resources: IListboxResource[] = [...Array(2)].map(() => ({ ...resource as IListboxResource })); + const resources: IListboxResource[] = [{ + ...resource, + } as IListboxResource, { + ...resource, + } as IListboxResource]; const size: ISize = { height: 1277, @@ -188,17 +238,17 @@ describe('distribute resources', () => { dimensionCount: resources.length, }; - const calculatedColumns = calculateColumns(size, [], isSmallDevice, expandProps); + const calculatedColumns = calculateColumns(size, [], isSmallDevice, expandProps, resources); const balancedColumns = balanceColumns(size, calculatedColumns, isSmallDevice, expandProps); const defaultValuesResources = setDefaultValues(resources); - const { columns: mergedColumns } = mergeColumnsAndResources(balancedColumns, defaultValuesResources); - const { columns: result } = calculateExpandPriority(mergedColumns, size, expandProps); - expect(result[0].items?.[0].fullyExpanded).toBe(true); + const { columns: mergedColumns } = assignListboxesToColumns(balancedColumns, defaultValuesResources, isSmallDevice); + const { columns: result } = calculateExpandPriority(mergedColumns, size, expandProps, isSmallDevice); + expect(result[0].items?.[1].fullyExpanded).toBe(true); expect(result[0].items?.[0].expand).toBe(true); - expect(result[0].items?.[0].height).toBe('802px'); - expect(result[0].items?.[1].fullyExpanded).toBe(false); + expect(result[0].items?.[1].height).toBe('802px'); + expect(result[0].items?.[0].fullyExpanded).toBe(false); expect(result[0].items?.[1].expand).toBe(true); - expect(result[0].items?.[1].height).toBe('465px'); + expect(result[0].items?.[0].height).toBe('465px'); }); }); @@ -209,9 +259,10 @@ describe('distribute resources', () => { const expandPropsGrid: ExpandProps = { hasHeader: true, isSingleGridLayout: true, + alwaysExpanded: false, }; const balancedColumns = balanceColumns(size, columns, isSmallDevice, expandPropsGrid); - expect(balancedColumns).toEqual(generateColumns({ expanded: [1] })); + expect(getColumns(balancedColumns)).toEqual(getColumns(generateColumns({ expanded: [1] }))); }); it('should expand when title is hidden at height 39px', () => { @@ -220,20 +271,24 @@ describe('distribute resources', () => { const expandPropsGrid: ExpandProps = { hasHeader: false, isSingleGridLayout: true, + alwaysExpanded: false, }; const balancedColumns = balanceColumns(size, columns, isSmallDevice, expandPropsGrid); - expect(balancedColumns).toEqual(generateColumns({ expanded: [1] })); + expect(getColumns(balancedColumns)).toEqual(getColumns(generateColumns({ expanded: [1] }))); }); it('should not expand when not single item in grid', () => { - const size: ISize = { height: 87, width: 1000, dimensionCount: 10 }; + const dimensionCount = 10; + const size: ISize = { height: 87, width: 1000, dimensionCount }; const columns = generateColumns({ collapsed: [1] }); + columns[0].items = Array(dimensionCount).fill(createItem()); const expandPropsGrid: ExpandProps = { hasHeader: true, isSingleGridLayout: false, + alwaysExpanded: false, }; const balancedColumns = balanceColumns(size, columns, isSmallDevice, expandPropsGrid); - expect(balancedColumns).toEqual(generateColumns({ collapsed: [1] })); + expect(getColumns(balancedColumns)).toEqual(getColumns(generateColumns({ collapsed: [1] }))); }); }); diff --git a/packages/sn-filter-pane/src/components/ListboxGrid/distribute-resources.ts b/packages/sn-filter-pane/src/components/ListboxGrid/distribute-resources.ts index 20da1ff2..928c620f 100644 --- a/packages/sn-filter-pane/src/components/ListboxGrid/distribute-resources.ts +++ b/packages/sn-filter-pane/src/components/ListboxGrid/distribute-resources.ts @@ -3,7 +3,6 @@ import { ExpandProps, IColumn, ISize } from './interfaces'; export const COLLAPSED_HEIGHT = 34; export const BUTTON_HEIGHT = 34; -const BUTTON_SPACING = 8; export const ITEM_SPACING = 8; const COLUMN_MIN_WIDTH = 160; const COLUMN_SPACING = 16; @@ -28,7 +27,7 @@ const getExpandedHeightLimit = (expandProps: ExpandProps) => { // heightLimit = expandProps.hasHeader ? EXPANDED_HEADER_HEIGHT + getExpandedRowHeight(false) : getExpandedRowHeight(false); } else if (expandProps.isSingleGridLayout) { const singleGridLimit = expandProps.hasHeader ? EXPANDED_HEADER_HEIGHT + SINGLE_GRID_ROW_HEIGHT : 0; - heightLimit = expandProps.isSingleGridLayout ? singleGridLimit : 90; + heightLimit = singleGridLimit; } return heightLimit; }; @@ -37,7 +36,7 @@ const getExpandedHeightLimit = (expandProps: ExpandProps) => { export const hasHeader = (resource?: IListboxResource) => (!!resource?.layout && resource.layout.title !== '' && resource.layout.showTitle !== false); -const getListBoxMinHeight = (resource: IListboxResource) => { +const getListBoxMinHeight = (resource: IListboxResource, outerWidth = false) => { const { dense = false, collapseMode } = resource.layout.layoutOptions || {}; let h = 0; @@ -46,8 +45,10 @@ const getListBoxMinHeight = (resource: IListboxResource) => { h += getExpandedRowHeight(dense); h += hasHeader(resource) ? EXPANDED_HEADER_HEIGHT : 0; } else { - const collapsedListboxOuterHeight = COLLAPSED_HEIGHT + ITEM_SPACING; - h += collapsedListboxOuterHeight; + h += COLLAPSED_HEIGHT; + } + if (outerWidth) { + h += ITEM_SPACING; } return h; @@ -63,7 +64,7 @@ const howManyListBoxesFit = (columnSize: ISize, resourcesSlice: ListboxResources for (let i = 0, len = resourcesSlice.length; i < len; i++) { resource = resourcesSlice[i]; - accHeight += getListBoxMinHeight(resource); + accHeight += getListBoxMinHeight(resource, true); if (accHeight >= columnOuterHeight) { break; // we cannot fit any more listboxes inside this column } @@ -86,11 +87,11 @@ const countListBoxes = (columns: IColumn[]) => { return count; }; -const getHeightOf = (collapsedItemCount: number) => { - // Subtract one ITEM_SPACING since the first item wont have a margin-top of ITEM_SPACING - const height = (COLLAPSED_HEIGHT + ITEM_SPACING) * collapsedItemCount - ITEM_SPACING; - return Math.max(height, 0); -}; +// const getHeightOf = (collapsedItemCount: number) => { +// // Subtract one ITEM_SPACING since the first item wont have a margin-top of ITEM_SPACING +// const height = (COLLAPSED_HEIGHT + ITEM_SPACING) * collapsedItemCount - ITEM_SPACING; +// return Math.max(height, 0); +// }; const getDimensionCardinal = (item: IListboxResource) => item.layout.qListObject.qDimensionInfo.qCardinal; @@ -123,9 +124,9 @@ function estimateColumnHeight(column: IColumn) { totHeight += getListBoxMinHeight(item); } else if (expand || item.alwaysExpanded) { if (fullyExpanded) { - totHeight += getListBoxMaxHeight(item); + totHeight += getListBoxMaxHeight(item) + ITEM_SPACING + 2; } else if (height) { - const h = Number.parseInt(height, 10); + const h = Number.parseFloat(height); totHeight += (h || getListBoxMinHeight(item)); } else { totHeight += getListBoxMinHeight(item); @@ -135,6 +136,8 @@ function estimateColumnHeight(column: IColumn) { } totHeight += ITEM_SPACING; }); + // Subtract one ITEM_SPACING since the first item wont have a margin-top of ITEM_SPACING + totHeight -= ITEM_SPACING; return totHeight; } @@ -148,12 +151,12 @@ const haveRoomToExpandOne = (size: ISize, column: IColumn, isSmallDevice: boolea if (isSmallDevice) { return false; } - // const spacing = (column.itemCount ?? 0) > 1 ? ITEM_SPACING : 0; - return size.height > estimateColumnHeight(column); // getExpandedHeightLimit(expandProps) + const canExpandOne = size.height >= estimateColumnHeight(column); + return canExpandOne; }; -export const calculateColumns = (size: ISize, columns: IColumn[], isSmallDevice: boolean, expandProps: ExpandProps, resources: ListboxResourcesArr) => { - const canFirstColumnExpand = size.height > getExpandedHeightLimit(expandProps) && !isSmallDevice; +export const calculateColumns = (size: ISize, columns: IColumn[], isSmallDevice: boolean, expandProps: ExpandProps, resources: ListboxResourcesArr = []) => { + const canFirstColumnExpand = size.height > (getExpandedHeightLimit(expandProps) + ITEM_SPACING) && !isSmallDevice; const maxColumns = getMaxColumns(size, isSmallDevice); // this many columns (maximum) can fit inside a Filter pane of current size let usedCount = countListBoxes(columns); const collapsedFitCount = howManyListBoxesFit(size, resources.slice(usedCount)); @@ -269,14 +272,14 @@ export const assignListboxesToColumns = (columns: IColumn[], resources: IListbox }; const setFullyExpanded = (item: IListboxResource) => { - if (parseFloat(item.height) >= getListBoxMaxHeight(item)) { - item.height = `${getListBoxMaxHeight(item)}px`; - item.fullyExpanded = true; - } + // if (parseFloat(item.height) >= getListBoxMaxHeight(item)) { + item.height = `${getListBoxMaxHeight(item)}px`; + item.fullyExpanded = true; + // } }; const expandUntilFull = (sortedItems: IListboxResource[] | undefined, initialLeftOverHeight: number, lastItemIndex: number, isSingleGridLayout = false) => { - if (!sortedItems) return { height: undefined, item: undefined }; + if (!sortedItems) return; let i; let item; let itemFits; @@ -290,14 +293,14 @@ const expandUntilFull = (sortedItems: IListboxResource[] | undefined, initialLef // subtract the target's expanded height to see if it fits. leftOverHeight = initialLeftOverHeight - estimateColumnHeight({ items: [...sortedItems.slice(0, i), ...sortedItems.slice(i + 1)] }); expandedHeight = getListBoxMaxHeight(item); - itemFits = leftOverHeight - expandedHeight >= 0; + itemFits = leftOverHeight >= expandedHeight; const isLastItem = i === lastItemIndex; item.fits = itemFits; if (itemFits && !item.neverExpanded) { item.expand = true; - - item.height = isLastItem ? DEFAULT_CSS_HEIGHT : `${expandedHeight}px`; setFullyExpanded(item); + item.height = `${expandedHeight}px`; + // item.height = isLastItem ? DEFAULT_CSS_HEIGHT : `${expandedHeight}px`; } else if (item.neverExpanded) { item.expand = false; } else { // if (item.alwaysExpanded) { @@ -307,12 +310,11 @@ const expandUntilFull = (sortedItems: IListboxResource[] | undefined, initialLef const fitsAsCollapsed = leftOverHeight - collapsedHeight >= expandedHeightLimit; if (fitsAsCollapsed) { item.expand = true; - item.height = isLastItem ? DEFAULT_CSS_HEIGHT : `${leftOverHeight}px`; + item.height = item.alwaysExpanded && isLastItem ? DEFAULT_CSS_HEIGHT : `${leftOverHeight}px`; } } } } - return { height: undefined, item: undefined }; }; function setDefaultItemSettings({ @@ -373,8 +375,6 @@ export const calculateExpandPriority = (columns: IColumn[], size: ISize, expandP }); if (column.expand) { - const totalExpandedHeight = 0; - if ((sortedItems.length ?? 0) > 1) { // First, expand all force expands (if any) to their smallest possible size (i.e. 1 row + header, if any). // sortedItems.filter((item) => !!item.alwaysExpanded).forEach((item) => { @@ -390,14 +390,14 @@ export const calculateExpandPriority = (columns: IColumn[], size: ISize, expandP const collapsedItems = sortedItems?.filter((item) => !item.expand); - if (leftOverHeight > getExpandedHeightLimit(expandProps) && collapsedItems?.length) { + if (leftOverHeight > (getExpandedHeightLimit(expandProps) + ITEM_SPACING) && collapsedItems?.length) { const item = collapsedItems[0]; if (!item.expand && !isSingleColumn) { if ((sortedItems?.length ?? 0) > 1) { - // Should only set specific height when multiple items in columns, otherwise 100% height from default value + // Should set fixed height only when there are multiple items in columns, otherwise 100% height from default value const spacing = 2 + (collapsedItems.length === 1 ? 0 : ITEM_SPACING); - item.height = `${size.height - estimateColumnHeight({ items: collapsedItems }) - totalExpandedHeight - spacing}px`; + item.height = `${size.height - estimateColumnHeight({ items: collapsedItems }) - spacing}px`; } } item.expand = item.expand || !!(item.alwaysExpanded && item.fits);