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.
This commit is contained in:
@@ -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(
|
||||
<ContentTagsDrawerSheetContext.Provider value={drawerParams}>
|
||||
<ContentTagsDrawer {...drawerParams} />
|
||||
</ContentTagsDrawerSheetContext.Provider>,
|
||||
{ path, params: { contentId } },
|
||||
)
|
||||
);
|
||||
{ path: renderPath, params },
|
||||
);
|
||||
};
|
||||
|
||||
describe('<ContentTagsDrawer />', () => {
|
||||
beforeEach(async () => {
|
||||
@@ -692,6 +694,42 @@ describe('<ContentTagsDrawer />', () => {
|
||||
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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -9,7 +9,7 @@ type TagCountProps = {
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react/prop-types
|
||||
const TagCount: React.FC<TagCountProps> = ({ count, onClick, size }) => {
|
||||
const TagCount: React.FC<TagCountProps> = ({ count, onClick, size }: TagCountProps) => {
|
||||
const renderContent = () => (
|
||||
<Stack direction="horizontal" gap={1}>
|
||||
<Icon size={size} src={Tag} />
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
</Stack>
|
||||
</Badge>
|
||||
)}
|
||||
<TagCount size="sm" count={container.tagsCount} />
|
||||
<TagCount
|
||||
size="sm"
|
||||
count={container.tagsCount}
|
||||
onClick={readOnly ? undefined : jumpToManageTags}
|
||||
/>
|
||||
{!readOnly && (
|
||||
<ContainerMenu
|
||||
containerKey={container.originalId}
|
||||
|
||||
@@ -389,5 +389,25 @@ describe('<LibrarySectionPage / LibrarySubsectionPage />', () => {
|
||||
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 },
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user