From 9327948b61d2738a0af605ada6f582e85bb8700d Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Wed, 3 Apr 2024 11:35:25 -0500 Subject: [PATCH] feat: [FC-0036] Refined tag drawer It contains different changes to achieve the reading and editing mode of the drawer tag: * Manage tags drawer footer with buttons added. * Creation of ContentTagsDrawerContext. * Creation of global state and global removed state to allow edit mode. * Update API client to match with openedx-learning 0.9.1: Save tags of multiple taxonomies; to save all tags added/removed on edit mode * Extract TagsTree and use it on the Tags Drawer. * Update TagsTree to allow edit mode. * Add a Toast on Tags Drawer; show the toast afert save. * Scrolling + sticky footer on tags drawer --- .../ContentTagsCollapsible.jsx | 60 ++- .../ContentTagsCollapsible.scss | 9 +- .../ContentTagsCollapsible.test.jsx | 232 +++++++--- .../ContentTagsCollapsibleHelper.jsx | 136 ++++-- src/content-tags-drawer/ContentTagsDrawer.jsx | 262 +++++------ .../ContentTagsDrawer.scss | 21 + .../ContentTagsDrawer.test.jsx | 421 ++++++++++++++++-- .../ContentTagsDrawerHelper.jsx | 373 ++++++++++++++++ src/content-tags-drawer/ContentTagsTree.jsx | 81 ---- .../ContentTagsTree.test.jsx | 57 --- src/content-tags-drawer/TagBubble.jsx | 51 --- src/content-tags-drawer/TagBubble.scss | 5 - src/content-tags-drawer/TagBubble.test.jsx | 109 ----- src/content-tags-drawer/TagsTree.jsx | 168 +++++++ src/content-tags-drawer/TagsTree.scss | 21 + src/content-tags-drawer/TagsTree.test.jsx | 60 +++ .../__mocks__/contentTaxonomyTagsTreeMock.js | 18 + src/content-tags-drawer/common/context.js | 37 ++ src/content-tags-drawer/data/api.js | 8 +- src/content-tags-drawer/data/api.test.js | 6 +- src/content-tags-drawer/data/apiHooks.jsx | 7 +- .../data/apiHooks.test.jsx | 13 +- src/content-tags-drawer/data/types.mjs | 31 ++ src/content-tags-drawer/index.scss | 2 +- src/content-tags-drawer/messages.js | 35 ++ .../tags-sidebar-controls/TagsSidebarBody.jsx | 2 +- .../tags-sidebar-controls/TagsTree.jsx | 50 --- .../tags-sidebar-controls/TagsTree.test.jsx | 13 - 28 files changed, 1585 insertions(+), 703 deletions(-) create mode 100644 src/content-tags-drawer/ContentTagsDrawerHelper.jsx delete mode 100644 src/content-tags-drawer/ContentTagsTree.jsx delete mode 100644 src/content-tags-drawer/ContentTagsTree.test.jsx delete mode 100644 src/content-tags-drawer/TagBubble.jsx delete mode 100644 src/content-tags-drawer/TagBubble.scss delete mode 100644 src/content-tags-drawer/TagBubble.test.jsx create mode 100644 src/content-tags-drawer/TagsTree.jsx create mode 100644 src/content-tags-drawer/TagsTree.scss create mode 100644 src/content-tags-drawer/TagsTree.test.jsx create mode 100644 src/content-tags-drawer/common/context.js delete mode 100644 src/content-tags-drawer/tags-sidebar-controls/TagsTree.jsx delete mode 100644 src/content-tags-drawer/tags-sidebar-controls/TagsTree.test.jsx diff --git a/src/content-tags-drawer/ContentTagsCollapsible.jsx b/src/content-tags-drawer/ContentTagsCollapsible.jsx index b58f33184d..2428e3633e 100644 --- a/src/content-tags-drawer/ContentTagsCollapsible.jsx +++ b/src/content-tags-drawer/ContentTagsCollapsible.jsx @@ -2,7 +2,7 @@ // disable prop-types since we're using TypeScript to define the prop types, // but the linter can't detect that in a .jsx file. /* eslint-disable react/prop-types */ -import React from 'react'; +import React, { useContext } from 'react'; import Select, { components } from 'react-select'; import { Collapsible, @@ -19,14 +19,14 @@ import messages from './messages'; import ContentTagsDropDownSelector from './ContentTagsDropDownSelector'; -import ContentTagsTree from './ContentTagsTree'; - import useContentTagsCollapsibleHelper from './ContentTagsCollapsibleHelper'; +import TagsTree from './TagsTree'; +import { ContentTagsDrawerContext } from './common/context'; -/** @typedef {import("./ContentTagsCollapsible").TagTreeEntry} TagTreeEntry */ /** @typedef {import("./ContentTagsCollapsible").TaxonomySelectProps} TaxonomySelectProps */ /** @typedef {import("../taxonomy/data/types.mjs").TaxonomyData} TaxonomyData */ /** @typedef {import("./data/types.mjs").Tag} ContentTagData */ +/** @typedef {import("./data/types.mjs").StagedTagData} StagedTagData */ /** * Custom Menu component for our Select box @@ -77,7 +77,7 @@ const CustomMenu = (props) => { tabIndex="0" ref={selectCancelRef} variant="tertiary" - className="cancel-add-tags-button" + className="tags-drawer-cancel-button" onClick={handleCancelStagedTags} > { intl.formatMessage(messages.collapsibleCancelStagedTagsButtonText) } @@ -224,17 +224,16 @@ const CustomIndicatorsContainer = (props) => { * * @param {Object} props - The component props. * @param {string} props.contentId - Id of the content object - * @param {{value: string, label: string}[]} props.stagedContentTags + * @param {StagedTagData[]} props.stagedContentTags * - Array of staged tags represented as objects with value/label - * @param {(taxonomyId: number, tag: {value: string, label: string}) => void} props.addStagedContentTag - * - Callback function to add a staged tag for a taxonomy - * @param {(taxonomyId: number, tagValue: string) => void} props.removeStagedContentTag - * - Callback function to remove a staged tag from a taxonomy - * @param {Function} props.setStagedTags - Callback function to set staged tags for a taxonomy to provided tags list * @param {TaxonomyData & {contentTags: ContentTagData[]}} props.taxonomyAndTagsData - Taxonomy metadata & applied tags + * @param {boolean} props.collapsibleState - True if the collapsible is open */ const ContentTagsCollapsible = ({ - contentId, taxonomyAndTagsData, stagedContentTags, addStagedContentTag, removeStagedContentTag, setStagedTags, + contentId, + taxonomyAndTagsData, + stagedContentTags, + collapsibleState, }) => { const intl = useIntl(); const { id: taxonomyId, name, canTagObject } = taxonomyAndTagsData; @@ -245,6 +244,13 @@ const ContentTagsCollapsible = ({ const [selectMenuIsOpen, setSelectMenuIsOpen] = React.useState(false); + const { + isEditMode, + setStagedTags, + openCollapsible, + closeCollapsible, + } = useContext(ContentTagsDrawerContext); + const { tagChangeHandler, removeAppliedTagHandler, @@ -252,14 +258,12 @@ const ContentTagsCollapsible = ({ stagedContentTagsTree, contentTagsCount, checkedTags, - commitStagedTags, + commitStagedTagsToGlobal, updateTags, } = useContentTagsCollapsibleHelper( contentId, - taxonomyAndTagsData, - addStagedContentTag, - removeStagedContentTag, stagedContentTags, + taxonomyAndTagsData, ); const [searchTerm, setSearchTerm] = React.useState(''); @@ -309,12 +313,12 @@ const ContentTagsCollapsible = ({ }, [taxonomyId, setStagedTags, stagedContentTags, tagChangeHandler]); const handleCommitStagedTags = React.useCallback(() => { - commitStagedTags(); + commitStagedTagsToGlobal(); handleStagedTagsMenuChange([]); selectRef.current?.blur(); setSearchTerm(''); setSelectMenuIsOpen(false); - }, [commitStagedTags, handleStagedTagsMenuChange, selectRef, setSearchTerm]); + }, [commitStagedTagsToGlobal, handleStagedTagsMenuChange, selectRef, setSearchTerm]); const handleCancelStagedTags = React.useCallback(() => { handleStagedTagsMenuChange([]); @@ -366,6 +370,9 @@ const ContentTagsCollapsible = ({
closeCollapsible(taxonomyId)} + onOpen={() => openCollapsible(taxonomyId)} > @@ -379,13 +386,19 @@ const ContentTagsCollapsible = ({ -
- -
+ { Object.keys(appliedContentTagsTree).length !== 0 + && ( +
+ +
+ )}
- - {canTagObject && ( + {isEditMode && canTagObject && (