diff --git a/src/content-tags-drawer/ContentTagsDrawer.test.jsx b/src/content-tags-drawer/ContentTagsDrawer.test.jsx
index 5035b2fe43..90fb145683 100644
--- a/src/content-tags-drawer/ContentTagsDrawer.test.jsx
+++ b/src/content-tags-drawer/ContentTagsDrawer.test.jsx
@@ -34,6 +34,7 @@ const {
languageWithoutTagsId,
largeTagsId,
emptyTagsId,
+ containerTagsId,
} = mockContentTaxonomyTagsData;
jest.mock('react-router-dom', () => ({
@@ -46,14 +47,15 @@ jest.mock('../library-authoring/common/context/SidebarContext', () => ({
useSidebarContext: () => ({ sidebarAction: mockSidebarAction() }),
}));
-const renderDrawer = (contentId, drawerParams = {}) => (
- render(
+const renderDrawer = (contentId, drawerParams = {}, renderPath = path, containerId = '') => {
+ const params = { contentId, containerId };
+ return render(
,
- { path, params: { contentId } },
- )
-);
+ { path: renderPath, params },
+ );
+};
describe('', () => {
beforeEach(async () => {
@@ -692,6 +694,42 @@ describe('', () => {
await waitFor(() => expect(axiosMock.history.put[0].url).toEqual(url));
});
+ [
+ 'lct:org:lib:unit:1',
+ 'lib-collection:org:lib:1',
+ 'lb:org:lib:html:1',
+ ].forEach((containerId) => {
+ it(`should invalidate children query when update child tag when containerId is ${containerId}`, async () => {
+ const newPath = '/container/:containerId/';
+ const { axiosMock, queryClient } = initializeMocks();
+ const mockInvalidateQueries = jest.spyOn(queryClient, 'invalidateQueries');
+ const url = getContentTaxonomyTagsApiUrl(containerTagsId);
+ axiosMock.onPut(url).reply(200);
+ renderDrawer(containerTagsId, { id: containerTagsId }, newPath, containerId);
+ expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument();
+ const editTagsButton = screen.getByRole('button', {
+ name: /edit tags/i,
+ });
+ fireEvent.click(editTagsButton);
+
+ const saveButton = screen.getByRole('button', {
+ name: /save/i,
+ });
+ fireEvent.click(saveButton);
+
+ await waitFor(() => expect(axiosMock.history.put[0].url).toEqual(url));
+ expect(mockInvalidateQueries).toHaveBeenCalledTimes(5);
+ expect(mockInvalidateQueries).toHaveBeenNthCalledWith(5, [
+ 'contentLibrary',
+ 'lib:org:lib',
+ 'content',
+ 'container',
+ containerId,
+ 'children',
+ ]);
+ });
+ });
+
it('should taxonomies must be ordered', async () => {
renderDrawer(largeTagsId);
expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument();
diff --git a/src/content-tags-drawer/data/api.mocks.ts b/src/content-tags-drawer/data/api.mocks.ts
index 2f7360be8f..932ae7311e 100644
--- a/src/content-tags-drawer/data/api.mocks.ts
+++ b/src/content-tags-drawer/data/api.mocks.ts
@@ -205,7 +205,7 @@ mockContentTaxonomyTagsData.emptyTagsId = 'block-v1:EmptyTagsOrg+STC1+2023_1+typ
mockContentTaxonomyTagsData.emptyTags = {
taxonomies: [],
};
-mockContentTaxonomyTagsData.containerTagsId = 'lct:org:lib:unit:container_tags';
+mockContentTaxonomyTagsData.containerTagsId = 'lct:StagedTagsOrg:lib:unit:container_tags';
mockContentTaxonomyTagsData.applyMock = () => jest.spyOn(api, 'getContentTaxonomyTagsData').mockImplementation(mockContentTaxonomyTagsData);
/**
diff --git a/src/content-tags-drawer/data/apiHooks.jsx b/src/content-tags-drawer/data/apiHooks.jsx
index 01ef3a3a7b..fcd8da92b2 100644
--- a/src/content-tags-drawer/data/apiHooks.jsx
+++ b/src/content-tags-drawer/data/apiHooks.jsx
@@ -112,7 +112,7 @@ export const useContentTaxonomyTagsData = (contentId) => (
/**
* Builds the query to get meta data about the content object
- * @param {string} contentId The id of the content object (unit/component)
+ * @param {string} contentId The id of the content object
* @param {boolean} enabled Flag to enable/disable the query
*/
export const useContentData = (contentId, enabled) => (
@@ -130,7 +130,7 @@ export const useContentData = (contentId, enabled) => (
export const useContentTaxonomyTagsUpdater = (contentId) => {
const queryClient = useQueryClient();
const unitIframe = window.frames['xblock-iframe'];
- const { unitId } = useParams();
+ const { containerId } = useParams();
return useMutation({
/**
@@ -143,7 +143,7 @@ export const useContentTaxonomyTagsUpdater = (contentId) => {
* >}
*/
mutationFn: ({ tagsData }) => updateContentTaxonomyTags(contentId, tagsData),
- onSettled: /* istanbul ignore next */ () => {
+ onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['contentTaxonomyTags', contentId] });
/// Invalidate query with pattern on course outline
let contentPattern;
@@ -160,9 +160,10 @@ export const useContentTaxonomyTagsUpdater = (contentId) => {
queryClient.invalidateQueries(xblockQueryKeys.componentMetadata(contentId));
// Invalidate content search to update tags count
queryClient.invalidateQueries(['content_search'], { predicate: (query) => libraryQueryPredicate(query, libraryId) });
- // If the tags for a compoent were edited from Unit page, invalidate children query to fetch count again.
- if (unitId) {
- queryClient.invalidateQueries(libraryAuthoringQueryKeys.containerChildren(unitId));
+ // If the tags for an item were edited from a container page (Unit, Subsection, Section),
+ // invalidate children query to fetch count again.
+ if (containerId) {
+ queryClient.invalidateQueries(libraryAuthoringQueryKeys.containerChildren(containerId));
}
}
},
diff --git a/src/generic/tag-count/index.tsx b/src/generic/tag-count/index.tsx
index d8787bb2bf..9d31639186 100644
--- a/src/generic/tag-count/index.tsx
+++ b/src/generic/tag-count/index.tsx
@@ -9,7 +9,7 @@ type TagCountProps = {
};
// eslint-disable-next-line react/prop-types
-const TagCount: React.FC = ({ count, onClick, size }) => {
+const TagCount: React.FC = ({ count, onClick, size }: TagCountProps) => {
const renderContent = () => (
diff --git a/src/library-authoring/data/apiHooks.ts b/src/library-authoring/data/apiHooks.ts
index 72db994e64..168117672d 100644
--- a/src/library-authoring/data/apiHooks.ts
+++ b/src/library-authoring/data/apiHooks.ts
@@ -703,7 +703,10 @@ export const useContainerChildren = (containerId?: string, published: boolean =
enabled: !!containerId,
queryKey: libraryAuthoringQueryKeys.containerChildren(containerId!),
queryFn: () => api.getLibraryContainerChildren(containerId!, published),
- structuralSharing: (oldData: api.LibraryBlockMetadata[], newData: api.LibraryBlockMetadata[]) => {
+ structuralSharing: (
+ oldData: api.LibraryBlockMetadata[] | api.Container[],
+ newData: api.LibraryBlockMetadata[] | api.Container[],
+ ) => {
// This just sets `isNew` flag to new children components
if (oldData) {
const oldDataIds = oldData.map((obj) => obj.id);
diff --git a/src/library-authoring/section-subsections/LibraryContainerChildren.tsx b/src/library-authoring/section-subsections/LibraryContainerChildren.tsx
index cf5a62c888..c0b2010ec5 100644
--- a/src/library-authoring/section-subsections/LibraryContainerChildren.tsx
+++ b/src/library-authoring/section-subsections/LibraryContainerChildren.tsx
@@ -24,7 +24,8 @@ import { ToastContext } from '../../generic/toast-context';
import TagCount from '../../generic/tag-count';
import { ContainerMenu } from '../components/ContainerCard';
import { useLibraryRoutes } from '../routes';
-import { useSidebarContext } from '../common/context/SidebarContext';
+import { SidebarActions, useSidebarContext } from '../common/context/SidebarContext';
+import { useRunOnNextRender } from '../../utils';
interface LibraryContainerChildrenProps {
containerKey: string;
@@ -45,6 +46,8 @@ const ContainerRow = ({ containerKey, container, readOnly }: ContainerRowProps)
const { showToast } = useContext(ToastContext);
const updateMutation = useUpdateContainer(container.originalId, containerKey);
const { showOnlyPublished } = useLibraryContext();
+ const { navigateTo } = useLibraryRoutes();
+ const { setSidebarAction } = useSidebarContext();
const handleSaveDisplayName = async (newDisplayName: string) => {
try {
@@ -57,6 +60,18 @@ const ContainerRow = ({ containerKey, container, readOnly }: ContainerRowProps)
}
};
+ /* istanbul ignore next */
+ const scheduleJumpToTags = useRunOnNextRender(() => {
+ // TODO: Ugly hack to make sure sidebar shows manage tags section
+ // This needs to run after all changes to url takes place to avoid conflicts.
+ setTimeout(() => setSidebarAction(SidebarActions.JumpToManageTags), 250);
+ });
+
+ const jumpToManageTags = () => {
+ navigateTo({ selectedItemId: container.originalId });
+ scheduleJumpToTags();
+ };
+
return (
<>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
@@ -90,7 +105,11 @@ const ContainerRow = ({ containerKey, container, readOnly }: ContainerRowProps)
)}
-
+
{!readOnly && (
', () => {
expect((await screen.findAllByText(new RegExp(`${childType} block 0`, 'i')))[0]).toBeInTheDocument();
expect(await screen.findByRole('button', { name: new RegExp(`${childType} Info`, 'i') })).toBeInTheDocument();
});
+
+ it(`should open manage tags on click tag count in ${cType} page`, async () => {
+ const cId = cType === ContainerType.Section
+ ? mockGetContainerMetadata.sectionId
+ : mockGetContainerMetadata.subsectionId;
+ renderLibrarySectionPage(cId, undefined, cType);
+ // check all children components are rendered.
+ expect((await screen.findAllByText(`${childType} block 0`))[0]).toBeInTheDocument();
+ expect((await screen.findAllByText(`${childType} block 1`))[0]).toBeInTheDocument();
+ expect((await screen.findAllByText(`${childType} block 2`))[0]).toBeInTheDocument();
+
+ const tagCountButton = screen.getAllByRole('button', { name: '0' })[0];
+ fireEvent.click(tagCountButton);
+
+ expect(await screen.findByTestId('library-sidebar')).toBeInTheDocument();
+ await waitFor(
+ () => expect(screen.getByRole('tab', { name: /manage/i })).toHaveClass('active'),
+ { timeout: 300 },
+ );
+ });
});
});