Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FC-0049] feat: Other Tags section added on tags drawer #987

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/content-tags-drawer/ContentTagsDrawer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const ContentTagsDrawer = ({ id, onClose }) => {
toastMessage,
closeToast,
setCollapsibleToInitalState,
otherTaxonomies,
} = context;

let onCloseDrawer = onClose;
Expand Down Expand Up @@ -122,6 +123,29 @@ const ContentTagsDrawer = ({ id, onClose }) => {
</div>
))
: <Loading />}
{otherTaxonomies.length !== 0 && (
<div>
<p className="h4 text-gray-500 font-weight-bold">
{intl.formatMessage(messages.otherTagsHeader)}
</p>
<p className="other-description text-gray-500">
{intl.formatMessage(messages.otherTagsDescription)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This shouldn't be h6 because that's making the text bold. The text should not be bold.

Screenshot 2024-05-08 at 10 37 18 AM

vs.

Screenshot 2024-05-08 at 10 38 16 AM

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated here: 5a45416

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

</p>
{ isTaxonomyListLoaded && isContentTaxonomyTagsLoaded && (
otherTaxonomies.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>
)}
</Container>
</Container>

Expand Down
4 changes: 4 additions & 0 deletions src/content-tags-drawer/ContentTagsDrawer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
background-color: transparent;
color: $gray-300 !important;
}

.other-description {
font-size: .9rem;
}
}

// Apply styles to sheet only if it has a child with a .tags-drawer class
Expand Down
142 changes: 142 additions & 0 deletions src/content-tags-drawer/ContentTagsDrawer.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,100 @@ describe('<ContentTagsDrawer />', () => {
});
};

const setupMockDataWithOtherTagsTestings = () => {
useContentTaxonomyTagsData.mockReturnValue({
isSuccess: true,
data: {
taxonomies: [
{
name: 'Taxonomy 1',
taxonomyId: 123,
canTagObject: true,
tags: [
{
value: 'Tag 1',
lineage: ['Tag 1'],
canDeleteObjecttag: true,
},
{
value: 'Tag 2',
lineage: ['Tag 2'],
canDeleteObjecttag: true,
},
],
},
{
name: 'Taxonomy 2',
taxonomyId: 1234,
canTagObject: false,
tags: [
{
value: 'Tag 3',
lineage: ['Tag 3'],
canDeleteObjecttag: true,
},
{
value: 'Tag 4',
lineage: ['Tag 4'],
canDeleteObjecttag: true,
},
],
},
],
},
});
getTaxonomyListData.mockResolvedValue({
results: [
{
id: 123,
name: 'Taxonomy 1',
description: 'This is a description 1',
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,
}, {
value: 'Tag 2',
externalId: null,
childCount: 0,
depth: 0,
parentValue: null,
id: 12346,
subTagsUrl: null,
canChangeTag: false,
canDeleteTag: false,
}, {
value: 'Tag 3',
externalId: null,
childCount: 0,
depth: 0,
parentValue: null,
id: 12347,
subTagsUrl: null,
canChangeTag: false,
canDeleteTag: false,
}],
},
});
};

const setupLargeMockDataForStagedTagsTesting = () => {
useContentTaxonomyTagsData.mockReturnValue({
isSuccess: true,
Expand Down Expand Up @@ -915,4 +1009,52 @@ describe('<ContentTagsDrawer />', () => {
expect(taxonomies[i].textContent).toBe(expectedOrder[i]);
}
});

it('should not show "Other tags" section', async () => {
setupMockDataForStagedTagsTesting();

render(<RootWrapper />);
expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument();

expect(screen.queryByText('Other tags')).not.toBeInTheDocument();
});

it('should show "Other tags" section', async () => {
setupMockDataWithOtherTagsTestings();

render(<RootWrapper />);
expect(await screen.findByText('Taxonomy 1')).toBeInTheDocument();

expect(screen.getByText('Other tags')).toBeInTheDocument();
expect(screen.getByText('Taxonomy 2')).toBeInTheDocument();
expect(screen.getByText('Tag 3')).toBeInTheDocument();
expect(screen.getByText('Tag 4')).toBeInTheDocument();
});

it('should test delete "Other tags" and cancel', async () => {
setupMockDataWithOtherTagsTestings();
render(<RootWrapper />);
expect(await screen.findByText('Taxonomy 2')).toBeInTheDocument();

// To edit mode
const editTagsButton = screen.getByRole('button', {
name: /edit tags/i,
});
fireEvent.click(editTagsButton);

// Delete the tag
const tag = screen.getByText(/tag 3/i);
const deleteButton = within(tag).getByRole('button', {
name: /delete/i,
});
fireEvent.click(deleteButton);

expect(tag).not.toBeInTheDocument();

// Click "Cancel"
const cancelButton = screen.getByRole('button', { name: /cancel/i });
fireEvent.click(cancelButton);

expect(screen.getByText(/tag 3/i)).toBeInTheDocument();
});
});
53 changes: 50 additions & 3 deletions src/content-tags-drawer/ContentTagsDrawerHelper.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
* showToastAfterSave: () => void,
* closeToast: () => void,
* setCollapsibleToInitalState: () => void,
* otherTaxonomies: TagsInTaxonomy[],
* }}
*/
const useContentTagsDrawerContext = (contentId) => {
Expand All @@ -61,6 +62,8 @@
const [globalStagedRemovedContentTags, setGlobalStagedRemovedContentTags] = React.useState({});
// Merges feched tags, global staged tags and global removed staged tags
const [tagsByTaxonomy, setTagsByTaxonomy] = React.useState(/** @type TagsInTaxonomy[] */ ([]));
// Other taxonomies that the user doesn't have permissions
const [otherTaxonomies, setOtherTaxonomies] = React.useState(/** @type TagsInTaxonomy[] */ ([]));
// This stores taxonomy collapsible states (open/close).
const [collapsibleStates, setColapsibleStates] = React.useState({});
// Message to show a toast in the content drawer.
Expand All @@ -77,7 +80,7 @@
const { data: taxonomyListData, isSuccess: isTaxonomyListLoaded } = useTaxonomyList(org);

// Tags feched from database
const fechedTaxonomies = React.useMemo(() => {
const { fechedTaxonomies, fechedOtherTaxonomies } = React.useMemo(() => {
const sortTaxonomies = (taxonomiesList) => {
const taxonomiesWithData = taxonomiesList.filter(
(t) => t.contentTags.length !== 0,
Expand Down Expand Up @@ -117,17 +120,37 @@

const contentTaxonomies = contentTaxonomyTagsData.taxonomies;

const otherTaxonomiesList = [];

// eslint-disable-next-line array-callback-return
contentTaxonomies.map((contentTaxonomyTags) => {
const contentTaxonomy = taxonomiesList.find((taxonomy) => taxonomy.id === contentTaxonomyTags.taxonomyId);
if (contentTaxonomy) {
contentTaxonomy.contentTags = contentTaxonomyTags.tags;
} else {
otherTaxonomiesList.push({
canChangeTaxonomy: false,
canDeleteTaxonomy: false,
canTagObject: false,
contentTags: contentTaxonomyTags.tags,
enabled: true,
exportId: contentTaxonomyTags.exportId,
id: contentTaxonomyTags.taxonomyId,
name: contentTaxonomyTags.name,
visibleToAuthors: true,
});
}
});

return sortTaxonomies(taxonomiesList);
return {
fechedTaxonomies: sortTaxonomies(taxonomiesList),
fechedOtherTaxonomies: otherTaxonomiesList,
};
}
return [];
return {
fechedTaxonomies: [],
fechedOtherTaxonomies: [],
};
}, [taxonomyListData, contentTaxonomyTagsData]);

// Add a content tags to the staged tags for a taxonomy
Expand Down Expand Up @@ -204,6 +227,9 @@
fechedTaxonomies.forEach((taxonomy) => {
updatedState[taxonomy.id] = true;
});
fechedOtherTaxonomies.forEach((taxonomy) => {
updatedState[taxonomy.id] = true;
});
setColapsibleStates(updatedState);
}, [fechedTaxonomies, setColapsibleStates]);

Expand All @@ -214,6 +240,10 @@
// Taxonomy with content tags must be open
updatedState[taxonomy.id] = taxonomy.contentTags.length !== 0;
});
fechedOtherTaxonomies.forEach((taxonomy) => {
// Taxonomy with content tags must be open
updatedState[taxonomy.id] = taxonomy.contentTags.length !== 0;
});
setColapsibleStates(updatedState);
}, [fechedTaxonomies, setColapsibleStates]);

Expand Down Expand Up @@ -310,6 +340,10 @@
{ ...acc, [obj.id]: obj }
), {});

const mergedOtherTaxonomies = cloneDeep(fechedOtherTaxonomies).reduce((acc, obj) => (
{ ...acc, [obj.id]: obj }
), {});

Object.keys(globalStagedContentTags).forEach((taxonomyId) => {
if (mergedTags[taxonomyId]) {
// TODO test this
Expand All @@ -329,6 +363,10 @@
mergedTags[taxonomyId].contentTags = mergedTags[taxonomyId].contentTags.filter(
(t) => !globalStagedRemovedContentTags[taxonomyId].includes(t.value),
);
} else if (mergedOtherTaxonomies[taxonomyId]) {
mergedOtherTaxonomies[taxonomyId].contentTags = mergedOtherTaxonomies[taxonomyId].contentTags.filter(
(t) => !globalStagedRemovedContentTags[taxonomyId].includes(t.value),
);
}
});

Expand All @@ -337,6 +375,7 @@
const mergedTagsArray = fechedTaxonomies.map(obj => mergedTags[obj.id]);

setTagsByTaxonomy(mergedTagsArray);
setOtherTaxonomies(Object.values(mergedOtherTaxonomies));

if (setBlockingSheet) {
const areChangesInTags = () => {
Expand Down Expand Up @@ -364,6 +403,7 @@
}
}, [
fechedTaxonomies,
fechedOtherTaxonomies,
globalStagedContentTags,
globalStagedRemovedContentTags,
]);
Expand All @@ -376,6 +416,12 @@
tags: tags.contentTags.map(t => t.value),
});
});
otherTaxonomies.forEach((tags) => {
tagsData.push({

Check warning on line 420 in src/content-tags-drawer/ContentTagsDrawerHelper.jsx

View check run for this annotation

Codecov / codecov/patch

src/content-tags-drawer/ContentTagsDrawerHelper.jsx#L420

Added line #L420 was not covered by tests
taxonomy: tags.id,
tags: tags.contentTags.map(t => t.value),

Check warning on line 422 in src/content-tags-drawer/ContentTagsDrawerHelper.jsx

View check run for this annotation

Codecov / codecov/patch

src/content-tags-drawer/ContentTagsDrawerHelper.jsx#L422

Added line #L422 was not covered by tests
});
});
// @ts-ignore
updateTags.mutate({ tagsData });
}, [tagsByTaxonomy]);
Expand Down Expand Up @@ -408,6 +454,7 @@
showToastAfterSave,
closeToast,
setCollapsibleToInitalState,
otherTaxonomies,
};
};

Expand Down
1 change: 1 addition & 0 deletions src/content-tags-drawer/common/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const ContentTagsDrawerContext = React.createContext({
showToastAfterSave: /** @type{() => void} */ (() => {}),
closeToast: /** @type{() => void} */ (() => {}),
setCollapsibleToInitalState: /** @type{() => void} */ (() => {}),
otherTaxonomies: /** @type{TagsInTaxonomy[]} */ ([]),
});

// This context has not been added to ContentTagsDrawerContext because it has been
Expand Down
1 change: 1 addition & 0 deletions src/content-tags-drawer/data/types.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* @property {number} taxonomyId
* @property {boolean} canTagObject
* @property {Tag[]} tags
* @property {string} exportId
*/

/**
Expand Down
10 changes: 10 additions & 0 deletions src/content-tags-drawer/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,16 @@ const messages = defineMessages({
defaultMessage: 'Delete',
description: 'Alt label for Delete tag button.',
},
otherTagsHeader: {
id: 'course-authoring.content-tags-drawer.other-tags.header',
defaultMessage: 'Other tags',
description: 'Header of "Other tags" subsection in tags drawer',
},
otherTagsDescription: {
id: 'course-authoring.content-tags-drawer.other-tags.description',
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',
},
});

export default messages;
Loading