Skip to content

Commit ebf4b7c

Browse files
authored
feat: Open Manage tags on click tag count in section/subsection page [FC-0090] (#2136)
Add functionality to open the manage tags when clicking the tag count in the section/subsection page.
1 parent 962b30b commit ebf4b7c

File tree

7 files changed

+97
-16
lines changed

7 files changed

+97
-16
lines changed

src/content-tags-drawer/ContentTagsDrawer.test.jsx

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const {
3434
languageWithoutTagsId,
3535
largeTagsId,
3636
emptyTagsId,
37+
containerTagsId,
3738
} = mockContentTaxonomyTagsData;
3839

3940
jest.mock('react-router-dom', () => ({
@@ -46,14 +47,15 @@ jest.mock('../library-authoring/common/context/SidebarContext', () => ({
4647
useSidebarContext: () => ({ sidebarAction: mockSidebarAction() }),
4748
}));
4849

49-
const renderDrawer = (contentId, drawerParams = {}) => (
50-
render(
50+
const renderDrawer = (contentId, drawerParams = {}, renderPath = path, containerId = '') => {
51+
const params = { contentId, containerId };
52+
return render(
5153
<ContentTagsDrawerSheetContext.Provider value={drawerParams}>
5254
<ContentTagsDrawer {...drawerParams} />
5355
</ContentTagsDrawerSheetContext.Provider>,
54-
{ path, params: { contentId } },
55-
)
56-
);
56+
{ path: renderPath, params },
57+
);
58+
};
5759

5860
describe('<ContentTagsDrawer />', () => {
5961
beforeEach(async () => {
@@ -692,6 +694,42 @@ describe('<ContentTagsDrawer />', () => {
692694
await waitFor(() => expect(axiosMock.history.put[0].url).toEqual(url));
693695
});
694696

697+
[
698+
'lct:org:lib:unit:1',
699+
'lib-collection:org:lib:1',
700+
'lb:org:lib:html:1',
701+
].forEach((containerId) => {
702+
it(`should invalidate children query when update child tag when containerId is ${containerId}`, async () => {
703+
const newPath = '/container/:containerId/';
704+
const { axiosMock, queryClient } = initializeMocks();
705+
const mockInvalidateQueries = jest.spyOn(queryClient, 'invalidateQueries');
706+
const url = getContentTaxonomyTagsApiUrl(containerTagsId);
707+
axiosMock.onPut(url).reply(200);
708+
renderDrawer(containerTagsId, { id: containerTagsId }, newPath, containerId);
709+
expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument();
710+
const editTagsButton = screen.getByRole('button', {
711+
name: /edit tags/i,
712+
});
713+
fireEvent.click(editTagsButton);
714+
715+
const saveButton = screen.getByRole('button', {
716+
name: /save/i,
717+
});
718+
fireEvent.click(saveButton);
719+
720+
await waitFor(() => expect(axiosMock.history.put[0].url).toEqual(url));
721+
expect(mockInvalidateQueries).toHaveBeenCalledTimes(5);
722+
expect(mockInvalidateQueries).toHaveBeenNthCalledWith(5, [
723+
'contentLibrary',
724+
'lib:org:lib',
725+
'content',
726+
'container',
727+
containerId,
728+
'children',
729+
]);
730+
});
731+
});
732+
695733
it('should taxonomies must be ordered', async () => {
696734
renderDrawer(largeTagsId);
697735
expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument();

src/content-tags-drawer/data/api.mocks.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ mockContentTaxonomyTagsData.emptyTagsId = 'block-v1:EmptyTagsOrg+STC1+2023_1+typ
205205
mockContentTaxonomyTagsData.emptyTags = {
206206
taxonomies: [],
207207
};
208-
mockContentTaxonomyTagsData.containerTagsId = 'lct:org:lib:unit:container_tags';
208+
mockContentTaxonomyTagsData.containerTagsId = 'lct:StagedTagsOrg:lib:unit:container_tags';
209209
mockContentTaxonomyTagsData.applyMock = () => jest.spyOn(api, 'getContentTaxonomyTagsData').mockImplementation(mockContentTaxonomyTagsData);
210210

211211
/**

src/content-tags-drawer/data/apiHooks.jsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ export const useContentTaxonomyTagsData = (contentId) => (
112112

113113
/**
114114
* Builds the query to get meta data about the content object
115-
* @param {string} contentId The id of the content object (unit/component)
115+
* @param {string} contentId The id of the content object
116116
* @param {boolean} enabled Flag to enable/disable the query
117117
*/
118118
export const useContentData = (contentId, enabled) => (
@@ -130,7 +130,7 @@ export const useContentData = (contentId, enabled) => (
130130
export const useContentTaxonomyTagsUpdater = (contentId) => {
131131
const queryClient = useQueryClient();
132132
const unitIframe = window.frames['xblock-iframe'];
133-
const { unitId } = useParams();
133+
const { containerId } = useParams();
134134

135135
return useMutation({
136136
/**
@@ -143,7 +143,7 @@ export const useContentTaxonomyTagsUpdater = (contentId) => {
143143
* >}
144144
*/
145145
mutationFn: ({ tagsData }) => updateContentTaxonomyTags(contentId, tagsData),
146-
onSettled: /* istanbul ignore next */ () => {
146+
onSettled: () => {
147147
queryClient.invalidateQueries({ queryKey: ['contentTaxonomyTags', contentId] });
148148
/// Invalidate query with pattern on course outline
149149
let contentPattern;
@@ -160,9 +160,10 @@ export const useContentTaxonomyTagsUpdater = (contentId) => {
160160
queryClient.invalidateQueries(xblockQueryKeys.componentMetadata(contentId));
161161
// Invalidate content search to update tags count
162162
queryClient.invalidateQueries(['content_search'], { predicate: (query) => libraryQueryPredicate(query, libraryId) });
163-
// If the tags for a compoent were edited from Unit page, invalidate children query to fetch count again.
164-
if (unitId) {
165-
queryClient.invalidateQueries(libraryAuthoringQueryKeys.containerChildren(unitId));
163+
// If the tags for an item were edited from a container page (Unit, Subsection, Section),
164+
// invalidate children query to fetch count again.
165+
if (containerId) {
166+
queryClient.invalidateQueries(libraryAuthoringQueryKeys.containerChildren(containerId));
166167
}
167168
}
168169
},

src/generic/tag-count/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ type TagCountProps = {
99
};
1010

1111
// eslint-disable-next-line react/prop-types
12-
const TagCount: React.FC<TagCountProps> = ({ count, onClick, size }) => {
12+
const TagCount: React.FC<TagCountProps> = ({ count, onClick, size }: TagCountProps) => {
1313
const renderContent = () => (
1414
<Stack direction="horizontal" gap={1}>
1515
<Icon size={size} src={Tag} />

src/library-authoring/data/apiHooks.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,10 @@ export const useContainerChildren = (containerId?: string, published: boolean =
703703
enabled: !!containerId,
704704
queryKey: libraryAuthoringQueryKeys.containerChildren(containerId!),
705705
queryFn: () => api.getLibraryContainerChildren(containerId!, published),
706-
structuralSharing: (oldData: api.LibraryBlockMetadata[], newData: api.LibraryBlockMetadata[]) => {
706+
structuralSharing: (
707+
oldData: api.LibraryBlockMetadata[] | api.Container[],
708+
newData: api.LibraryBlockMetadata[] | api.Container[],
709+
) => {
707710
// This just sets `isNew` flag to new children components
708711
if (oldData) {
709712
const oldDataIds = oldData.map((obj) => obj.id);

src/library-authoring/section-subsections/LibraryContainerChildren.tsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ import { ToastContext } from '../../generic/toast-context';
2424
import TagCount from '../../generic/tag-count';
2525
import { ContainerMenu } from '../components/ContainerCard';
2626
import { useLibraryRoutes } from '../routes';
27-
import { useSidebarContext } from '../common/context/SidebarContext';
27+
import { SidebarActions, useSidebarContext } from '../common/context/SidebarContext';
28+
import { useRunOnNextRender } from '../../utils';
2829

2930
interface LibraryContainerChildrenProps {
3031
containerKey: string;
@@ -45,6 +46,8 @@ const ContainerRow = ({ containerKey, container, readOnly }: ContainerRowProps)
4546
const { showToast } = useContext(ToastContext);
4647
const updateMutation = useUpdateContainer(container.originalId, containerKey);
4748
const { showOnlyPublished } = useLibraryContext();
49+
const { navigateTo } = useLibraryRoutes();
50+
const { setSidebarAction } = useSidebarContext();
4851

4952
const handleSaveDisplayName = async (newDisplayName: string) => {
5053
try {
@@ -57,6 +60,18 @@ const ContainerRow = ({ containerKey, container, readOnly }: ContainerRowProps)
5760
}
5861
};
5962

63+
/* istanbul ignore next */
64+
const scheduleJumpToTags = useRunOnNextRender(() => {
65+
// TODO: Ugly hack to make sure sidebar shows manage tags section
66+
// This needs to run after all changes to url takes place to avoid conflicts.
67+
setTimeout(() => setSidebarAction(SidebarActions.JumpToManageTags), 250);
68+
});
69+
70+
const jumpToManageTags = () => {
71+
navigateTo({ selectedItemId: container.originalId });
72+
scheduleJumpToTags();
73+
};
74+
6075
return (
6176
<>
6277
{/* 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)
90105
</Stack>
91106
</Badge>
92107
)}
93-
<TagCount size="sm" count={container.tagsCount} />
108+
<TagCount
109+
size="sm"
110+
count={container.tagsCount}
111+
onClick={readOnly ? undefined : jumpToManageTags}
112+
/>
94113
{!readOnly && (
95114
<ContainerMenu
96115
containerKey={container.originalId}

src/library-authoring/section-subsections/LibrarySectionSubsectionPage.test.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,5 +389,25 @@ describe('<LibrarySectionPage / LibrarySubsectionPage />', () => {
389389
expect((await screen.findAllByText(new RegExp(`${childType} block 0`, 'i')))[0]).toBeInTheDocument();
390390
expect(await screen.findByRole('button', { name: new RegExp(`${childType} Info`, 'i') })).toBeInTheDocument();
391391
});
392+
393+
it(`should open manage tags on click tag count in ${cType} page`, async () => {
394+
const cId = cType === ContainerType.Section
395+
? mockGetContainerMetadata.sectionId
396+
: mockGetContainerMetadata.subsectionId;
397+
renderLibrarySectionPage(cId, undefined, cType);
398+
// check all children components are rendered.
399+
expect((await screen.findAllByText(`${childType} block 0`))[0]).toBeInTheDocument();
400+
expect((await screen.findAllByText(`${childType} block 1`))[0]).toBeInTheDocument();
401+
expect((await screen.findAllByText(`${childType} block 2`))[0]).toBeInTheDocument();
402+
403+
const tagCountButton = screen.getAllByRole('button', { name: '0' })[0];
404+
fireEvent.click(tagCountButton);
405+
406+
expect(await screen.findByTestId('library-sidebar')).toBeInTheDocument();
407+
await waitFor(
408+
() => expect(screen.getByRole('tab', { name: /manage/i })).toHaveClass('active'),
409+
{ timeout: 300 },
410+
);
411+
});
392412
});
393413
});

0 commit comments

Comments
 (0)