diff --git a/src/index.scss b/src/index.scss index f1e1999361..480e868998 100755 --- a/src/index.scss +++ b/src/index.scss @@ -19,4 +19,4 @@ @import "course-updates/CourseUpdates"; @import "export-page/CourseExportPage"; @import "import-page/CourseImportPage"; -@import "taxonomy/TaxonomyCard.scss"; +@import "taxonomy/taxonomy-card/TaxonomyCard.scss"; diff --git a/src/taxonomy/TaxonomyListPage.jsx b/src/taxonomy/TaxonomyListPage.jsx index 6026e34d27..bf285133dc 100644 --- a/src/taxonomy/TaxonomyListPage.jsx +++ b/src/taxonomy/TaxonomyListPage.jsx @@ -9,7 +9,7 @@ import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import Header from '../header'; import SubHeader from '../generic/sub-header/SubHeader'; import messages from './messages'; -import TaxonomyCard from './TaxonomyCard'; +import TaxonomyCard from './taxonomy-card/TaxonomyCard'; import { useTaxonomyListDataResponse, useIsTaxonomyListDataLoaded } from './api/hooks/selectors'; const TaxonomyListPage = ({ intl }) => { diff --git a/src/taxonomy/messages.js b/src/taxonomy/messages.js index f64a56156c..24f80bf6da 100644 --- a/src/taxonomy/messages.js +++ b/src/taxonomy/messages.js @@ -45,6 +45,30 @@ const messages = defineMessages({ id: 'course-authoring.taxonomy-list.menu.alt', defaultMessage: '{name} menu', }, + exportModalTitle: { + id: 'course-authoring.taxonomy-list.modal.export.title', + defaultMessage: 'Select format to export', + }, + exportModalBodyDescription: { + id: 'course-authoring.taxonomy-list.modal.export.body', + defaultMessage: 'Select the file format in which you would like the taxonomy to be exported:', + }, + exportModalSubmitButtonLabel: { + id: 'course-authoring.taxonomy-list.modal.export.submit.label', + defaultMessage: 'Export', + }, + taxonomyCSVFormat: { + id: 'course-authoring.taxonomy-list.csv-format', + defaultMessage: 'CSV file', + }, + taxonomyJSONFormat: { + id: 'course-authoring.taxonomy-list.json-format', + defaultMessage: 'JSON file', + }, + taxonomyModalsCancelLabel: { + id: 'course-authoring.taxonomy-list.modal.cancel', + defaultMessage: 'Cancel', + }, }); export default messages; diff --git a/src/taxonomy/modals/ExportModal.jsx b/src/taxonomy/modals/ExportModal.jsx new file mode 100644 index 0000000000..360151dc1b --- /dev/null +++ b/src/taxonomy/modals/ExportModal.jsx @@ -0,0 +1,79 @@ +import React, { useState } from 'react'; +import { + ActionRow, + Button, + Form, + ModalDialog, +} from '@edx/paragon'; +import PropTypes from 'prop-types'; +import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; +import messages from '../messages'; + +const ExportModal = ({ + taxonomyId, + isOpen, + onClose, + intl, +}) => { + const [modalSize, setModalSize] = useState('csv'); + + return ( + + + + {intl.formatMessage(messages.exportModalTitle)} + + + + + + {intl.formatMessage(messages.exportModalBodyDescription)} + + setModalSize(e.target.value)} + > + + {intl.formatMessage(messages.taxonomyCSVFormat)} + + + {intl.formatMessage(messages.taxonomyJSONFormat)} + + + + + + + + {intl.formatMessage(messages.taxonomyModalsCancelLabel)} + + + + + + ); +}; + +ExportModal.propTypes = { + taxonomyId: PropTypes.number.isRequired, + isOpen: PropTypes.bool.isRequired, + onClose: PropTypes.func.isRequired, + intl: intlShape.isRequired, +}; + +export default injectIntl(ExportModal); diff --git a/src/taxonomy/TaxonomyCard.jsx b/src/taxonomy/taxonomy-card/TaxonomyCard.jsx similarity index 54% rename from src/taxonomy/TaxonomyCard.jsx rename to src/taxonomy/taxonomy-card/TaxonomyCard.jsx index c4d4c25d5a..df340fe1cf 100644 --- a/src/taxonomy/TaxonomyCard.jsx +++ b/src/taxonomy/taxonomy-card/TaxonomyCard.jsx @@ -1,31 +1,36 @@ import React, { useState } from 'react'; import { Badge, - IconButton, Card, OverlayTrigger, Popover, - ModalPopup, - Menu, - Icon, - MenuItem, } from '@edx/paragon'; -import { MoreVert } from '@edx/paragon/icons'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import PropTypes from 'prop-types'; import classNames from 'classnames'; -import messages from './messages'; +import messages from '../messages'; +import TaxonomyCardMenu from './TaxonomyCardMenu'; +import ExportModal from '../modals/ExportModal'; const TaxonomyCard = ({ className, original, intl }) => { const { id, name, description, systemDefined, orgsCount, } = original; - const [menuIsOpen, setMenuIsOpen] = useState(false); - const [menuTarget, setMenuTarget] = useState(null); + const [isExportModalOpen, setIsExportModalOpen] = useState(false); const orgsCountEnabled = () => orgsCount !== undefined && orgsCount !== 0; + const onClickMenuItem = (menuName) => { + switch (menuName) { + case 'export': + setIsExportModalOpen(true); + break; + default: + break; + } + }; + const getSystemBadgeToolTip = () => ( @@ -61,52 +66,42 @@ const TaxonomyCard = ({ className, original, intl }) => { return undefined; }; - const onClickExport = () => { - setMenuIsOpen(false); - }; - const getHeaderActions = () => ( + + ); + + const renderModals = () => ( + // eslint-disable-next-line react/jsx-no-useless-fragment <> - setMenuIsOpen(true)} - ref={setMenuTarget} - src={MoreVert} - iconAs={Icon} - alt={intl.formatMessage(messages.taxonomyMenuAlt, { name })} - data-testid={`taxonomy-card-menu-button-${id}`} - /> - setMenuIsOpen(false)} - > - - - {intl.formatMessage(messages.taxonomyCardExportMenu)} - - - + {isExportModalOpen && ( + setIsExportModalOpen(false)} + /> + )} ); return ( - - - - - {description} - - - + <> + + + + + {description} + + + + {renderModals()} + ); }; diff --git a/src/taxonomy/TaxonomyCard.scss b/src/taxonomy/taxonomy-card/TaxonomyCard.scss similarity index 100% rename from src/taxonomy/TaxonomyCard.scss rename to src/taxonomy/taxonomy-card/TaxonomyCard.scss diff --git a/src/taxonomy/TaxonomyCard.test.jsx b/src/taxonomy/taxonomy-card/TaxonomyCard.test.jsx similarity index 81% rename from src/taxonomy/TaxonomyCard.test.jsx rename to src/taxonomy/taxonomy-card/TaxonomyCard.test.jsx index a4e192342a..91e76c984a 100644 --- a/src/taxonomy/TaxonomyCard.test.jsx +++ b/src/taxonomy/taxonomy-card/TaxonomyCard.test.jsx @@ -5,7 +5,7 @@ import { AppProvider } from '@edx/frontend-platform/react'; import { render, fireEvent } from '@testing-library/react'; import PropTypes from 'prop-types'; -import initializeStore from '../store'; +import initializeStore from '../../store'; import TaxonomyCard from './TaxonomyCard'; @@ -92,7 +92,7 @@ describe('', async () => { // Click on the menu button to open fireEvent.click(getByTestId('taxonomy-card-menu-button-1')); - // Menu open + // Menu opened expect(getByTestId('taxonomy-card-menu-1')).toBeInTheDocument(); // Click on any element to close the menu @@ -101,4 +101,24 @@ describe('', async () => { // Menu closed expect(() => getByTestId('taxonomy-card-menu-1')).toThrow(); }); + + test('should open export modal on export menu click', () => { + const { getByTestId, getByText } = render(); + + // Modal closed + expect(() => getByText('Select format to export')).toThrow(); + + // Click on export menu + fireEvent.click(getByTestId('taxonomy-card-menu-button-1')); + fireEvent.click(getByText('Export')); + + // Modal opened + expect(getByText('Select format to export')).toBeInTheDocument(); + + // Click on cancel button + fireEvent.click(getByText('Cancel')); + + // Modal closed + expect(() => getByText('Select format to export')).toThrow(); + }); }); diff --git a/src/taxonomy/taxonomy-card/TaxonomyCardMenu.jsx b/src/taxonomy/taxonomy-card/TaxonomyCardMenu.jsx new file mode 100644 index 0000000000..87e1722f4e --- /dev/null +++ b/src/taxonomy/taxonomy-card/TaxonomyCardMenu.jsx @@ -0,0 +1,58 @@ +import React, { useState } from 'react'; +import { + IconButton, + ModalPopup, + Menu, + Icon, + MenuItem, +} from '@edx/paragon'; +import { MoreVert } from '@edx/paragon/icons'; +import PropTypes from 'prop-types'; +import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; +import messages from '../messages'; + +const TaxonomyCardMenu = ({ + id, name, onClickMenuItem, intl, +}) => { + const [menuIsOpen, setMenuIsOpen] = useState(false); + const [menuTarget, setMenuTarget] = useState(null); + + const onClickItem = (menuName) => { + setMenuIsOpen(false); + onClickMenuItem(menuName); + }; + + return ( + <> + setMenuIsOpen(true)} + ref={setMenuTarget} + src={MoreVert} + iconAs={Icon} + alt={intl.formatMessage(messages.taxonomyMenuAlt, { name })} + data-testid={`taxonomy-card-menu-button-${id}`} + /> + setMenuIsOpen(false)} + > + + onClickItem('export')}> + {intl.formatMessage(messages.taxonomyCardExportMenu)} + + + + + ); +}; + +TaxonomyCardMenu.propTypes = { + id: PropTypes.number.isRequired, + name: PropTypes.string.isRequired, + onClickMenuItem: PropTypes.func.isRequired, + intl: intlShape.isRequired, +}; + +export default injectIntl(TaxonomyCardMenu);