Skip to content

Commit

Permalink
[FC-0042] Fix: Bug: Unusable "Languages" taxonomy appears in tagging …
Browse files Browse the repository at this point in the history
…drawer (#1031)

Hide language taxonomy when is empty
New message on search result when taxonomy is empty
Empty taxonomies message added in drawer
  • Loading branch information
ChrisChV committed Jun 3, 2024
1 parent 9b4eb10 commit 460de70
Show file tree
Hide file tree
Showing 8 changed files with 240 additions and 19 deletions.
77 changes: 61 additions & 16 deletions src/content-tags-drawer/ContentTagsDrawer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,72 @@ import {
Button,
Toast,
} from '@openedx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';
import { useParams } from 'react-router-dom';
import { useIntl, FormattedMessage } from '@edx/frontend-platform/i18n';
import { useParams, useNavigate } from 'react-router-dom';
import messages from './messages';
import ContentTagsCollapsible from './ContentTagsCollapsible';
import Loading from '../generic/Loading';
import useContentTagsDrawerContext from './ContentTagsDrawerHelper';
import { ContentTagsDrawerContext, ContentTagsDrawerSheetContext } from './common/context';

const TaxonomyList = ({ contentId }) => {
const navigate = useNavigate();
const intl = useIntl();

const {
isTaxonomyListLoaded,
isContentTaxonomyTagsLoaded,
tagsByTaxonomy,
stagedContentTags,
collapsibleStates,
} = React.useContext(ContentTagsDrawerContext);

if (isTaxonomyListLoaded && isContentTaxonomyTagsLoaded) {
if (tagsByTaxonomy.length !== 0) {
return (
<div>
{ tagsByTaxonomy.map((data) => (
<div key={`taxonomy-tags-collapsible-${data.id}`}>
<ContentTagsCollapsible
contentId={contentId}
taxonomyAndTagsData={data}
stagedContentTags={stagedContentTags[data.id] || []}
collapsibleState={collapsibleStates[data.id] || false}
/>
<hr />
</div>
))}
</div>
);
}

return (
<FormattedMessage
{...messages.emptyDrawerContent}
values={{
link: (
<Button
tabIndex="0"
size="inline"
variant="link"
className="text-info-500 p-0 enable-taxonomies-button"
onClick={() => navigate('/taxonomies')}
>
{ intl.formatMessage(messages.emptyDrawerContentLink) }
</Button>
),
}}
/>
);
}

return <Loading />;
};

TaxonomyList.propTypes = {
contentId: PropTypes.string.isRequired,
};

/**
* Drawer with the functionality to show and manage tags in a certain content.
* It is used both in interfaces of this MFE and in edx-platform interfaces such as iframe.
Expand All @@ -42,7 +100,6 @@ const ContentTagsDrawer = ({ id, onClose }) => {
contentName,
isTaxonomyListLoaded,
isContentTaxonomyTagsLoaded,
tagsByTaxonomy,
stagedContentTags,
collapsibleStates,
isEditMode,
Expand Down Expand Up @@ -110,19 +167,7 @@ const ContentTagsDrawer = ({ id, onClose }) => {
<p className="h4 text-gray-500 font-weight-bold">
{intl.formatMessage(messages.headerSubtitle)}
</p>
{ isTaxonomyListLoaded && isContentTaxonomyTagsLoaded
? tagsByTaxonomy.map((data) => (
<div key={`taxonomy-tags-collapsible-${data.id}`}>
<ContentTagsCollapsible
contentId={contentId}
taxonomyAndTagsData={data}
stagedContentTags={stagedContentTags[data.id] || []}
collapsibleState={collapsibleStates[data.id] || false}
/>
<hr />
</div>
))
: <Loading />}
<TaxonomyList contentId={contentId} />
{otherTaxonomies.length !== 0 && (
<div>
<p className="h4 text-gray-500 font-weight-bold">
Expand Down
5 changes: 5 additions & 0 deletions src/content-tags-drawer/ContentTagsDrawer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
.other-description {
font-size: .9rem;
}

.enable-taxonomies-button:not([disabled]):hover {
background-color: transparent;
color: $info-900 !important;
}
}

// Apply styles to sheet only if it has a child with a .tags-drawer class
Expand Down
123 changes: 123 additions & 0 deletions src/content-tags-drawer/ContentTagsDrawer.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,20 @@ import {
import { getTaxonomyListData } from '../taxonomy/data/api';
import messages from './messages';
import { ContentTagsDrawerSheetContext } from './common/context';
import { languageExportId } from './utils';

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

jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useParams: () => ({
contentId,
}),
useNavigate: () => mockNavigate,
}));

// FIXME: replace these mocks with API mocks
Expand Down Expand Up @@ -256,6 +259,83 @@ describe('<ContentTagsDrawer />', () => {
});
};

const setupMockDataLanguageTaxonomyTestings = (hasTags) => {
useContentTaxonomyTagsData.mockReturnValue({
isSuccess: true,
data: {
taxonomies: [
{
name: 'Languages',
taxonomyId: 123,
exportId: languageExportId,
canTagObject: true,
tags: hasTags ? [
{
value: 'Tag 1',
lineage: ['Tag 1'],
canDeleteObjecttag: true,
},
] : [],
},
{
name: 'Taxonomy 1',
taxonomyId: 1234,
canTagObject: true,
tags: [
{
value: 'Tag 1',
lineage: ['Tag 1'],
canDeleteObjecttag: true,
},
{
value: 'Tag 2',
lineage: ['Tag 2'],
canDeleteObjecttag: true,
},
],
},
],
},
});
getTaxonomyListData.mockResolvedValue({
results: [
{
id: 123,
name: 'Languages',
description: 'This is a description 1',
exportId: languageExportId,
canTagObject: true,
},
{
id: 1234,
name: 'Taxonomy 1',
description: 'This is a description 2',
canTagObject: true,
},
],
});

useTaxonomyTagsData.mockReturnValue({
hasMorePages: false,
canAddTag: false,
tagPages: {
isLoading: false,
isError: false,
data: [{
value: 'Tag 1',
externalId: null,
childCount: 0,
depth: 0,
parentValue: null,
id: 12345,
subTagsUrl: null,
canChangeTag: false,
canDeleteTag: false,
}],
},
});
};

const setupLargeMockDataForStagedTagsTesting = () => {
useContentTaxonomyTagsData.mockReturnValue({
isSuccess: true,
Expand Down Expand Up @@ -1057,4 +1137,47 @@ describe('<ContentTagsDrawer />', () => {

expect(screen.getByText(/tag 3/i)).toBeInTheDocument();
});

it('should show Language Taxonomy', async () => {
setupMockDataLanguageTaxonomyTestings(true);
render(<RootWrapper />);
expect(await screen.findByText('Languages')).toBeInTheDocument();
});

it('should hide Language Taxonomy', async () => {
setupMockDataLanguageTaxonomyTestings(false);
render(<RootWrapper />);
expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument();

expect(screen.queryByText('Languages')).not.toBeInTheDocument();
});

it('should show empty drawer message', async () => {
useContentTaxonomyTagsData.mockReturnValue({
isSuccess: true,
data: {
taxonomies: [],
},
});
getTaxonomyListData.mockResolvedValue({
results: [],
});
useTaxonomyTagsData.mockReturnValue({
hasMorePages: false,
canAddTag: false,
tagPages: {
isLoading: false,
isError: false,
data: [],
},
});

render(<RootWrapper />);
expect(await screen.findByText(/to use tags, please or contact your administrator\./i)).toBeInTheDocument();
const enableButton = screen.getByRole('button', {
name: /enable a taxonomy/i,
});
fireEvent.click(enableButton);
expect(mockNavigate).toHaveBeenCalledWith('/taxonomies');
});
});
10 changes: 8 additions & 2 deletions src/content-tags-drawer/ContentTagsDrawerHelper.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useIntl } from '@edx/frontend-platform/i18n';
import { cloneDeep } from 'lodash';
import { useContentData, useContentTaxonomyTagsData, useContentTaxonomyTagsUpdater } from './data/apiHooks';
import { useTaxonomyList } from '../taxonomy/data/apiHooks';
import { extractOrgFromContentId } from './utils';
import { extractOrgFromContentId, languageExportId } from './utils';
import messages from './messages';
import { ContentTagsDrawerSheetContext } from './common/context';

Expand Down Expand Up @@ -142,8 +142,14 @@ const useContentTagsDrawerContext = (contentId) => {
}
});

// Delete Language taxonomy if is empty
const filteredTaxonomies = taxonomiesList.filter(
(taxonomy) => taxonomy.exportId !== languageExportId
|| taxonomy.contentTags.length !== 0,
);

return {
fechedTaxonomies: sortTaxonomies(taxonomiesList),
fechedTaxonomies: sortTaxonomies(filteredTaxonomies),
fechedOtherTaxonomies: otherTaxonomiesList,
};
}
Expand Down
4 changes: 3 additions & 1 deletion src/content-tags-drawer/ContentTagsDropDownSelector.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,9 @@ const ContentTagsDropDownSelector = ({

{ tagPages.data.length === 0 && !tagPages.isLoading && (
<div className="d-flex justify-content-center muted-text">
<FormattedMessage {...messages.noTagsFoundMessage} values={{ searchTerm }} />
{ searchTerm
? <FormattedMessage {...messages.noTagsFoundMessage} values={{ searchTerm }} />
: <FormattedMessage {...messages.noTagsInTaxonomyMessage} />}
</div>
)}

Expand Down
24 changes: 24 additions & 0 deletions src/content-tags-drawer/ContentTagsDropDownSelector.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -282,4 +282,28 @@ describe('<ContentTagsDropDownSelector />', () => {
expect(getByText(message)).toBeInTheDocument();
});
});

it('should render "noTagsInTaxonomy" message if taxonomy is empty', async () => {
useTaxonomyTagsData.mockReturnValueOnce({
hasMorePages: false,
tagPages: {
isLoading: false,
isError: false,
isSuccess: true,
data: [],
},
});

const searchTerm = '';
await act(async () => {
const { getByText } = await getComponent({ ...data, searchTerm });

await waitFor(() => {
expect(useTaxonomyTagsData).toBeCalledWith(data.taxonomyId, null, 1, searchTerm);
});

const message = 'No tags in this taxonomy yet';
expect(getByText(message)).toBeInTheDocument();
});
});
});
15 changes: 15 additions & 0 deletions src/content-tags-drawer/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ const messages = defineMessages({
id: 'course-authoring.content-tags-drawer.tags-dropdown-selector.no-tags-found',
defaultMessage: 'No tags found with the search term "{searchTerm}"',
},
noTagsInTaxonomyMessage: {
id: 'course-authoring.content-tags-drawer.tags-dropdown-selector.no-tags-in-taxonomy',
defaultMessage: 'No tags in this taxonomy yet',
description: 'Message when the user uses the tags dropdown selector of an empty taxonomy',
},
taxonomyTagChecked: {
id: 'course-authoring.content-tags-drawer.tags-dropdown-selector.tag-checked',
defaultMessage: 'Checked',
Expand Down Expand Up @@ -124,6 +129,16 @@ const messages = defineMessages({
defaultMessage: 'These tags are already applied, but you can\'t add new ones as you don\'t have access to their taxonomies.',
description: 'Description of "Other tags" subsection in tags drawer',
},
emptyDrawerContent: {
id: 'course-authoring.content-tags-drawer.empty',
defaultMessage: 'To use tags, please {link} or contact your administrator.',
description: 'Message when there are no taxonomies.',
},
emptyDrawerContentLink: {
id: 'course-authoring.content-tags-drawer.empty-link',
defaultMessage: 'enable a taxonomy',
description: 'Message of the link used in empty drawer message.',
},
});

export default messages;
1 change: 1 addition & 0 deletions src/content-tags-drawer/utils.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
// eslint-disable-next-line import/prefer-default-export
export const extractOrgFromContentId = (contentId) => contentId.split('+')[0].split(':')[1];
export const languageExportId = 'languages-v1';

0 comments on commit 460de70

Please sign in to comment.