diff --git a/src/taxonomy/TaxonomyListPage.jsx b/src/taxonomy/TaxonomyListPage.jsx index 9a1e69ffd..8f991fbfd 100644 --- a/src/taxonomy/TaxonomyListPage.jsx +++ b/src/taxonomy/TaxonomyListPage.jsx @@ -1,19 +1,69 @@ import React, { useContext } from 'react'; import { + Button, CardView, Container, DataTable, + Dropdown, + OverlayTrigger, Spinner, + Tooltip, } from '@edx/paragon'; +import { + Add, +} from '@edx/paragon/icons'; import { useIntl } from '@edx/frontend-platform/i18n'; import { Helmet } from 'react-helmet'; import SubHeader from '../generic/sub-header/SubHeader'; import getPageHeadTitle from '../generic/utils'; import messages from './messages'; import TaxonomyCard from './taxonomy-card'; +import { getTaxonomyTemplateApiUrl } from './data/api'; import { useTaxonomyListDataResponse, useIsTaxonomyListDataLoaded, useDeleteTaxonomy } from './data/apiHooks'; import { TaxonomyContext } from './common/context'; +const TaxonomyListHeaderButtons = () => { + const intl = useIntl(); + return ( + <> + + {intl.formatMessage(messages.downloadTemplateButtonHint)} + + )} + > + + + {intl.formatMessage(messages.downloadTemplateButtonLabel)} + + + + {intl.formatMessage(messages.downloadTemplateButtonCSVLabel)} + + + {intl.formatMessage(messages.downloadTemplateButtonJSONLabel)} + + + + + + + ); +}; + const TaxonomyListPage = () => { const intl = useIntl(); const deleteTaxonomy = useDeleteTaxonomy(); @@ -37,12 +87,6 @@ const TaxonomyListPage = () => { }; const { taxonomyListData, isLoaded } = useTaxonomyListData(); - const getHeaderButtons = () => ( - // Download template and import buttons. - // TODO Add functionality to this buttons. - undefined - ); - const getOrgSelect = () => ( // Organization select component // TODO Add functionality to this component @@ -59,7 +103,7 @@ const TaxonomyListPage = () => { } hideBorder /> diff --git a/src/taxonomy/TaxonomyListPage.test.jsx b/src/taxonomy/TaxonomyListPage.test.jsx index 17c95034e..c7a634d29 100644 --- a/src/taxonomy/TaxonomyListPage.test.jsx +++ b/src/taxonomy/TaxonomyListPage.test.jsx @@ -5,7 +5,7 @@ import { AppProvider } from '@edx/frontend-platform/react'; import { act, render, fireEvent } from '@testing-library/react'; import initializeStore from '../store'; - +import { getTaxonomyTemplateApiUrl } from './data/api'; import TaxonomyListPage from './TaxonomyListPage'; import { useTaxonomyListDataResponse, useIsTaxonomyListDataLoaded } from './data/apiHooks'; import { TaxonomyContext } from './common/context'; @@ -84,6 +84,24 @@ describe('', async () => { }); }); + it.each(['CSV', 'JSON'])('downloads the taxonomy template %s', async (fileFormat) => { + useIsTaxonomyListDataLoaded.mockReturnValue(true); + useTaxonomyListDataResponse.mockReturnValue({ + results: [{ + id: 1, + name: 'Taxonomy', + description: 'This is a description', + }], + }); + const { findByRole } = render(); + const templateMenu = await findByRole('button', { name: 'Download template' }); + fireEvent.click(templateMenu); + const templateButton = await findByRole('link', { name: `${fileFormat} template` }); + fireEvent.click(templateButton); + + expect(templateButton.href).toBe(getTaxonomyTemplateApiUrl(fileFormat.toLowerCase())); + }); + it('should show the success toast after delete', async () => { useIsTaxonomyListDataLoaded.mockReturnValue(true); useTaxonomyListDataResponse.mockReturnValue({ diff --git a/src/taxonomy/data/api.js b/src/taxonomy/data/api.js index f7fdf447c..95a675ba4 100644 --- a/src/taxonomy/data/api.js +++ b/src/taxonomy/data/api.js @@ -18,6 +18,10 @@ export const getExportTaxonomyApiUrl = (pk, format) => new URL( `api/content_tagging/v1/taxonomies/${pk}/export/?output_format=${format}&download=1`, getApiBaseUrl(), ).href; +export const getTaxonomyTemplateApiUrl = (format) => new URL( + `api/content_tagging/v1/taxonomies/import/template.${format}`, + getApiBaseUrl(), +).href; export const getTaxonomyApiUrl = (pk) => new URL(`api/content_tagging/v1/taxonomies/${pk}/`, getApiBaseUrl()).href; /** diff --git a/src/taxonomy/data/api.test.js b/src/taxonomy/data/api.test.js index 3b83dd1b2..b95ed9a57 100644 --- a/src/taxonomy/data/api.test.js +++ b/src/taxonomy/data/api.test.js @@ -5,10 +5,10 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; import { taxonomyListMock } from '../__mocks__'; import { - getTaxonomyListApiUrl, getExportTaxonomyApiUrl, - getTaxonomyListData, getTaxonomyExportFile, + getTaxonomyListApiUrl, + getTaxonomyListData, getTaxonomyApiUrl, deleteTaxonomy, } from './api'; @@ -26,6 +26,7 @@ describe('taxonomy api calls', () => { roles: [], }, }); + axiosMock = new MockAdapter(getAuthenticatedHttpClient()); }); @@ -44,14 +45,6 @@ describe('taxonomy api calls', () => { window.location = location; }); - it('should get taxonomy list data', async () => { - axiosMock.onGet(getTaxonomyListApiUrl()).reply(200, taxonomyListMock); - const result = await getTaxonomyListData(); - - expect(axiosMock.history.get[0].url).toEqual(getTaxonomyListApiUrl()); - expect(result).toEqual(taxonomyListMock); - }); - it('should get taxonomy list data with org', async () => { const org = 'testOrg'; axiosMock.onGet(getTaxonomyListApiUrl(org)).reply(200, taxonomyListMock); @@ -68,7 +61,7 @@ describe('taxonomy api calls', () => { expect(axiosMock.history.delete[0].url).toEqual(getTaxonomyApiUrl()); }); - it('should set window.location.href correctly', () => { + it('Export should set window.location.href correctly', () => { const pk = 1; const format = 'json'; diff --git a/src/taxonomy/messages.js b/src/taxonomy/messages.js index 0a60c4725..ace08d33d 100644 --- a/src/taxonomy/messages.js +++ b/src/taxonomy/messages.js @@ -9,6 +9,18 @@ const messages = defineMessages({ id: 'course-authoring.taxonomy-list.button.download-template.label', defaultMessage: 'Download template', }, + downloadTemplateButtonCSVLabel: { + id: 'course-authoring.taxonomy-list.button.download-template.csv.label', + defaultMessage: 'CSV template', + }, + downloadTemplateButtonJSONLabel: { + id: 'course-authoring.taxonomy-list.button.download-template.json.label', + defaultMessage: 'JSON template', + }, + downloadTemplateButtonHint: { + id: 'course-authoring.taxonomy-list.butotn.download-template.hint', + defaultMessage: 'Download example taxonomy', + }, importButtonLabel: { id: 'course-authoring.taxonomy-list.button.import.label', defaultMessage: 'Import',