From 69ba392bf91b1ab368f91af864c7ba0d0ed65a4c Mon Sep 17 00:00:00 2001 From: Vio Date: Tue, 26 Dec 2023 23:50:46 +0100 Subject: [PATCH] fix(ui): Respect filters order --- .../bundle-assets/bundle-assets.jsx | 58 +++++++------- .../bundle-modules/bundle-modules.utils.ts | 38 ++++++---- packages/ui/src/types.d.ts | 5 +- .../src/ui/filters/__tests__/filters.utils.ts | 76 ++++++++++--------- packages/ui/src/ui/filters/filters.tsx | 16 ++-- packages/ui/src/ui/filters/filters.utils.ts | 17 ++++- 6 files changed, 114 insertions(+), 96 deletions(-) diff --git a/packages/ui/src/components/bundle-assets/bundle-assets.jsx b/packages/ui/src/components/bundle-assets/bundle-assets.jsx index 5ff8fe6a12..35364bc4aa 100644 --- a/packages/ui/src/components/bundle-assets/bundle-assets.jsx +++ b/packages/ui/src/components/bundle-assets/bundle-assets.jsx @@ -36,19 +36,11 @@ const RUNS_LABELS = [RUN_TITLE_CURRENT, RUN_TITLE_BASELINE]; const getFileTypeFilters = (filters) => Object.entries(FILE_TYPE_LABELS) - .map(([id, label]) => ({ - [id]: { - label, - defaultValue: get(filters, `${ASSET_FILE_TYPE}.${id}`, true), - }, - })) - .reduce( - (agg, current) => ({ - ...agg, - ...current, - }), - {}, - ); + .map(([key, label]) => ({ + key, + label, + defaultValue: get(filters, `${ASSET_FILE_TYPE}.${key}`, true), + })); const getFilters = ({ compareMode, filters }) => ({ [ASSET_FILTERS.CHANGED]: { @@ -58,26 +50,32 @@ const getFilters = ({ compareMode, filters }) => ({ }, [ASSET_ENTRY_TYPE]: { label: 'Entry type', - [ASSET_FILTERS.ENTRY]: { - label: 'Entry', - defaultValue: get(filters, `${ASSET_ENTRY_TYPE}.${ASSET_FILTERS.ENTRY}`, true), - }, - [ASSET_FILTERS.INITIAL]: { - label: 'Initial', - defaultValue: get(filters, `${ASSET_ENTRY_TYPE}.${ASSET_FILTERS.INITIAL}`, true), - }, - [ASSET_FILTERS.CHUNK]: { - label: 'Chunk', - defaultValue: get(filters, `${ASSET_ENTRY_TYPE}.${ASSET_FILTERS.CHUNK}`, true), - }, - [ASSET_FILTERS.OTHER]: { - label: 'Other', - defaultValue: get(filters, `${ASSET_ENTRY_TYPE}.${ASSET_FILTERS.OTHER}`, true), - }, + children: [ + { + key: ASSET_FILTERS.ENTRY, + label: 'Entry', + defaultValue: get(filters, `${ASSET_ENTRY_TYPE}.${ASSET_FILTERS.ENTRY}`, true), + }, + { + key: ASSET_FILTERS.INITIAL, + label: 'Initial', + defaultValue: get(filters, `${ASSET_ENTRY_TYPE}.${ASSET_FILTERS.INITIAL}`, true), + }, + { + key: ASSET_FILTERS.CHUNK, + label: 'Chunk', + defaultValue: get(filters, `${ASSET_ENTRY_TYPE}.${ASSET_FILTERS.CHUNK}`, true), + }, + { + key: ASSET_FILTERS.OTHER, + label: 'Other', + defaultValue: get(filters, `${ASSET_ENTRY_TYPE}.${ASSET_FILTERS.OTHER}`, true), + }, + ], }, [ASSET_FILE_TYPE]: { label: 'File type', - ...getFileTypeFilters(filters), + children: getFileTypeFilters(filters), }, }); diff --git a/packages/ui/src/components/bundle-modules/bundle-modules.utils.ts b/packages/ui/src/components/bundle-modules/bundle-modules.utils.ts index 154ad697c7..470f664409 100644 --- a/packages/ui/src/components/bundle-modules/bundle-modules.utils.ts +++ b/packages/ui/src/components/bundle-modules/bundle-modules.utils.ts @@ -3,6 +3,7 @@ import get from 'lodash/get'; import isEmpty from 'lodash/isEmpty'; import union from 'lodash/union'; import uniqBy from 'lodash/uniqBy'; +import orderBy from 'lodash/orderBy'; import type { MetricRunInfo, ReportMetricRow } from '@bundle-stats/utils'; // @ts-ignore import { MODULE_PATH_PACKAGES, type Module } from '@bundle-stats/utils/lib-esm/webpack'; @@ -164,34 +165,41 @@ export const generateFilterFieldsData = (params: GetFiltersFormDataParams): Filt result[MODULE_SOURCE_TYPE] = { label: I18N.SOURCE, - [MODULE_FILTERS.FIRST_PARTY]: { - label: I18N.SOURCE_FIRST_PARTY, - defaultValue: get(filters, `${MODULE_SOURCE_TYPE}.${MODULE_FILTERS.FIRST_PARTY}`, true), - }, - [MODULE_FILTERS.THIRD_PARTY]: { - label: I18N.SOURCE_THIRD_PARTY, - defaultValue: get(filters, `${MODULE_SOURCE_TYPE}.${MODULE_FILTERS.THIRD_PARTY}`, true), - }, + children: [ + { + key: MODULE_FILTERS.FIRST_PARTY, + label: I18N.SOURCE_FIRST_PARTY, + defaultValue: get(filters, `${MODULE_SOURCE_TYPE}.${MODULE_FILTERS.FIRST_PARTY}`, true), + }, + { + key: MODULE_FILTERS.THIRD_PARTY, + label: I18N.SOURCE_THIRD_PARTY, + defaultValue: get(filters, `${MODULE_SOURCE_TYPE}.${MODULE_FILTERS.THIRD_PARTY}`, true), + }, + ], } as FilterGroupFieldData; if (!isEmpty(chunks)) { - const chunksFilter = { label: I18N.CHUNK } as FilterGroupFieldData; - chunks?.forEach((chunk) => { - chunksFilter[chunk.id] = { + const chunksFilter = { label: I18N.CHUNK, children: [] } as FilterGroupFieldData; + const chunksOrderedByName = orderBy(chunks, 'name'); + chunksOrderedByName.forEach((chunk) => { + chunksFilter.children.push({ + key: chunk.id, label: chunk.name, defaultValue: filters[`${MODULE_CHUNK}.${chunk.id}`] ?? true, - }; + }); }); result[MODULE_CHUNK] = chunksFilter; } - const moduleFileTypeFilter = { label: I18N.FILE_TYPE } as FilterGroupFieldData; + const moduleFileTypeFilter = { label: I18N.FILE_TYPE, children: [] } as FilterGroupFieldData; MODULE_SOURCE_FILE_TYPES.forEach((fileType: string) => { - moduleFileTypeFilter[fileType] = { + moduleFileTypeFilter.children.push({ + key: fileType, label: FILE_TYPE_LABELS[fileType as keyof typeof FILE_TYPE_LABELS], defaultValue: get(filters, `${MODULE_FILE_TYPE}.${fileType}`, true), - }; + }); }); result[MODULE_FILE_TYPE] = moduleFileTypeFilter; diff --git a/packages/ui/src/types.d.ts b/packages/ui/src/types.d.ts index 41d27dcf59..3464760610 100644 --- a/packages/ui/src/types.d.ts +++ b/packages/ui/src/types.d.ts @@ -8,5 +8,8 @@ interface FilterFieldData { defaultValue: boolean; disabled?: boolean; } -type FilterGroupFieldData = { label: string } & { [key: string]: FilterFieldData }; +type FilterGroupFieldData = { + label: string; + children: Array<{ key: string } & FilterFieldData>; +}; type FilterFieldsData = Record; diff --git a/packages/ui/src/ui/filters/__tests__/filters.utils.ts b/packages/ui/src/ui/filters/__tests__/filters.utils.ts index fbb57bb13b..22c2cfbc33 100644 --- a/packages/ui/src/ui/filters/__tests__/filters.utils.ts +++ b/packages/ui/src/ui/filters/__tests__/filters.utils.ts @@ -4,13 +4,13 @@ describe('UI / Filters / utils', () => { describe('getGroupFiltersLabelSuffix', () => { test('should return none', () => { const suffix = getGroupFiltersLabelSuffix([ - ['CSS', { label: 'CSS', defaultValue: false }], - ['JS', { label: 'JS', defaultValue: false }], - ['IMG', { label: 'IMG', defaultValue: false }], - ['MEDIA', { label: 'Media', defaultValue: false }], - ['FONTS', { label: 'Fonts', defaultValue: false }], - ['HTML', { label: 'HTML', defaultValue: false }], - ['OTHER', { label: 'Other', defaultValue: false }], + { key: 'CSS', label: 'CSS', defaultValue: false }, + { key: 'JS', label: 'JS', defaultValue: false }, + { key: 'IMG', label: 'IMG', defaultValue: false }, + { key: 'MEDIA', label: 'Media', defaultValue: false }, + { key: 'FONTS', label: 'Fonts', defaultValue: false }, + { key: 'HTML', label: 'HTML', defaultValue: false }, + { key: 'OTHER', label: 'Other', defaultValue: false }, ]); expect(suffix).toEqual('none'); @@ -18,26 +18,26 @@ describe('UI / Filters / utils', () => { test('should return all', () => { const suffix = getGroupFiltersLabelSuffix([ - ['CSS', { label: 'CSS', defaultValue: true }], - ['JS', { label: 'JS', defaultValue: true }], - ['IMG', { label: 'IMG', defaultValue: true }], - ['MEDIA', { label: 'Media', defaultValue: true }], - ['FONTS', { label: 'Fonts', defaultValue: true }], - ['HTML', { label: 'HTML', defaultValue: true }], - ['OTHER', { label: 'Other', defaultValue: true }], + { key: 'CSS', label: 'CSS', defaultValue: true }, + { key: 'JS', label: 'JS', defaultValue: true }, + { key: 'IMG', label: 'IMG', defaultValue: true }, + { key: 'MEDIA', label: 'Media', defaultValue: true }, + { key: 'FONTS', label: 'Fonts', defaultValue: true }, + { key: 'HTML', label: 'HTML', defaultValue: true }, + { key: 'OTHER', label: 'Other', defaultValue: true }, ]); expect(suffix).toEqual('all'); }); test('should return checked filters', () => { const suffix = getGroupFiltersLabelSuffix([ - ['CSS', { label: 'CSS', defaultValue: true }], - ['JS', { label: 'JS', defaultValue: true }], - ['IMG', { label: 'IMG', defaultValue: false }], - ['MEDIA', { label: 'Media', defaultValue: false }], - ['FONTS', { label: 'Fonts', defaultValue: false }], - ['HTML', { label: 'HTML', defaultValue: false }], - ['OTHER', { label: 'Other', defaultValue: false }], + { key: 'CSS', label: 'CSS', defaultValue: true }, + { key: 'JS', label: 'JS', defaultValue: true }, + { key: 'IMG', label: 'IMG', defaultValue: false }, + { key: 'MEDIA', label: 'Media', defaultValue: false }, + { key: 'FONTS', label: 'Fonts', defaultValue: false }, + { key: 'HTML', label: 'HTML', defaultValue: false }, + { key: 'OTHER', label: 'Other', defaultValue: false }, ]); expect(suffix).toEqual('CSS, JS'); @@ -45,13 +45,13 @@ describe('UI / Filters / utils', () => { test('should crop last filter if need it', () => { const suffix = getGroupFiltersLabelSuffix([ - ['CSS', { label: 'CSS', defaultValue: true }], - ['JS', { label: 'JS', defaultValue: true }], - ['IMG', { label: 'IMG', defaultValue: true }], - ['MEDIA', { label: 'Meeeeeeedia', defaultValue: true }], - ['FONTS', { label: 'Fonts', defaultValue: false }], - ['HTML', { label: 'HTML', defaultValue: false }], - ['OTHER', { label: 'Other', defaultValue: false }], + { key: 'CSS', label: 'CSS', defaultValue: true }, + { key: 'JS', label: 'JS', defaultValue: true }, + { key: 'IMG', label: 'IMG', defaultValue: true }, + { key: 'MEDIA', label: 'Meeeeeeedia', defaultValue: true }, + { key: 'FONTS', label: 'Fonts', defaultValue: false }, + { key: 'HTML', label: 'HTML', defaultValue: false }, + { key: 'OTHER', label: 'Other', defaultValue: false }, ]); expect(suffix).toEqual('CSS, JS, IMG, Mee...'); @@ -59,13 +59,13 @@ describe('UI / Filters / utils', () => { test('should crop and add remaining filters count', () => { const suffix = getGroupFiltersLabelSuffix([ - ['CSS', { label: 'CSS', defaultValue: true }], - ['JS', { label: 'JS', defaultValue: true }], - ['IMG', { label: 'IMG', defaultValue: true }], - ['MEDIA', { label: 'Media', defaultValue: true }], - ['FONTS', { label: 'Fonts', defaultValue: true }], - ['HTML', { label: 'HTML', defaultValue: true }], - ['OTHER', { label: 'Other', defaultValue: false }], + { key: 'CSS', label: 'CSS', defaultValue: true }, + { key: 'JS', label: 'JS', defaultValue: true }, + { key: 'IMG', label: 'IMG', defaultValue: true }, + { key: 'MEDIA', label: 'Media', defaultValue: true }, + { key: 'FONTS', label: 'Fonts', defaultValue: true }, + { key: 'HTML', label: 'HTML', defaultValue: true }, + { key: 'OTHER', label: 'Other', defaultValue: false }, ]); expect(suffix).toEqual('CSS, JS, IMG, Media +2'); @@ -81,8 +81,10 @@ describe('UI / Filters / utils', () => { }, fileType: { label: 'File type', - CSS: { label: 'CSS', defaultValue: false }, - JS: { label: 'JS', defaultValue: true }, + children: [ + { key: 'CSS', label: 'CSS', defaultValue: false }, + { key: 'JS', label: 'JS', defaultValue: true }, + ], }, }); diff --git a/packages/ui/src/ui/filters/filters.tsx b/packages/ui/src/ui/filters/filters.tsx index 43dec2a473..0c627daec9 100644 --- a/packages/ui/src/ui/filters/filters.tsx +++ b/packages/ui/src/ui/filters/filters.tsx @@ -75,14 +75,10 @@ const FilterGroup = (props: FilterGroupProps) => { toggleFilters, } = props; - const { label: groupLabel, ...groupData } = data; + const { label: groupLabel, children: groupItems } = data; - const groupItems = Object.entries(groupData); - const groupCheckboxes = groupItems.filter( - ([_, item]) => typeof item?.defaultValue !== 'undefined', - ); - const areAllGroupItemsChecked = groupCheckboxes - .map(([itemKey]) => values?.[`${groupKey}.${itemKey}`]) + const areAllGroupItemsChecked = groupItems + .map(({ key: itemKey }) => values?.[`${groupKey}.${itemKey}`]) .reduce((agg, val) => agg && val, true); const filterSuffix = getGroupFiltersLabelSuffix(groupItems); @@ -98,8 +94,8 @@ const FilterGroup = (props: FilterGroupProps) => { const getOnGroupCheck = (value: boolean, overrides = {}) => () => { - const newFilters = groupCheckboxes.reduce( - (agg, [itemKey]) => ({ + const newFilters = groupItems.reduce( + (agg, { key: itemKey }) => ({ ...agg, [`${groupKey}.${itemKey}`]: value, }), @@ -123,7 +119,7 @@ const FilterGroup = (props: FilterGroupProps) => { return ( <>
- {groupItems.map(([itemKey, itemData]) => { + {groupItems.map(({ key: itemKey, ...itemData }) => { const id = [groupKey, itemKey].join('.'); const getOnOnlyClick = () => getOnGroupCheck(false, { [id]: true }); diff --git a/packages/ui/src/ui/filters/filters.utils.ts b/packages/ui/src/ui/filters/filters.utils.ts index a963458190..69ad80bcce 100644 --- a/packages/ui/src/ui/filters/filters.utils.ts +++ b/packages/ui/src/ui/filters/filters.utils.ts @@ -9,9 +9,9 @@ export const LABELS = { ALL: 'all', }; -export const getGroupFiltersLabelSuffix = (filters: Array<[string, FilterFieldData]>): string => { +export const getGroupFiltersLabelSuffix = (filters: FilterGroupFieldData['children']): string => { const filterCount = filters.length; - const checkedFilters = filters.filter(([_, { defaultValue }]) => defaultValue); + const checkedFilters = filters.filter(({ defaultValue }) => defaultValue); const filterCheckedCount = checkedFilters.length; if (filterCheckedCount === 0) { @@ -23,7 +23,7 @@ export const getGroupFiltersLabelSuffix = (filters: Array<[string, FilterFieldDa } // eslint-disable-next-line no-unused-vars - const checkedFilterLabels = checkedFilters.map(([_, { label }]) => label); + const checkedFilterLabels = checkedFilters.map(({ label }) => label); let suffix = ''; let inlinedLabelCount = 0; @@ -76,6 +76,17 @@ export const getInitialValues = ( }; } + if ('children' in filtersData) { + const result = {} as Record; + + (filtersData as FilterGroupFieldData).children.forEach(({ key: childKey, defaultValue }) => { + const fullKey = [...(key ? [key] : []), childKey].join('.'); + result[fullKey] = defaultValue; + }); + + return result; + } + let result = {}; Object.entries(filtersData).forEach(([groupKey, groupFilters]) => {