Skip to content

Commit e4c5238

Browse files
authored
fix: Bug - Unusable "Languages" taxonomy appears in tagging drawer (#1057)
* Hide language taxonomy when empty * New message on search result when taxonomy is empty * Empty taxonomies message added in drawer
1 parent 1bc759a commit e4c5238

8 files changed

+240
-19
lines changed

src/content-tags-drawer/ContentTagsDrawer.jsx

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,72 @@ import {
88
Button,
99
Toast,
1010
} from '@openedx/paragon';
11-
import { useIntl } from '@edx/frontend-platform/i18n';
12-
import { useParams } from 'react-router-dom';
11+
import { useIntl, FormattedMessage } from '@edx/frontend-platform/i18n';
12+
import { useParams, useNavigate } from 'react-router-dom';
1313
import messages from './messages';
1414
import ContentTagsCollapsible from './ContentTagsCollapsible';
1515
import Loading from '../generic/Loading';
1616
import useContentTagsDrawerContext from './ContentTagsDrawerHelper';
1717
import { ContentTagsDrawerContext, ContentTagsDrawerSheetContext } from './common/context';
1818

19+
const TaxonomyList = ({ contentId }) => {
20+
const navigate = useNavigate();
21+
const intl = useIntl();
22+
23+
const {
24+
isTaxonomyListLoaded,
25+
isContentTaxonomyTagsLoaded,
26+
tagsByTaxonomy,
27+
stagedContentTags,
28+
collapsibleStates,
29+
} = React.useContext(ContentTagsDrawerContext);
30+
31+
if (isTaxonomyListLoaded && isContentTaxonomyTagsLoaded) {
32+
if (tagsByTaxonomy.length !== 0) {
33+
return (
34+
<div>
35+
{ tagsByTaxonomy.map((data) => (
36+
<div key={`taxonomy-tags-collapsible-${data.id}`}>
37+
<ContentTagsCollapsible
38+
contentId={contentId}
39+
taxonomyAndTagsData={data}
40+
stagedContentTags={stagedContentTags[data.id] || []}
41+
collapsibleState={collapsibleStates[data.id] || false}
42+
/>
43+
<hr />
44+
</div>
45+
))}
46+
</div>
47+
);
48+
}
49+
50+
return (
51+
<FormattedMessage
52+
{...messages.emptyDrawerContent}
53+
values={{
54+
link: (
55+
<Button
56+
tabIndex="0"
57+
size="inline"
58+
variant="link"
59+
className="text-info-500 p-0 enable-taxonomies-button"
60+
onClick={() => navigate('/taxonomies')}
61+
>
62+
{ intl.formatMessage(messages.emptyDrawerContentLink) }
63+
</Button>
64+
),
65+
}}
66+
/>
67+
);
68+
}
69+
70+
return <Loading />;
71+
};
72+
73+
TaxonomyList.propTypes = {
74+
contentId: PropTypes.string.isRequired,
75+
};
76+
1977
/**
2078
* Drawer with the functionality to show and manage tags in a certain content.
2179
* It is used both in interfaces of this MFE and in edx-platform interfaces such as iframe.
@@ -42,7 +100,6 @@ const ContentTagsDrawer = ({ id, onClose }) => {
42100
contentName,
43101
isTaxonomyListLoaded,
44102
isContentTaxonomyTagsLoaded,
45-
tagsByTaxonomy,
46103
stagedContentTags,
47104
collapsibleStates,
48105
isEditMode,
@@ -110,19 +167,7 @@ const ContentTagsDrawer = ({ id, onClose }) => {
110167
<p className="h4 text-gray-500 font-weight-bold">
111168
{intl.formatMessage(messages.headerSubtitle)}
112169
</p>
113-
{ isTaxonomyListLoaded && isContentTaxonomyTagsLoaded
114-
? tagsByTaxonomy.map((data) => (
115-
<div key={`taxonomy-tags-collapsible-${data.id}`}>
116-
<ContentTagsCollapsible
117-
contentId={contentId}
118-
taxonomyAndTagsData={data}
119-
stagedContentTags={stagedContentTags[data.id] || []}
120-
collapsibleState={collapsibleStates[data.id] || false}
121-
/>
122-
<hr />
123-
</div>
124-
))
125-
: <Loading />}
170+
<TaxonomyList contentId={contentId} />
126171
{otherTaxonomies.length !== 0 && (
127172
<div>
128173
<p className="h4 text-gray-500 font-weight-bold">

src/content-tags-drawer/ContentTagsDrawer.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222
.other-description {
2323
font-size: .9rem;
2424
}
25+
26+
.enable-taxonomies-button:not([disabled]):hover {
27+
background-color: transparent;
28+
color: $info-900 !important;
29+
}
2530
}
2631

2732
// Apply styles to sheet only if it has a child with a .tags-drawer class

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

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,20 @@ import {
2020
import { getTaxonomyListData } from '../taxonomy/data/api';
2121
import messages from './messages';
2222
import { ContentTagsDrawerSheetContext } from './common/context';
23+
import { languageExportId } from './utils';
2324

2425
const contentId = 'block-v1:SampleTaxonomyOrg1+STC1+2023_1+type@vertical+block@7f47fe2dbcaf47c5a071671c741fe1ab';
2526
const mockOnClose = jest.fn();
2627
const mockMutate = jest.fn();
2728
const mockSetBlockingSheet = jest.fn();
29+
const mockNavigate = jest.fn();
2830

2931
jest.mock('react-router-dom', () => ({
3032
...jest.requireActual('react-router-dom'),
3133
useParams: () => ({
3234
contentId,
3335
}),
36+
useNavigate: () => mockNavigate,
3437
}));
3538

3639
// FIXME: replace these mocks with API mocks
@@ -256,6 +259,83 @@ describe('<ContentTagsDrawer />', () => {
256259
});
257260
};
258261

262+
const setupMockDataLanguageTaxonomyTestings = (hasTags) => {
263+
useContentTaxonomyTagsData.mockReturnValue({
264+
isSuccess: true,
265+
data: {
266+
taxonomies: [
267+
{
268+
name: 'Languages',
269+
taxonomyId: 123,
270+
exportId: languageExportId,
271+
canTagObject: true,
272+
tags: hasTags ? [
273+
{
274+
value: 'Tag 1',
275+
lineage: ['Tag 1'],
276+
canDeleteObjecttag: true,
277+
},
278+
] : [],
279+
},
280+
{
281+
name: 'Taxonomy 1',
282+
taxonomyId: 1234,
283+
canTagObject: true,
284+
tags: [
285+
{
286+
value: 'Tag 1',
287+
lineage: ['Tag 1'],
288+
canDeleteObjecttag: true,
289+
},
290+
{
291+
value: 'Tag 2',
292+
lineage: ['Tag 2'],
293+
canDeleteObjecttag: true,
294+
},
295+
],
296+
},
297+
],
298+
},
299+
});
300+
getTaxonomyListData.mockResolvedValue({
301+
results: [
302+
{
303+
id: 123,
304+
name: 'Languages',
305+
description: 'This is a description 1',
306+
exportId: languageExportId,
307+
canTagObject: true,
308+
},
309+
{
310+
id: 1234,
311+
name: 'Taxonomy 1',
312+
description: 'This is a description 2',
313+
canTagObject: true,
314+
},
315+
],
316+
});
317+
318+
useTaxonomyTagsData.mockReturnValue({
319+
hasMorePages: false,
320+
canAddTag: false,
321+
tagPages: {
322+
isLoading: false,
323+
isError: false,
324+
data: [{
325+
value: 'Tag 1',
326+
externalId: null,
327+
childCount: 0,
328+
depth: 0,
329+
parentValue: null,
330+
id: 12345,
331+
subTagsUrl: null,
332+
canChangeTag: false,
333+
canDeleteTag: false,
334+
}],
335+
},
336+
});
337+
};
338+
259339
const setupLargeMockDataForStagedTagsTesting = () => {
260340
useContentTaxonomyTagsData.mockReturnValue({
261341
isSuccess: true,
@@ -1057,4 +1137,47 @@ describe('<ContentTagsDrawer />', () => {
10571137

10581138
expect(screen.getByText(/tag 3/i)).toBeInTheDocument();
10591139
});
1140+
1141+
it('should show Language Taxonomy', async () => {
1142+
setupMockDataLanguageTaxonomyTestings(true);
1143+
render(<RootWrapper />);
1144+
expect(await screen.findByText('Languages')).toBeInTheDocument();
1145+
});
1146+
1147+
it('should hide Language Taxonomy', async () => {
1148+
setupMockDataLanguageTaxonomyTestings(false);
1149+
render(<RootWrapper />);
1150+
expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument();
1151+
1152+
expect(screen.queryByText('Languages')).not.toBeInTheDocument();
1153+
});
1154+
1155+
it('should show empty drawer message', async () => {
1156+
useContentTaxonomyTagsData.mockReturnValue({
1157+
isSuccess: true,
1158+
data: {
1159+
taxonomies: [],
1160+
},
1161+
});
1162+
getTaxonomyListData.mockResolvedValue({
1163+
results: [],
1164+
});
1165+
useTaxonomyTagsData.mockReturnValue({
1166+
hasMorePages: false,
1167+
canAddTag: false,
1168+
tagPages: {
1169+
isLoading: false,
1170+
isError: false,
1171+
data: [],
1172+
},
1173+
});
1174+
1175+
render(<RootWrapper />);
1176+
expect(await screen.findByText(/to use tags, please or contact your administrator\./i)).toBeInTheDocument();
1177+
const enableButton = screen.getByRole('button', {
1178+
name: /enable a taxonomy/i,
1179+
});
1180+
fireEvent.click(enableButton);
1181+
expect(mockNavigate).toHaveBeenCalledWith('/taxonomies');
1182+
});
10601183
});

src/content-tags-drawer/ContentTagsDrawerHelper.jsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { useIntl } from '@edx/frontend-platform/i18n';
44
import { cloneDeep } from 'lodash';
55
import { useContentData, useContentTaxonomyTagsData, useContentTaxonomyTagsUpdater } from './data/apiHooks';
66
import { useTaxonomyList } from '../taxonomy/data/apiHooks';
7-
import { extractOrgFromContentId } from './utils';
7+
import { extractOrgFromContentId, languageExportId } from './utils';
88
import messages from './messages';
99
import { ContentTagsDrawerSheetContext } from './common/context';
1010

@@ -142,8 +142,14 @@ const useContentTagsDrawerContext = (contentId) => {
142142
}
143143
});
144144

145+
// Delete Language taxonomy if is empty
146+
const filteredTaxonomies = taxonomiesList.filter(
147+
(taxonomy) => taxonomy.exportId !== languageExportId
148+
|| taxonomy.contentTags.length !== 0,
149+
);
150+
145151
return {
146-
fechedTaxonomies: sortTaxonomies(taxonomiesList),
152+
fechedTaxonomies: sortTaxonomies(filteredTaxonomies),
147153
fechedOtherTaxonomies: otherTaxonomiesList,
148154
};
149155
}

src/content-tags-drawer/ContentTagsDropDownSelector.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,9 @@ const ContentTagsDropDownSelector = ({
323323

324324
{ tagPages.data.length === 0 && !tagPages.isLoading && (
325325
<div className="d-flex justify-content-center muted-text">
326-
<FormattedMessage {...messages.noTagsFoundMessage} values={{ searchTerm }} />
326+
{ searchTerm
327+
? <FormattedMessage {...messages.noTagsFoundMessage} values={{ searchTerm }} />
328+
: <FormattedMessage {...messages.noTagsInTaxonomyMessage} />}
327329
</div>
328330
)}
329331

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,4 +282,28 @@ describe('<ContentTagsDropDownSelector />', () => {
282282
expect(getByText(message)).toBeInTheDocument();
283283
});
284284
});
285+
286+
it('should render "noTagsInTaxonomy" message if taxonomy is empty', async () => {
287+
useTaxonomyTagsData.mockReturnValueOnce({
288+
hasMorePages: false,
289+
tagPages: {
290+
isLoading: false,
291+
isError: false,
292+
isSuccess: true,
293+
data: [],
294+
},
295+
});
296+
297+
const searchTerm = '';
298+
await act(async () => {
299+
const { getByText } = await getComponent({ ...data, searchTerm });
300+
301+
await waitFor(() => {
302+
expect(useTaxonomyTagsData).toBeCalledWith(data.taxonomyId, null, 1, searchTerm);
303+
});
304+
305+
const message = 'No tags in this taxonomy yet';
306+
expect(getByText(message)).toBeInTheDocument();
307+
});
308+
});
285309
});

src/content-tags-drawer/messages.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ const messages = defineMessages({
2525
id: 'course-authoring.content-tags-drawer.tags-dropdown-selector.no-tags-found',
2626
defaultMessage: 'No tags found with the search term "{searchTerm}"',
2727
},
28+
noTagsInTaxonomyMessage: {
29+
id: 'course-authoring.content-tags-drawer.tags-dropdown-selector.no-tags-in-taxonomy',
30+
defaultMessage: 'No tags in this taxonomy yet',
31+
description: 'Message when the user uses the tags dropdown selector of an empty taxonomy',
32+
},
2833
taxonomyTagChecked: {
2934
id: 'course-authoring.content-tags-drawer.tags-dropdown-selector.tag-checked',
3035
defaultMessage: 'Checked',
@@ -124,6 +129,16 @@ const messages = defineMessages({
124129
defaultMessage: 'These tags are already applied, but you can\'t add new ones as you don\'t have access to their taxonomies.',
125130
description: 'Description of "Other tags" subsection in tags drawer',
126131
},
132+
emptyDrawerContent: {
133+
id: 'course-authoring.content-tags-drawer.empty',
134+
defaultMessage: 'To use tags, please {link} or contact your administrator.',
135+
description: 'Message when there are no taxonomies.',
136+
},
137+
emptyDrawerContentLink: {
138+
id: 'course-authoring.content-tags-drawer.empty-link',
139+
defaultMessage: 'enable a taxonomy',
140+
description: 'Message of the link used in empty drawer message.',
141+
},
127142
});
128143

129144
export default messages;

src/content-tags-drawer/utils.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
// eslint-disable-next-line import/prefer-default-export
22
export const extractOrgFromContentId = (contentId) => contentId.split('+')[0].split(':')[1];
3+
export const languageExportId = 'languages-v1';

0 commit comments

Comments
 (0)