From aa908efba3b3c099ede7903441876a6b0e4bdbd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B4mulo=20Penido?= Date: Thu, 1 May 2025 19:31:43 -0300 Subject: [PATCH 1/2] fix: several library unit page UX bugs (#1868) * fix: rename "Organize" tab to "Manage" * fix: duplicate key warnings * fix: uniform messages while adding to collection * fix: do not allow units be added to a unit (cherry picked from commit 0fdc460c5b59ab54ec55c56bdc2246f328ec7a17) --- .../LibraryAuthoringPage.test.tsx | 2 +- .../add-content/AddContent.test.tsx | 2 +- .../add-content/AddContent.tsx | 9 ++++- .../PickLibraryContentModal.test.tsx | 11 +++++-- .../add-content/PickLibraryContentModal.tsx | 11 +++++-- src/library-authoring/add-content/messages.ts | 11 ++----- .../common/context/SidebarContext.tsx | 2 +- .../components/ContainerCard.tsx | 4 ++- .../containers/ContainerOrganize.tsx | 4 +-- src/library-authoring/containers/UnitInfo.tsx | 4 +-- src/library-authoring/containers/messages.ts | 20 +++++------ .../ManageCollections.test.tsx | 6 ++-- .../manage-collections/ManageCollections.tsx | 5 +-- .../generic/manage-collections/messages.ts | 10 ------ src/library-authoring/generic/messages.ts | 16 +++++++++ .../units/LibraryUnitBlocks.tsx | 33 ++++++++++--------- .../units/LibraryUnitPage.tsx | 2 +- 17 files changed, 88 insertions(+), 64 deletions(-) create mode 100644 src/library-authoring/generic/messages.ts diff --git a/src/library-authoring/LibraryAuthoringPage.test.tsx b/src/library-authoring/LibraryAuthoringPage.test.tsx index ce07e0c70f..670c3af1a6 100644 --- a/src/library-authoring/LibraryAuthoringPage.test.tsx +++ b/src/library-authoring/LibraryAuthoringPage.test.tsx @@ -433,7 +433,7 @@ describe('', () => { const { getByRole, queryByText } = within(sidebar); await waitFor(() => expect(queryByText(displayName)).toBeInTheDocument()); - expect(getByRole('tab', { selected: true })).toHaveTextContent('Organize'); + expect(getByRole('tab', { selected: true })).toHaveTextContent('Manage'); const closeButton = getByRole('button', { name: /close/i }); fireEvent.click(closeButton); diff --git a/src/library-authoring/add-content/AddContent.test.tsx b/src/library-authoring/add-content/AddContent.test.tsx index 8820981c0e..09f01bd174 100644 --- a/src/library-authoring/add-content/AddContent.test.tsx +++ b/src/library-authoring/add-content/AddContent.test.tsx @@ -272,7 +272,7 @@ describe('', () => { await waitFor(() => expect(axiosMock.history.post[0].url).toEqual(pasteUrl)); await waitFor(() => expect(axiosMock.history.patch.length).toEqual(1)); await waitFor(() => expect(axiosMock.history.patch[0].url).toEqual(collectionComponentUrl)); - expect(mockShowToast).toHaveBeenCalledWith('There was an error linking the content to this collection.'); + expect(mockShowToast).toHaveBeenCalledWith('Failed to add content to collection.'); }); it('should stop user from pasting unsupported blocks and show toast', async () => { diff --git a/src/library-authoring/add-content/AddContent.tsx b/src/library-authoring/add-content/AddContent.tsx index 7610b743f6..8ddbdae354 100644 --- a/src/library-authoring/add-content/AddContent.tsx +++ b/src/library-authoring/add-content/AddContent.tsx @@ -29,6 +29,8 @@ import { useLibraryContext } from '../common/context/LibraryContext'; import { PickLibraryContentModal } from './PickLibraryContentModal'; import { blockTypes } from '../../editors/data/constants/app'; +import { ContentType as LibraryContentTypes } from '../routes'; +import genericMessages from '../generic/messages'; import messages from './messages'; import type { BlockTypeMetadata } from '../data/api'; import { getContainerTypeFromId, ContainerType } from '../../generic/key-utils'; @@ -114,6 +116,9 @@ const AddContentView = ({ blockType: 'libraryContent', }; + const extraFilter = unitId ? ['NOT block_type = "unit"', 'NOT type = "collections"'] : undefined; + const visibleTabs = unitId ? [LibraryContentTypes.components] : undefined; + return ( <> {(collectionId || unitId) && componentPicker && ( @@ -123,6 +128,8 @@ const AddContentView = ({ )} @@ -301,7 +308,7 @@ const AddContent = () => { const linkComponent = (opaqueKey: string) => { if (collectionId) { addComponentsToCollectionMutation.mutateAsync([opaqueKey]).catch(() => { - showToast(intl.formatMessage(messages.errorAssociateComponentToCollectionMessage)); + showToast(intl.formatMessage(genericMessages.manageCollectionsFailed)); }); } if (unitId) { diff --git a/src/library-authoring/add-content/PickLibraryContentModal.test.tsx b/src/library-authoring/add-content/PickLibraryContentModal.test.tsx index a73ce8118e..982b657e8b 100644 --- a/src/library-authoring/add-content/PickLibraryContentModal.test.tsx +++ b/src/library-authoring/add-content/PickLibraryContentModal.test.tsx @@ -92,7 +92,10 @@ describe('', () => { } }); expect(onClose).toHaveBeenCalled(); - expect(mockShowToast).toHaveBeenCalledWith('Content linked successfully.'); + const text = context === 'collection' + ? 'Content added to collection.' + : 'Content linked successfully.'; + expect(mockShowToast).toHaveBeenCalledWith(text); }); it(`show error when api call fails (${context})`, async () => { @@ -130,8 +133,10 @@ describe('', () => { } }); expect(onClose).toHaveBeenCalled(); - const name = context === 'collection' ? 'collection' : 'container'; - expect(mockShowToast).toHaveBeenCalledWith(`There was an error linking the content to this ${name}.`); + const text = context === 'collection' + ? 'Failed to add content to collection.' + : 'There was an error linking the content to this container.'; + expect(mockShowToast).toHaveBeenCalledWith(text); }); }); }); diff --git a/src/library-authoring/add-content/PickLibraryContentModal.tsx b/src/library-authoring/add-content/PickLibraryContentModal.tsx index f71f40d081..4f243f5bf1 100644 --- a/src/library-authoring/add-content/PickLibraryContentModal.tsx +++ b/src/library-authoring/add-content/PickLibraryContentModal.tsx @@ -6,6 +6,8 @@ import { ToastContext } from '../../generic/toast-context'; import { useLibraryContext } from '../common/context/LibraryContext'; import type { SelectedComponent } from '../common/context/ComponentPickerContext'; import { useAddItemsToCollection, useAddComponentsToContainer } from '../data/apiHooks'; +import genericMessages from '../generic/messages'; +import type { ContentType } from '../routes'; import messages from './messages'; interface PickLibraryContentModalFooterProps { @@ -32,12 +34,14 @@ interface PickLibraryContentModalProps { isOpen: boolean; onClose: () => void; extraFilter?: string[]; + visibleTabs?: ContentType[], } export const PickLibraryContentModal: React.FC = ({ isOpen, onClose, extraFilter, + visibleTabs, }) => { const intl = useIntl(); @@ -69,16 +73,16 @@ export const PickLibraryContentModal: React.FC = ( if (collectionId) { updateCollectionItemsMutation.mutateAsync(usageKeys) .then(() => { - showToast(intl.formatMessage(messages.successAssociateComponentMessage)); + showToast(intl.formatMessage(genericMessages.manageCollectionsSuccess)); }) .catch(() => { - showToast(intl.formatMessage(messages.errorAssociateComponentToCollectionMessage)); + showToast(intl.formatMessage(genericMessages.manageCollectionsFailed)); }); } if (unitId) { updateUnitComponentsMutation.mutateAsync(usageKeys) .then(() => { - showToast(intl.formatMessage(messages.successAssociateComponentMessage)); + showToast(intl.formatMessage(messages.successAssociateComponentToContainerMessage)); }) .catch(() => { showToast(intl.formatMessage(messages.errorAssociateComponentToContainerMessage)); @@ -109,6 +113,7 @@ export const PickLibraryContentModal: React.FC = ( componentPickerMode="multiple" onChangeComponentSelection={setSelectedComponents} extraFilter={extraFilter} + visibleTabs={visibleTabs} /> ); diff --git a/src/library-authoring/add-content/messages.ts b/src/library-authoring/add-content/messages.ts index 120b5896fb..cd7e688c5e 100644 --- a/src/library-authoring/add-content/messages.ts +++ b/src/library-authoring/add-content/messages.ts @@ -84,15 +84,10 @@ const messages = defineMessages({ + ' The {detail} text provides more information about the error.' ), }, - successAssociateComponentMessage: { - id: 'course-authoring.library-authoring.associate-collection-content.success.text', + successAssociateComponentToContainerMessage: { + id: 'course-authoring.library-authoring.associate-container-content.success.text', defaultMessage: 'Content linked successfully.', - description: 'Message when linking of content to a collection in library is success', - }, - errorAssociateComponentToCollectionMessage: { - id: 'course-authoring.library-authoring.associate-collection-content.error.text', - defaultMessage: 'There was an error linking the content to this collection.', - description: 'Message when linking of content to a collection in library fails', + description: 'Message when linking of content to a container in library is success', }, errorAssociateComponentToContainerMessage: { id: 'course-authoring.library-authoring.associate-container-content.error.text', diff --git a/src/library-authoring/common/context/SidebarContext.tsx b/src/library-authoring/common/context/SidebarContext.tsx index 83e545e8eb..f40ccfec1d 100644 --- a/src/library-authoring/common/context/SidebarContext.tsx +++ b/src/library-authoring/common/context/SidebarContext.tsx @@ -36,7 +36,7 @@ export const isComponentInfoTab = (tab: string): tab is ComponentInfoTab => ( export const UNIT_INFO_TABS = { Preview: 'preview', - Organize: 'organize', + Manage: 'manage', Usage: 'usage', Settings: 'settings', } as const; diff --git a/src/library-authoring/components/ContainerCard.tsx b/src/library-authoring/components/ContainerCard.tsx index 45268aa150..b2f66e963f 100644 --- a/src/library-authoring/components/ContainerCard.tsx +++ b/src/library-authoring/components/ContainerCard.tsx @@ -147,7 +147,9 @@ const ContainerCardPreview = ({ containerId, showMaxChildren = 5 }: ContainerCar } return (
{blockPreview} diff --git a/src/library-authoring/containers/ContainerOrganize.tsx b/src/library-authoring/containers/ContainerOrganize.tsx index e6585785e2..5c336abc04 100644 --- a/src/library-authoring/containers/ContainerOrganize.tsx +++ b/src/library-authoring/containers/ContainerOrganize.tsx @@ -85,7 +85,7 @@ const ContainerOrganize = () => { > - {intl.formatMessage(messages.organizeTabTagsTitle, { count: tagsCount })} + {intl.formatMessage(messages.manageTabTagsTitle, { count: tagsCount })} @@ -113,7 +113,7 @@ const ContainerOrganize = () => { > - {intl.formatMessage(messages.organizeTabCollectionsTitle, { count: collectionsCount })} + {intl.formatMessage(messages.manageTabCollectionsTitle, { count: collectionsCount })} diff --git a/src/library-authoring/containers/UnitInfo.tsx b/src/library-authoring/containers/UnitInfo.tsx index 164962fcc6..df70791e14 100644 --- a/src/library-authoring/containers/UnitInfo.tsx +++ b/src/library-authoring/containers/UnitInfo.tsx @@ -120,7 +120,7 @@ const UnitInfo = () => { useEffect(() => { // Show Organize tab if JumpToAddCollections action is set in sidebarComponentInfo if (jumpToCollections) { - setSidebarTab(UNIT_INFO_TABS.Organize); + setSidebarTab(UNIT_INFO_TABS.Manage); } }, [jumpToCollections, setSidebarTab]); @@ -166,7 +166,7 @@ const UnitInfo = () => { onSelect={setSidebarTab} > {renderTab(UNIT_INFO_TABS.Preview, , intl.formatMessage(messages.previewTabTitle))} - {renderTab(UNIT_INFO_TABS.Organize, , intl.formatMessage(messages.organizeTabTitle))} + {renderTab(UNIT_INFO_TABS.Manage, , intl.formatMessage(messages.manageTabTitle))} {renderTab(UNIT_INFO_TABS.Settings, 'Unit Settings', intl.formatMessage(messages.settingsTabTitle))} diff --git a/src/library-authoring/containers/messages.ts b/src/library-authoring/containers/messages.ts index 9ebae29e1b..fe8e0139ed 100644 --- a/src/library-authoring/containers/messages.ts +++ b/src/library-authoring/containers/messages.ts @@ -11,20 +11,20 @@ const messages = defineMessages({ defaultMessage: 'Preview', description: 'Title for preview tab', }, - organizeTabTitle: { - id: 'course-authoring.library-authoring.container-sidebar.organize-tab.title', - defaultMessage: 'Organize', - description: 'Title for organize tab', + manageTabTitle: { + id: 'course-authoring.library-authoring.container-sidebar.manage-tab.title', + defaultMessage: 'Manage', + description: 'Title for manage tab', }, - organizeTabTagsTitle: { - id: 'course-authoring.library-authoring.container-sidebar.organize-tab.tags.title', + manageTabTagsTitle: { + id: 'course-authoring.library-authoring.container-sidebar.manage-tab.tags.title', defaultMessage: 'Tags ({count})', - description: 'Title for tags section in organize tab', + description: 'Title for tags section in manage tab', }, - organizeTabCollectionsTitle: { - id: 'course-authoring.library-authoring.container-sidebar.organize-tab.collections.title', + manageTabCollectionsTitle: { + id: 'course-authoring.library-authoring.container-sidebar.manage-tab.collections.title', defaultMessage: 'Collections ({count})', - description: 'Title for collections section in organize tab', + description: 'Title for collections section in manage tab', }, publishContainerButton: { id: 'course-authoring.library-authoring.container-sidebar.publish-button', diff --git a/src/library-authoring/generic/manage-collections/ManageCollections.test.tsx b/src/library-authoring/generic/manage-collections/ManageCollections.test.tsx index b73dd0d837..7f7b79d436 100644 --- a/src/library-authoring/generic/manage-collections/ManageCollections.test.tsx +++ b/src/library-authoring/generic/manage-collections/ManageCollections.test.tsx @@ -77,7 +77,7 @@ describe('', () => { await waitFor(() => { expect(axiosMock.history.patch.length).toEqual(1); }); - expect(mockShowToast).toHaveBeenCalledWith('Item collections updated'); + expect(mockShowToast).toHaveBeenCalledWith('Content added to collection.'); expect(JSON.parse(axiosMock.history.patch[0].data)).toEqual({ collection_keys: ['my-first-collection', 'my-second-collection'], }); @@ -103,7 +103,7 @@ describe('', () => { await waitFor(() => { expect(axiosMock.history.patch.length).toEqual(1); }); - expect(mockShowToast).toHaveBeenCalledWith('Item collections updated'); + expect(mockShowToast).toHaveBeenCalledWith('Content added to collection.'); expect(JSON.parse(axiosMock.history.patch[0].data)).toEqual({ collection_keys: ['my-first-collection', 'my-second-collection'], }); @@ -133,7 +133,7 @@ describe('', () => { expect(JSON.parse(axiosMock.history.patch[0].data)).toEqual({ collection_keys: ['my-second-collection'], }); - expect(mockShowToast).toHaveBeenCalledWith('Failed to update item collections'); + expect(mockShowToast).toHaveBeenCalledWith('Failed to add content to collection.'); expect(screen.queryByRole('search')).not.toBeInTheDocument(); }); diff --git a/src/library-authoring/generic/manage-collections/ManageCollections.tsx b/src/library-authoring/generic/manage-collections/ManageCollections.tsx index 41bc36ce7c..96591b3274 100644 --- a/src/library-authoring/generic/manage-collections/ManageCollections.tsx +++ b/src/library-authoring/generic/manage-collections/ManageCollections.tsx @@ -16,6 +16,7 @@ import { ToastContext } from '../../../generic/toast-context'; import { CollectionMetadata } from '../../data/api'; import { useLibraryContext } from '../../common/context/LibraryContext'; import { SidebarActions, useSidebarContext } from '../../common/context/SidebarContext'; +import genericMessages from '../messages'; import messages from './messages'; interface ManageCollectionsProps { @@ -50,9 +51,9 @@ const CollectionsSelectableBox = ({ const handleConfirmation = () => { setBtnState('pending'); updateCollectionsMutation.mutateAsync(selectedCollections).then(() => { - showToast(intl.formatMessage(messages.manageCollectionsToComponentSuccess)); + showToast(intl.formatMessage(genericMessages.manageCollectionsSuccess)); }).catch(() => { - showToast(intl.formatMessage(messages.manageCollectionsToComponentFailed)); + showToast(intl.formatMessage(genericMessages.manageCollectionsFailed)); }).finally(() => { setBtnState('default'); onClose(); diff --git a/src/library-authoring/generic/manage-collections/messages.ts b/src/library-authoring/generic/manage-collections/messages.ts index c9b998be47..1afefa4967 100644 --- a/src/library-authoring/generic/manage-collections/messages.ts +++ b/src/library-authoring/generic/manage-collections/messages.ts @@ -21,16 +21,6 @@ const messages = defineMessages({ defaultMessage: 'Collection selection', description: 'Aria label text for collection selection box', }, - manageCollectionsToComponentSuccess: { - id: 'course-authoring.library-authoring.manage-collections.add-success', - defaultMessage: 'Item collections updated', - description: 'Message to display on updating item collections', - }, - manageCollectionsToComponentFailed: { - id: 'course-authoring.library-authoring.manage-collections.add-failed', - defaultMessage: 'Failed to update item collections', - description: 'Message to display on failure of updating item collections', - }, manageCollectionsToComponentConfirmBtn: { id: 'course-authoring.library-authoring.manage-collections.add-confirm-btn', defaultMessage: 'Confirm', diff --git a/src/library-authoring/generic/messages.ts b/src/library-authoring/generic/messages.ts new file mode 100644 index 0000000000..e1aec050f0 --- /dev/null +++ b/src/library-authoring/generic/messages.ts @@ -0,0 +1,16 @@ +import { defineMessages } from '@edx/frontend-platform/i18n'; + +const messages = defineMessages({ + manageCollectionsSuccess: { + id: 'course-authoring.library-authoring.manage-collections.success', + defaultMessage: 'Content added to collection.', + description: 'Message to display on updating item collections', + }, + manageCollectionsFailed: { + id: 'course-authoring.library-authoring.manage-collections.failed', + defaultMessage: 'Failed to add content to collection.', + description: 'Message to display on failure of updating item collections', + }, +}); + +export default messages; diff --git a/src/library-authoring/units/LibraryUnitBlocks.tsx b/src/library-authoring/units/LibraryUnitBlocks.tsx index 1ef20fa39a..43e985eb8c 100644 --- a/src/library-authoring/units/LibraryUnitBlocks.tsx +++ b/src/library-authoring/units/LibraryUnitBlocks.tsx @@ -28,7 +28,7 @@ import { useUpdateXBlockFields, } from '../data/apiHooks'; import { LibraryBlock } from '../LibraryBlock'; -import { useLibraryRoutes } from '../routes'; +import { useLibraryRoutes, ContentType } from '../routes'; import messages from './messages'; import { useSidebarContext } from '../common/context/SidebarContext'; import { ToastContext } from '../../generic/toast-context'; @@ -200,8 +200,10 @@ export const LibraryUnitBlocks = ({ preview }: LibraryUnitBlocksProps) => { ); }; - const renderedBlocks = orderedBlocks?.map((block) => ( - + const renderedBlocks = orderedBlocks?.map((block, idx) => ( + // A container can have multiple instances of the same block + // eslint-disable-next-line react/no-array-index-key + { disabled={preview} > {hidePreviewFor !== block.id && ( -
- -
+
+ +
)}
@@ -245,7 +247,7 @@ export const LibraryUnitBlocks = ({ preview }: LibraryUnitBlocksProps) => { > {renderedBlocks} - { !preview && ( + {!preview && (
diff --git a/src/library-authoring/units/LibraryUnitPage.tsx b/src/library-authoring/units/LibraryUnitPage.tsx index e362cdb900..a84bed2a9f 100644 --- a/src/library-authoring/units/LibraryUnitPage.tsx +++ b/src/library-authoring/units/LibraryUnitPage.tsx @@ -137,7 +137,7 @@ export const LibraryUnitPage = () => { setDefaultTab({ collection: COLLECTION_INFO_TABS.Details, component: COMPONENT_INFO_TABS.Manage, - unit: UNIT_INFO_TABS.Organize, + unit: UNIT_INFO_TABS.Manage, }); setHiddenTabs([COMPONENT_INFO_TABS.Preview, UNIT_INFO_TABS.Preview]); return () => { From 323c8531b556822d03c2b611c9abd1e06023d6b4 Mon Sep 17 00:00:00 2001 From: Jillian Date: Fri, 2 May 2025 11:43:43 +0930 Subject: [PATCH 2/2] perf: use Library search results to populate container card preview (#1820) * fix: use Library search results to populate container card preview * feat: show published children when showing only published Unit content * fix: nits (cherry picked from commit 24e469542df017990ad309949c62a0546e99c1e7) --- .../components/ContainerCard.test.tsx | 42 +++++++++++++++---- .../components/ContainerCard.tsx | 28 +++++++------ src/search-manager/data/api.ts | 12 ++++-- 3 files changed, 57 insertions(+), 25 deletions(-) diff --git a/src/library-authoring/components/ContainerCard.test.tsx b/src/library-authoring/components/ContainerCard.test.tsx index a76601e1d0..a8716d7ad1 100644 --- a/src/library-authoring/components/ContainerCard.test.tsx +++ b/src/library-authoring/components/ContainerCard.test.tsx @@ -6,7 +6,7 @@ import { fireEvent, } from '../../testUtils'; import { LibraryProvider } from '../common/context/LibraryContext'; -import { mockContentLibrary, mockGetContainerChildren } from '../data/api.mocks'; +import { mockContentLibrary } from '../data/api.mocks'; import { type ContainerHit, PublishStatus } from '../../search-manager'; import ContainerCard from './ContainerCard'; import { getLibraryContainerApiUrl, getLibraryContainerRestoreApiUrl } from '../data/api'; @@ -40,7 +40,6 @@ let axiosMock: MockAdapter; let mockShowToast; mockContentLibrary.applyMock(); -mockGetContainerChildren.applyMock(); const render = (ui: React.ReactElement, showOnlyPublished: boolean = false) => baseRender(ui, { extraWrapper: ({ children }) => ( @@ -155,29 +154,54 @@ describe('', () => { it('should render no child blocks in card preview', async () => { render(); - expect(screen.queryByTitle('text block')).not.toBeInTheDocument(); + expect(screen.queryByTitle('lb:org1:Demo_course:html:text-0')).not.toBeInTheDocument(); expect(screen.queryByText('+0')).not.toBeInTheDocument(); }); it('should render <=5 child blocks in card preview', async () => { const containerWith5Children = { ...containerHitSample, - usageKey: mockGetContainerChildren.fiveChildren, - }; + content: { + childUsageKeys: Array(5).fill('').map((_child, idx) => `lb:org1:Demo_course:html:text-${idx}`), + }, + } satisfies ContainerHit; render(); - expect((await screen.findAllByTitle(/text block */)).length).toBe(5); + expect((await screen.findAllByTitle(/lb:org1:Demo_course:html:text-*/)).length).toBe(5); expect(screen.queryByText('+0')).not.toBeInTheDocument(); }); it('should render >5 child blocks with +N in card preview', async () => { const containerWith6Children = { ...containerHitSample, - usageKey: mockGetContainerChildren.sixChildren, - }; + content: { + childUsageKeys: Array(6).fill('').map((_child, idx) => `lb:org1:Demo_course:html:text-${idx}`), + }, + } satisfies ContainerHit; render(); - expect((await screen.findAllByTitle(/text block */)).length).toBe(4); + expect((await screen.findAllByTitle(/lb:org1:Demo_course:html:text-*/)).length).toBe(4); expect(screen.queryByText('+2')).toBeInTheDocument(); }); + + it('should render published child blocks when rendering a published card preview', async () => { + const containerWithPublishedChildren = { + ...containerHitSample, + content: { + childUsageKeys: Array(6).fill('').map((_child, idx) => `lb:org1:Demo_course:html:text-${idx}`), + }, + published: { + content: { + childUsageKeys: Array(2).fill('').map((_child, idx) => `lb:org1:Demo_course:html:text-${idx}`), + }, + }, + } satisfies ContainerHit; + render( + , + true, + ); + + expect((await screen.findAllByTitle(/lb:org1:Demo_course:html:text-*/)).length).toBe(2); + expect(screen.queryByText('+2')).not.toBeInTheDocument(); + }); }); diff --git a/src/library-authoring/components/ContainerCard.tsx b/src/library-authoring/components/ContainerCard.tsx index b2f66e963f..a1d3115a61 100644 --- a/src/library-authoring/components/ContainerCard.tsx +++ b/src/library-authoring/components/ContainerCard.tsx @@ -12,12 +12,13 @@ import { MoreVert } from '@openedx/paragon/icons'; import { Link } from 'react-router-dom'; import { getItemIcon, getComponentStyleColor } from '../../generic/block-type-utils'; +import { getBlockType } from '../../generic/key-utils'; import { ToastContext } from '../../generic/toast-context'; import { type ContainerHit, PublishStatus } from '../../search-manager'; import { useComponentPickerContext } from '../common/context/ComponentPickerContext'; import { useLibraryContext } from '../common/context/LibraryContext'; import { SidebarActions, useSidebarContext } from '../common/context/SidebarContext'; -import { useContainerChildren, useRemoveItemsFromCollection } from '../data/apiHooks'; +import { useRemoveItemsFromCollection } from '../data/apiHooks'; import { useLibraryRoutes } from '../routes'; import AddComponentWidget from './AddComponentWidget'; import BaseCard from './BaseCard'; @@ -107,21 +108,17 @@ const ContainerMenu = ({ hit } : ContainerMenuProps) => { }; type ContainerCardPreviewProps = { - containerId: string; + childUsageKeys: Array; showMaxChildren?: number; }; -const ContainerCardPreview = ({ containerId, showMaxChildren = 5 }: ContainerCardPreviewProps) => { - const { data, isLoading, isError } = useContainerChildren(containerId); - if (isLoading || isError) { - return null; - } - - const hiddenChildren = data.length - showMaxChildren; +const ContainerCardPreview = ({ childUsageKeys, showMaxChildren = 5 }: ContainerCardPreviewProps) => { + const hiddenChildren = childUsageKeys.length - showMaxChildren; return ( { - data.slice(0, showMaxChildren).map(({ id, blockType, displayName }, idx) => { + childUsageKeys.slice(0, showMaxChildren).map((usageKey, idx) => { + const blockType = getBlockType(usageKey); let blockPreview: ReactNode; let classNames; @@ -133,7 +130,7 @@ const ContainerCardPreview = ({ containerId, showMaxChildren = 5 }: ContainerCar ); } else { @@ -149,7 +146,7 @@ const ContainerCardPreview = ({ containerId, showMaxChildren = 5 }: ContainerCar
{blockPreview} @@ -178,6 +175,7 @@ const ContainerCard = ({ hit } : ContainerCardProps) => { published, publishStatus, usageKey: unitId, + content, } = hit; const numChildrenCount = showOnlyPublished ? ( @@ -188,6 +186,10 @@ const ContainerCard = ({ hit } : ContainerCardProps) => { showOnlyPublished ? formatted.published?.displayName : formatted.displayName ) ?? ''; + const childUsageKeys: Array = ( + showOnlyPublished ? published?.content?.childUsageKeys : content?.childUsageKeys + ) ?? []; + const { navigateTo } = useLibraryRoutes(); const openContainer = useCallback(() => { @@ -202,7 +204,7 @@ const ContainerCard = ({ hit } : ContainerCardProps) => { } + preview={} tags={tags} numChildren={numChildrenCount} actions={( diff --git a/src/search-manager/data/api.ts b/src/search-manager/data/api.ts index 549054b3fe..e1a6aeaa33 100644 --- a/src/search-manager/data/api.ts +++ b/src/search-manager/data/api.ts @@ -50,6 +50,7 @@ export const getContentSearchConfig = async (): Promise<{ url: string, indexName export interface ContentDetails { htmlContent?: string; capaContent?: string; + childUsageKeys?: Array; [k: string]: any; } @@ -151,9 +152,10 @@ export interface ContentHit extends BaseContentHit { * Defined in edx-platform/openedx/core/djangoapps/content/search/documents.py */ export interface ContentPublishedData { - description?: string, - displayName?: string, - numChildren?: number, + description?: string; + displayName?: string; + numChildren?: number; + content?: ContentDetails; } /** @@ -171,6 +173,9 @@ export interface CollectionHit extends BaseContentHit { * Information about a single container returned in the search results * Defined in edx-platform/openedx/core/djangoapps/content/search/documents.py */ +interface ContainerHitContent { + childUsageKeys?: string[], +} export interface ContainerHit extends BaseContentHit { type: 'library_container'; blockType: 'unit'; // This should be expanded to include other container types @@ -178,6 +183,7 @@ export interface ContainerHit extends BaseContentHit { published?: ContentPublishedData; publishStatus: PublishStatus; formatted: BaseContentHit['formatted'] & { published?: ContentPublishedData, }; + content?: ContainerHitContent; } export type HitType = ContentHit | CollectionHit | ContainerHit;