From 866c5c02c1ef66cfe1b798a7558acaa6b6855606 Mon Sep 17 00:00:00 2001 From: Navin Karkera Date: Wed, 3 Sep 2025 19:59:24 +0530 Subject: [PATCH 1/7] feat: migration filter and search bar in legacy libraries list --- src/search-manager/SearchFilterWidget.tsx | 7 +- src/studio-home/card-item/index.tsx | 2 +- .../tabs-section/libraries-tab/index.tsx | 131 +++++++++++++++++- src/studio-home/tabs-section/messages.ts | 15 ++ 4 files changed, 148 insertions(+), 7 deletions(-) diff --git a/src/search-manager/SearchFilterWidget.tsx b/src/search-manager/SearchFilterWidget.tsx index cdcd1bf710..b5ef410908 100644 --- a/src/search-manager/SearchFilterWidget.tsx +++ b/src/search-manager/SearchFilterWidget.tsx @@ -27,6 +27,7 @@ const SearchFilterWidget: React.FC<{ children: React.ReactNode; clearFilter: () => void, icon: React.ComponentType; + skipUpdateLabel?: boolean; }> = ({ appliedFilters, ...props }) => { const intl = useIntl(); const [isOpen, open, close] = useToggle(false); @@ -49,8 +50,10 @@ const SearchFilterWidget: React.FC<{ iconAfter={ArrowDropDown} > {props.label} - {appliedFilters.length >= 1 ? <>: {appliedFilters[0].label} : null} - {appliedFilters.length > 1 ? <>, +{appliedFilters.length - 1} : null} + {!props.skipUpdateLabel && appliedFilters.length >= 1 ? <>: {appliedFilters[0].label} : null} + {!props.skipUpdateLabel && appliedFilters.length > 1 ? ( + <>, +{appliedFilters.length - 1} + ) : null} (arr: T[] | undefined, value: string) { + return arr?.filter(o => Object.entries(o).some(entry => String(entry[1]).toLowerCase().includes( + String(value).toLowerCase().trim() + ))); +} + +enum Filter { + migrated = 'migrated', + unmigrated = 'unmigrated', +} + +const BaseFilterState = Object.values(Filter); + +interface MigrationFilterProps { + filters: Filter[]; + setFilters: React.Dispatch>; +} + +const MigrationFilter = ({ filters, setFilters }: MigrationFilterProps) => { + const intl = useIntl(); + const filterLabels = { + [Filter.migrated]: intl.formatMessage(messages.librariesV1TabMigrationFilterMigratedLabel), + [Filter.unmigrated]: intl.formatMessage(messages.librariesV1TabMigrationFilterUnmigratedLabel), + }; + + const toggleFilter = useCallback((filter: Filter) => { + setFilters((oldList: Filter[]) => { + if (oldList.includes(filter)) { + const newList = oldList.filter(m => m !== filter); + if (newList.length === 0) { + return BaseFilterState; + } + return newList; + } + return [...oldList, filter]; + }); + }, [setFilters]); + + const menuItems = () => BaseFilterState.map((item) => ( + { toggleFilter(item); }} + > + {filterLabels[item]} + + )); + + let label = intl.formatMessage(messages.librariesV1TabMigrationFilterLabel); + let appliedFilters: { label: string }[] = []; + if (filters.length === 1) { + label = filterLabels[filters[0]]; + appliedFilters = filters.map(filter => ({ label: filterLabels[filter] })); + } + return ( + setFilters(BaseFilterState)} + icon={FilterList} + skipUpdateLabel + > + + + + {menuItems()} + + + + + ); +}; const LibrariesTab = () => { const intl = useIntl(); const { isLoading, data, isError } = useLibrariesV1Data(); + const [currentPage, setCurrentPage] = useState(1); + const [search, setSearch] = useState(''); + const [migrationFilter, setMigrationFilter] = useState(BaseFilterState); + + let filteredData = findInValues(data?.libraries, search || '') || []; + if (migrationFilter.length === 1) { + filteredData = filteredData.filter((obj) => obj.isMigrated === (migrationFilter[0] === Filter.migrated)); + } + const perPage = 15; + const totalPages = Math.ceil(filteredData.length / perPage); // 15 items per page + const currentPageData = filteredData.slice((currentPage - 1) * perPage, currentPage * perPage); if (isLoading) { return ( @@ -37,7 +128,27 @@ const LibrariesTab = () => { <> {getConfig().ENABLE_LEGACY_LIBRARY_MIGRATOR === 'true' && ()}
- {sortAlphabeticallyArray(data?.libraries || []).map(({ + + {}} + onChange={setSearch} + value={search} + className="mr-4" + placeholder={intl.formatMessage(messages.librariesV2TabLibrarySearchPlaceholder)} + /> + + + {!isLoading && !isError + && ( + <> + {intl.formatMessage(messages.coursesPaginationInfo, { + length: currentPageData?.length || 0, + total: data?.libraries.length || 0, + })} + + )} + + {currentPageData?.map(({ displayName, org, number, url, isMigrated, migratedToKey, migratedToTitle, migratedToCollectionKey, }) => ( { migratedToCollectionKey={migratedToCollectionKey} /> ))} + { + totalPages > 1 + && ( + + ) + }
) diff --git a/src/studio-home/tabs-section/messages.ts b/src/studio-home/tabs-section/messages.ts index db60d65367..1cb1c715db 100644 --- a/src/studio-home/tabs-section/messages.ts +++ b/src/studio-home/tabs-section/messages.ts @@ -106,6 +106,21 @@ const messages = defineMessages({ defaultMessage: 'Review Legacy Libraries', description: 'Label for the button to review legacy libraries', }, + librariesV1TabMigrationFilterLabel: { + id: 'course-authoring.studio-home.libraries.tab.migration.filter.label', + description: 'Label text for migration filter in legacy libraries tab', + defaultMessage: 'Any Migration Status', + }, + librariesV1TabMigrationFilterMigratedLabel: { + id: 'course-authoring.studio-home.libraries.tab.migration.filter.item.migrated.label', + description: 'Label text for migrated migration filter menu item in legacy libraries tab', + defaultMessage: 'Migrated', + }, + librariesV1TabMigrationFilterUnmigratedLabel: { + id: 'course-authoring.studio-home.libraries.tab.migration.filter.item.unmigrated.label', + description: 'Label text for unmigrated migration filter menu item in legacy libraries tab', + defaultMessage: 'Unmigrated', + }, }); export default messages; From 2dccfdfab34a316724b3bff11a2d5b6bb2a1a0eb Mon Sep 17 00:00:00 2001 From: Navin Karkera Date: Wed, 3 Sep 2025 20:44:49 +0530 Subject: [PATCH 2/7] fixup! feat: migration filter and search bar in legacy libraries list --- src/search-manager/SearchFilterWidget.tsx | 8 +- .../tabs-section/libraries-tab/index.tsx | 73 ++++++++++++------- 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/src/search-manager/SearchFilterWidget.tsx b/src/search-manager/SearchFilterWidget.tsx index b5ef410908..c2a6a3536d 100644 --- a/src/search-manager/SearchFilterWidget.tsx +++ b/src/search-manager/SearchFilterWidget.tsx @@ -12,7 +12,7 @@ import messages from './messages'; /** * A button that represents a filter on the search. - * If the filter is active, the button displays the currently applied values. + * If the filter is active and skipLabelUpdate is not true, the button displays the currently applied values. * So when no filter is active it may look like: * [ Type ▼ ] * Or when a filter is active and limited to two values, it may look like: @@ -27,7 +27,7 @@ const SearchFilterWidget: React.FC<{ children: React.ReactNode; clearFilter: () => void, icon: React.ComponentType; - skipUpdateLabel?: boolean; + skipLabelUpdate?: boolean; }> = ({ appliedFilters, ...props }) => { const intl = useIntl(); const [isOpen, open, close] = useToggle(false); @@ -50,8 +50,8 @@ const SearchFilterWidget: React.FC<{ iconAfter={ArrowDropDown} > {props.label} - {!props.skipUpdateLabel && appliedFilters.length >= 1 ? <>: {appliedFilters[0].label} : null} - {!props.skipUpdateLabel && appliedFilters.length > 1 ? ( + {!props.skipLabelUpdate && appliedFilters.length >= 1 ? <>: {appliedFilters[0].label} : null} + {!props.skipLabelUpdate && appliedFilters.length > 1 ? ( <>, +{appliedFilters.length - 1} ) : null} diff --git a/src/studio-home/tabs-section/libraries-tab/index.tsx b/src/studio-home/tabs-section/libraries-tab/index.tsx index c06b26c51c..e92173f636 100644 --- a/src/studio-home/tabs-section/libraries-tab/index.tsx +++ b/src/studio-home/tabs-section/libraries-tab/index.tsx @@ -39,6 +39,16 @@ const MigrationFilter = ({ filters, setFilters }: MigrationFilterProps) => { [Filter.unmigrated]: intl.formatMessage(messages.librariesV1TabMigrationFilterUnmigratedLabel), }; + let label = intl.formatMessage(messages.librariesV1TabMigrationFilterLabel); + // Set appliedFilters to empty list to indicate clear state + let appliedFilters: { label: string }[] = []; + if (filters.length === 1) { + // Update label to display selected filter item, i.e., Migrated or Unmigrated + label = filterLabels[filters[0]]; + // Only update appliedFilters if a single option is selected else show clear state. + appliedFilters = filters.map(filter => ({ label: filterLabels[filter] })); + } + const toggleFilter = useCallback((filter: Filter) => { setFilters((oldList: Filter[]) => { if (oldList.includes(filter)) { @@ -52,7 +62,7 @@ const MigrationFilter = ({ filters, setFilters }: MigrationFilterProps) => { }); }, [setFilters]); - const menuItems = () => BaseFilterState.map((item) => ( + const menuItems = useCallback(() => BaseFilterState.map((item) => ( { > {filterLabels[item]} - )); + )), [toggleFilter, BaseFilterState]); - let label = intl.formatMessage(messages.librariesV1TabMigrationFilterLabel); - let appliedFilters: { label: string }[] = []; - if (filters.length === 1) { - label = filterLabels[filters[0]]; - appliedFilters = filters.map(filter => ({ label: filterLabels[filter] })); - } return ( setFilters(BaseFilterState)} + clearFilter={() => setFilters(BaseFilterState)} // On clear select both migrated and unmigrated options. icon={FilterList} - skipUpdateLabel + skipLabelUpdate > { let filteredData = findInValues(data?.libraries, search || '') || []; if (migrationFilter.length === 1) { + // filter results by migrated status filteredData = filteredData.filter((obj) => obj.isMigrated === (migrationFilter[0] === Filter.migrated)); } - const perPage = 15; - const totalPages = Math.ceil(filteredData.length / perPage); // 15 items per page + const perPage = 10; + const totalPages = Math.ceil(filteredData.length / perPage); const currentPageData = filteredData.slice((currentPage - 1) * perPage, currentPage * perPage); if (isLoading) { @@ -113,6 +118,19 @@ const LibrariesTab = () => { ); } + + if (isError) { + + + {intl.formatMessage(messages.librariesTabErrorMessage)} + + )} + /> + } + return ( isError ? ( { migratedToCollectionKey={migratedToCollectionKey} /> ))} - { - totalPages > 1 - && ( - - ) - } - - - ) - ); + { + totalPages > 1 + && ( + + ) + } + + + ) }; export default LibrariesTab; From 90062786a3118eef9c80324ba9f5ed55ea53e5fa Mon Sep 17 00:00:00 2001 From: Navin Karkera Date: Thu, 4 Sep 2025 16:19:48 +0530 Subject: [PATCH 3/7] test: search and migration filter --- .../factories/mockApiResponses.tsx | 9 ++ .../tabs-section/TabsSection.test.tsx | 62 ++++++++--- .../tabs-section/libraries-tab/index.tsx | 103 ++++++++---------- 3 files changed, 105 insertions(+), 69 deletions(-) diff --git a/src/studio-home/factories/mockApiResponses.tsx b/src/studio-home/factories/mockApiResponses.tsx index 7407f5343f..295971f7d0 100644 --- a/src/studio-home/factories/mockApiResponses.tsx +++ b/src/studio-home/factories/mockApiResponses.tsx @@ -103,6 +103,15 @@ export const generateGetStudioHomeLibrariesApiResponse = () => ({ migratedToCollectionKey: 'imported-content', migratedToCollectionTitle: 'Imported content', }, + { + displayName: 'MBA 1', + libraryKey: 'library-v1:MBA+1234', + url: '/library/library-v1:MBA+1234', + org: 'Cambridge', + number: '1234', + canEdit: true, + isMigrated: false, + }, ], }); diff --git a/src/studio-home/tabs-section/TabsSection.test.tsx b/src/studio-home/tabs-section/TabsSection.test.tsx index 72a4b16d68..be46979471 100644 --- a/src/studio-home/tabs-section/TabsSection.test.tsx +++ b/src/studio-home/tabs-section/TabsSection.test.tsx @@ -12,6 +12,7 @@ import { fireEvent, screen, act, + within, } from '@src/testUtils'; import messages from '../messages'; import tabMessages from './messages'; @@ -269,7 +270,7 @@ describe('', () => { beforeEach(async () => { await axiosMock.onGet(courseApiLinkV2).reply(200, generateGetStudioCoursesApiResponseV2()); }); - it('should switch to Legacy Libraries tab and render specific v1 library details', async () => { + it('should switch to Legacy Libraries tab and render - search and filter should work as expected', async () => { await axiosMock.onGet(getStudioHomeApiUrl()).reply(200, generateGetStudioHomeDataApiResponse()); await axiosMock.onGet(libraryApiLink).reply(200, generateGetStudioHomeLibrariesApiResponse()); render(); @@ -280,12 +281,57 @@ describe('', () => { await user.click(librariesTab); expect(librariesTab).toHaveClass('active'); + const panel = await screen.findByRole('tabpanel', { hidden: false }); expect(await screen.findByText(studioHomeMock.libraries[0].displayName)).toBeVisible(); expect( await screen.findByText(`${studioHomeMock.libraries[0].org} / ${studioHomeMock.libraries[0].number}`), ).toBeVisible(); + + // Migration info should be displayed + const migratedContent = generateGetStudioHomeLibrariesApiResponse().libraries[1]; + expect(await screen.findByText(migratedContent.displayName)).toBeVisible(); + const newTitleElement = await screen.findAllByText(migratedContent.migratedToTitle!); + expect(newTitleElement[0]).toBeVisible(); + expect(newTitleElement[0]).toHaveAttribute('href', `/library/${migratedContent.migratedToKey}`); + expect(newTitleElement[1]).toHaveAttribute( + 'href', + `/library/${migratedContent.migratedToKey}/collection/${migratedContent.migratedToCollectionKey}`, + ); + + // Check total count display + expect(await within(panel).findByText('Showing 3 of 3')).toBeInTheDocument(); + + // Test search functionality + const searchField = await within(panel).findByPlaceholderText('Search'); + + fireEvent.change(searchField, { target: { value: 'Legacy' } }); + // Should only show 1 result i.e. migratedContent.displayName + expect(await within(panel).findByText('Showing 1 of 3')).toBeInTheDocument(); + expect(await within(panel).findByText(migratedContent.displayName)).toBeVisible(); + // Should not show other items. + expect(within(panel).queryByText( + generateGetStudioHomeLibrariesApiResponse().libraries[0].displayName, + )).not.toBeInTheDocument(); + // reset search + fireEvent.change(searchField, { target: { value: '' } }); + + // Test migration filter + const filter = await within(panel).findByRole('button', { name: 'Any Migration Status' }); + await user.click(filter); + const migratedOption = await within(panel).findByRole('checkbox', { name: 'Migrated' }); + // This should uncheck Migrated option as all options are selected by default + await user.click(migratedOption); + // Should only show 2 result i.e. unmigrated libraries + expect(await within(panel).findByText('Showing 2 of 3')).toBeInTheDocument(); + const unmigratedOption = await within(panel).findByRole('checkbox', { name: 'Unmigrated' }); + // Un-checking both options should reset the state to both checked. + await user.click(unmigratedOption); + expect(migratedOption).toBeChecked(); + expect(unmigratedOption).toBeChecked(); + // Should only show all 3 results + expect(await within(panel).findByText('Showing 3 of 3')).toBeInTheDocument(); }); it('should switch to Libraries tab and render specific v2 library details', async () => { @@ -331,17 +377,6 @@ describe('', () => { expect( await screen.findByText(`${studioHomeMock.libraries[0].org} / ${studioHomeMock.libraries[0].number}`), ).toBeVisible(); - - // Migration info should be displayed - const migratedContent = generateGetStudioHomeLibrariesApiResponse().libraries[1]; - expect(await screen.findByText(migratedContent.displayName)).toBeVisible(); - const newTitleElement = await screen.findAllByText(migratedContent.migratedToTitle!); - expect(newTitleElement[0]).toBeVisible(); - expect(newTitleElement[0]).toHaveAttribute('href', `/library/${migratedContent.migratedToKey}`); - expect(newTitleElement[1]).toHaveAttribute( - 'href', - `/library/${migratedContent.migratedToKey}/collection/${migratedContent.migratedToCollectionKey}`, - ); }); it('should switch to Libraries tab and render specific v2 library details ("v2 only" mode)', async () => { @@ -402,10 +437,11 @@ describe('', () => { await axiosMock.onGet(getStudioHomeApiUrl()).reply(200, generateGetStudioHomeDataApiResponse()); await axiosMock.onGet(libraryApiLink).reply(404); render(); + const user = userEvent.setup(); await executeThunk(fetchStudioHomeData(), store.dispatch); const librariesTab = await screen.findByText(tabMessages.legacyLibrariesTabTitle.defaultMessage); - fireEvent.click(librariesTab); + await user.click(librariesTab); expect(librariesTab).toHaveClass('active'); diff --git a/src/studio-home/tabs-section/libraries-tab/index.tsx b/src/studio-home/tabs-section/libraries-tab/index.tsx index e92173f636..874083b2e5 100644 --- a/src/studio-home/tabs-section/libraries-tab/index.tsx +++ b/src/studio-home/tabs-section/libraries-tab/index.tsx @@ -16,7 +16,7 @@ import SearchFilterWidget from '../../../search-manager/SearchFilterWidget'; function findInValues(arr: T[] | undefined, value: string) { return arr?.filter(o => Object.entries(o).some(entry => String(entry[1]).toLowerCase().includes( - String(value).toLowerCase().trim() + String(value).toLowerCase().trim(), ))); } @@ -77,7 +77,7 @@ const MigrationFilter = ({ filters, setFilters }: MigrationFilterProps) => { setFilters(BaseFilterState)} // On clear select both migrated and unmigrated options. + clearFilter={() => setFilters(BaseFilterState)} // On clear select both migrated and unmigrated options. icon={FilterList} skipLabelUpdate > @@ -120,19 +120,7 @@ const LibrariesTab = () => { } if (isError) { - - - {intl.formatMessage(messages.librariesTabErrorMessage)} - - )} - /> - } - - return ( - isError ? ( + return ( { )} /> - ) : ( - <> - {getConfig().ENABLE_LEGACY_LIBRARY_MIGRATOR === 'true' && ()} -
- - {}} - onChange={setSearch} - value={search} - className="mr-4" - placeholder={intl.formatMessage(messages.librariesV2TabLibrarySearchPlaceholder)} - /> - - - {!isLoading && !isError - && ( - <> - {intl.formatMessage(messages.coursesPaginationInfo, { - length: currentPageData?.length || 0, - total: data?.libraries.length || 0, - })} - - )} - - {currentPageData?.map(({ - displayName, org, number, url, isMigrated, migratedToKey, migratedToTitle, migratedToCollectionKey, - }) => ( - - ))} + ); + } + + return ( + <> + +
+ + {}} + onChange={setSearch} + value={search} + className="mr-4" + placeholder={intl.formatMessage(messages.librariesV2TabLibrarySearchPlaceholder)} + /> + + + {!isLoading && !isError + && ( + <> + {intl.formatMessage(messages.coursesPaginationInfo, { + length: currentPageData?.length, + total: data.libraries.length, + })} + + )} + + {currentPageData?.map(({ + displayName, org, number, url, isMigrated, migratedToKey, migratedToTitle, migratedToCollectionKey, + }) => ( + + ))} { totalPages > 1 && ( @@ -196,7 +187,7 @@ const LibrariesTab = () => { }
- ) + ); }; export default LibrariesTab; From 9f87db112779c5847b52ce7abb606e235e34b68d Mon Sep 17 00:00:00 2001 From: Navin Karkera Date: Thu, 4 Sep 2025 17:04:55 +0530 Subject: [PATCH 4/7] fixup! test: search and migration filter --- src/generic/key-utils.test.ts | 25 +++++++++++++++++++ src/generic/key-utils.ts | 6 ++++- .../tabs-section/TabsSection.test.tsx | 17 +++++++++++-- .../tabs-section/libraries-tab/index.tsx | 2 ++ 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/generic/key-utils.test.ts b/src/generic/key-utils.test.ts index 60e4fe4ec6..cd97c26688 100644 --- a/src/generic/key-utils.test.ts +++ b/src/generic/key-utils.test.ts @@ -6,6 +6,7 @@ import { isLibraryKey, isLibraryV1Key, normalizeContainerType, + parseLibraryKey, } from './key-utils'; describe('component utils', () => { @@ -69,6 +70,30 @@ describe('component utils', () => { } }); + describe('parseLibraryKey', () => { + for (const [input, expected] of [ + ['lib:org:lib', { org: 'org', lib: 'lib' }], + ['lib:OpenCraftX:ALPHA', { org: 'OpenCraftX', lib: 'ALPHA' }], + ] as const) { + it(`returns '${JSON.stringify(expected)}' for learning context key '${input}'`, () => { + expect(parseLibraryKey(input)).toStrictEqual(expected); + }); + } + + for (const input of [ + '', + undefined, + null, + 'not a key', + 'lb:foo', + 'lb:org:lib:html:id', + ]) { + it(`throws an exception for library key '${input}'`, () => { + expect(() => parseLibraryKey(input as any)).toThrow(`Invalid libraryKey: ${input}`); + }); + } + }); + describe('isLibraryV1Key', () => { for (const [input, expected] of [ ['library-v1:AximX+L1', true], diff --git a/src/generic/key-utils.ts b/src/generic/key-utils.ts index a98bce9ab0..88dbb14bf4 100644 --- a/src/generic/key-utils.ts +++ b/src/generic/key-utils.ts @@ -17,7 +17,11 @@ export function getBlockType(usageKey: string): string { * Parses a library key and returns the organization and library name as an object. */ export function parseLibraryKey(libraryKey: string): { org: string, lib: string } { - const [, org, lib] = libraryKey?.split(':') || []; + const splitKey = libraryKey?.split(':') || []; + if (splitKey.length !== 3) { + throw new Error(`Invalid libraryKey: ${libraryKey}`); + } + const [, org, lib] = splitKey; if (org && lib) { return { org, lib }; } diff --git a/src/studio-home/tabs-section/TabsSection.test.tsx b/src/studio-home/tabs-section/TabsSection.test.tsx index be46979471..c63aac248f 100644 --- a/src/studio-home/tabs-section/TabsSection.test.tsx +++ b/src/studio-home/tabs-section/TabsSection.test.tsx @@ -320,17 +320,30 @@ describe('', () => { // Test migration filter const filter = await within(panel).findByRole('button', { name: 'Any Migration Status' }); await user.click(filter); - const migratedOption = await within(panel).findByRole('checkbox', { name: 'Migrated' }); + let migratedOption = await within(panel).findByRole('checkbox', { name: 'Migrated' }); // This should uncheck Migrated option as all options are selected by default await user.click(migratedOption); // Should only show 2 result i.e. unmigrated libraries expect(await within(panel).findByText('Showing 2 of 3')).toBeInTheDocument(); + // test clearing filter + const clearFilter = await within(panel).findByRole('button', { name: 'Clear Filter' }); + await user.click(clearFilter); + // Should show all 3 results + expect(await within(panel).findByText('Showing 3 of 3')).toBeInTheDocument(); + // Open the filter again + await user.click(filter); + // Reload migratedOption as clearing and opening the filter again creates a new modal + migratedOption = await within(panel).findByRole('checkbox', { name: 'Migrated' }); const unmigratedOption = await within(panel).findByRole('checkbox', { name: 'Unmigrated' }); + // both options should be selected by default - even after clearing + expect(migratedOption).toBeChecked(); + expect(unmigratedOption).toBeChecked(); // Un-checking both options should reset the state to both checked. await user.click(unmigratedOption); + await user.click(migratedOption); expect(migratedOption).toBeChecked(); expect(unmigratedOption).toBeChecked(); - // Should only show all 3 results + // Should show all 3 results expect(await within(panel).findByText('Showing 3 of 3')).toBeInTheDocument(); }); diff --git a/src/studio-home/tabs-section/libraries-tab/index.tsx b/src/studio-home/tabs-section/libraries-tab/index.tsx index 874083b2e5..793c8b7fe9 100644 --- a/src/studio-home/tabs-section/libraries-tab/index.tsx +++ b/src/studio-home/tabs-section/libraries-tab/index.tsx @@ -58,6 +58,7 @@ const MigrationFilter = ({ filters, setFilters }: MigrationFilterProps) => { } return newList; } + // istanbul ignore next return [...oldList, filter]; }); }, [setFilters]); @@ -139,6 +140,7 @@ const LibrariesTab = () => {
{}} onChange={setSearch} value={search} From fcacd8f97fbe5e39cfee451f50d483cb52be4c8f Mon Sep 17 00:00:00 2001 From: Navin Karkera Date: Thu, 4 Sep 2025 20:00:11 +0530 Subject: [PATCH 5/7] refactor: use @src --- src/studio-home/tabs-section/libraries-tab/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/studio-home/tabs-section/libraries-tab/index.tsx b/src/studio-home/tabs-section/libraries-tab/index.tsx index 793c8b7fe9..8fc510d8d0 100644 --- a/src/studio-home/tabs-section/libraries-tab/index.tsx +++ b/src/studio-home/tabs-section/libraries-tab/index.tsx @@ -10,9 +10,9 @@ import AlertMessage from '@src/generic/alert-message'; import { useLibrariesV1Data } from '@src/studio-home/data/apiHooks'; import CardItem from '@src/studio-home/card-item'; import { useCallback, useState } from 'react'; +import SearchFilterWidget from '@src/search-manager/SearchFilterWidget'; import messages from '../messages'; import { MigrateLegacyLibrariesAlert } from './MigrateLegacyLibrariesAlert'; -import SearchFilterWidget from '../../../search-manager/SearchFilterWidget'; function findInValues(arr: T[] | undefined, value: string) { return arr?.filter(o => Object.entries(o).some(entry => String(entry[1]).toLowerCase().includes( From 036a05d2889413547461f33623f9c5d6c68161ca Mon Sep 17 00:00:00 2001 From: Navin Karkera Date: Fri, 5 Sep 2025 11:03:24 +0530 Subject: [PATCH 6/7] refactor: search function --- src/studio-home/tabs-section/libraries-tab/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/studio-home/tabs-section/libraries-tab/index.tsx b/src/studio-home/tabs-section/libraries-tab/index.tsx index 8fc510d8d0..b079a2c3bb 100644 --- a/src/studio-home/tabs-section/libraries-tab/index.tsx +++ b/src/studio-home/tabs-section/libraries-tab/index.tsx @@ -14,9 +14,9 @@ import SearchFilterWidget from '@src/search-manager/SearchFilterWidget'; import messages from '../messages'; import { MigrateLegacyLibrariesAlert } from './MigrateLegacyLibrariesAlert'; -function findInValues(arr: T[] | undefined, value: string) { - return arr?.filter(o => Object.entries(o).some(entry => String(entry[1]).toLowerCase().includes( - String(value).toLowerCase().trim(), +function findInValues(arr: T[] | undefined, searchValue: string) { + return arr?.filter(o => Object.values(o).some(value => String(value).toLowerCase().includes( + String(searchValue).toLowerCase().trim(), ))); } From 7d50dddc24cbf61cc4e1209a997fb2708841c2cd Mon Sep 17 00:00:00 2001 From: Navin Karkera Date: Thu, 25 Sep 2025 11:31:26 +0530 Subject: [PATCH 7/7] fix: rebase issues --- src/studio-home/tabs-section/libraries-tab/index.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/studio-home/tabs-section/libraries-tab/index.tsx b/src/studio-home/tabs-section/libraries-tab/index.tsx index b079a2c3bb..5ac5c9d26b 100644 --- a/src/studio-home/tabs-section/libraries-tab/index.tsx +++ b/src/studio-home/tabs-section/libraries-tab/index.tsx @@ -98,7 +98,7 @@ const MigrationFilter = ({ filters, setFilters }: MigrationFilterProps) => { const LibrariesTab = () => { const intl = useIntl(); - const { isLoading, data, isError } = useLibrariesV1Data(); + const { isPending, data, isError } = useLibrariesV1Data(); const [currentPage, setCurrentPage] = useState(1); const [search, setSearch] = useState(''); const [migrationFilter, setMigrationFilter] = useState(BaseFilterState); @@ -112,7 +112,7 @@ const LibrariesTab = () => { const totalPages = Math.ceil(filteredData.length / perPage); const currentPageData = filteredData.slice((currentPage - 1) * perPage, currentPage * perPage); - if (isLoading) { + if (isPending) { return ( @@ -136,7 +136,7 @@ const LibrariesTab = () => { return ( <> - + {getConfig().ENABLE_LEGACY_LIBRARY_MIGRATOR === 'true' && ()}
{ /> - {!isLoading && !isError + {!isPending && !isError && ( <> {intl.formatMessage(messages.coursesPaginationInfo, { length: currentPageData?.length, - total: data.libraries.length, + total: data?.libraries.length, })} )}