-
Notifications
You must be signed in to change notification settings - Fork 84
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: bare bones taxonomy detail page [FC-0036] (#655)
* feat: System-defined tooltip added * feat: Taxonomy card menu added. Export menu item added * feat: Modal for export taxonomy * feat: Connect with export API * test: Tests for API and selectors * feat: Use windows.location.href to call the export endpoint * test: ExportModal.test added * style: Delete unnecesary code * docs: README updated with taxonomy feature * style: TaxonomyCard updated to a better code style * style: injectIntl replaced by useIntl on taxonomy pages and components * refactor: Move and rename taxonomy UI components to match 0002 ADR * refactor: Move api to data to match with 0002 ADR * test: Refactor ExportModal tests * chore: Fix validations * chore: Lint * refactor: Moving hooks to apiHooks * feat: add taxonomy detail page * fix: address nits in PR review * refactor: move data/selectors to data/apiHooks and fix tests to mock useQuery. * fix: address nits in PR review * fix: replace taxonomy menu ModalPopup with Dropdown menu Avoids clicking through to the card when using the menu button to hide a card's menu. * fix: change taxonomy URLs * /taxonomy-list is now /taxonomies, and there's a temporary redirect * /taxonomy-list/:id: is now /taxonomy/:id: --------- Co-authored-by: Christofer <[email protected]> Co-authored-by: XnpioChV <[email protected]> Co-authored-by: Christofer Chavez <[email protected]> Co-authored-by: Jillian Vogel <[email protected]> Co-authored-by: Braden MacDonald <[email protected]>
- Loading branch information
1 parent
375006d
commit 02cdccc
Showing
30 changed files
with
963 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { StudioFooter } from '@edx/frontend-component-footer'; | ||
import { Outlet } from 'react-router-dom'; | ||
|
||
import Header from '../header'; | ||
|
||
const TaxonomyLayout = () => ( | ||
<div className="bg-light-400"> | ||
<Header isHiddenMainMenu /> | ||
<Outlet /> | ||
<StudioFooter /> | ||
</div> | ||
); | ||
|
||
export default TaxonomyLayout; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import React from 'react'; | ||
import { IntlProvider } from '@edx/frontend-platform/i18n'; | ||
import { initializeMockApp } from '@edx/frontend-platform'; | ||
import { AppProvider } from '@edx/frontend-platform/react'; | ||
import { render } from '@testing-library/react'; | ||
|
||
import initializeStore from '../store'; | ||
import TaxonomyLayout from './TaxonomyLayout'; | ||
|
||
let store; | ||
|
||
jest.mock('../header', () => jest.fn(() => <div data-testid="mock-header" />)); | ||
jest.mock('@edx/frontend-component-footer', () => ({ | ||
StudioFooter: jest.fn(() => <div data-testid="mock-footer" />), | ||
})); | ||
jest.mock('react-router-dom', () => ({ | ||
...jest.requireActual('react-router-dom'), | ||
Outlet: jest.fn(() => <div data-testid="mock-content" />), | ||
})); | ||
|
||
const RootWrapper = () => ( | ||
<AppProvider store={store}> | ||
<IntlProvider locale="en" messages={{}}> | ||
<TaxonomyLayout /> | ||
</IntlProvider> | ||
</AppProvider> | ||
); | ||
|
||
describe('<TaxonomyLayout />', async () => { | ||
beforeEach(async () => { | ||
initializeMockApp({ | ||
authenticatedUser: { | ||
userId: 3, | ||
username: 'abc123', | ||
administrator: true, | ||
roles: [], | ||
}, | ||
}); | ||
store = initializeStore(); | ||
}); | ||
|
||
it('should render page correctly', async () => { | ||
const { getByTestId } = render(<RootWrapper />); | ||
expect(getByTestId('mock-header')).toBeInTheDocument(); | ||
expect(getByTestId('mock-content')).toBeInTheDocument(); | ||
expect(getByTestId('mock-footer')).toBeInTheDocument(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
// eslint-disable-next-line import/prefer-default-export | ||
export { default as TaxonomyListPage } from './TaxonomyListPage'; | ||
export { default as TaxonomyLayout } from './TaxonomyLayout'; | ||
export { TaxonomyDetailPage } from './taxonomy-detail'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
// ts-check | ||
import { useIntl } from '@edx/frontend-platform/i18n'; | ||
import { | ||
DataTable, | ||
} from '@edx/paragon'; | ||
import _ from 'lodash'; | ||
import Proptypes from 'prop-types'; | ||
import { useState } from 'react'; | ||
|
||
import messages from './messages'; | ||
import { useTagListDataResponse, useTagListDataStatus } from './data/apiHooks'; | ||
|
||
const TagListTable = ({ taxonomyId }) => { | ||
const intl = useIntl(); | ||
const [options, setOptions] = useState({ | ||
pageIndex: 0, | ||
}); | ||
const { isLoading } = useTagListDataStatus(taxonomyId, options); | ||
const tagList = useTagListDataResponse(taxonomyId, options); | ||
|
||
const fetchData = (args) => { | ||
if (!_.isEqual(args, options)) { | ||
setOptions({ ...args }); | ||
} | ||
}; | ||
|
||
return ( | ||
<DataTable | ||
isLoading={isLoading} | ||
isPaginated | ||
manualPagination | ||
fetchData={fetchData} | ||
data={tagList?.results || []} | ||
itemCount={tagList?.count || 0} | ||
pageCount={tagList?.numPages || 0} | ||
initialState={options} | ||
columns={[ | ||
{ | ||
Header: intl.formatMessage(messages.tagListColumnValueHeader), | ||
accessor: 'value', | ||
}, | ||
]} | ||
> | ||
<DataTable.TableControlBar /> | ||
<DataTable.Table /> | ||
<DataTable.EmptyTable content={intl.formatMessage(messages.noResultsFoundMessage)} /> | ||
<DataTable.TableFooter /> | ||
</DataTable> | ||
); | ||
}; | ||
|
||
TagListTable.propTypes = { | ||
taxonomyId: Proptypes.string.isRequired, | ||
}; | ||
|
||
export default TagListTable; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import React from 'react'; | ||
import { IntlProvider } from '@edx/frontend-platform/i18n'; | ||
import { initializeMockApp } from '@edx/frontend-platform'; | ||
import { AppProvider } from '@edx/frontend-platform/react'; | ||
import { render } from '@testing-library/react'; | ||
|
||
import { useTagListData } from './data/api'; | ||
import initializeStore from '../../store'; | ||
import TagListTable from './TagListTable'; | ||
|
||
let store; | ||
|
||
jest.mock('./data/api', () => ({ | ||
useTagListData: jest.fn(), | ||
})); | ||
|
||
const RootWrapper = () => ( | ||
<AppProvider store={store}> | ||
<IntlProvider locale="en" messages={{}}> | ||
<TagListTable taxonomyId="1" /> | ||
</IntlProvider> | ||
</AppProvider> | ||
); | ||
|
||
describe('<TagListPage />', async () => { | ||
beforeEach(async () => { | ||
initializeMockApp({ | ||
authenticatedUser: { | ||
userId: 3, | ||
username: 'abc123', | ||
administrator: true, | ||
roles: [], | ||
}, | ||
}); | ||
store = initializeStore(); | ||
}); | ||
|
||
it('shows the spinner before the query is complete', async () => { | ||
useTagListData.mockReturnValue({ | ||
isLoading: true, | ||
isFetched: false, | ||
}); | ||
const { getByRole } = render(<RootWrapper />); | ||
const spinner = getByRole('status'); | ||
expect(spinner.textContent).toEqual('loading'); | ||
}); | ||
|
||
it('should render page correctly', async () => { | ||
useTagListData.mockReturnValue({ | ||
isSuccess: true, | ||
isFetched: true, | ||
isError: false, | ||
data: { | ||
count: 3, | ||
numPages: 1, | ||
results: [ | ||
{ value: 'Tag 1' }, | ||
{ value: 'Tag 2' }, | ||
{ value: 'Tag 3' }, | ||
], | ||
}, | ||
}); | ||
const { getAllByRole } = render(<RootWrapper />); | ||
const rows = getAllByRole('row'); | ||
expect(rows.length).toBe(3 + 1); // 3 items plus header | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// @ts-check | ||
import { useQuery } from '@tanstack/react-query'; | ||
import { camelCaseObject, getConfig } from '@edx/frontend-platform'; | ||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; | ||
|
||
const getApiBaseUrl = () => getConfig().STUDIO_BASE_URL; | ||
const getTagListApiUrl = (taxonomyId, page) => new URL( | ||
`api/content_tagging/v1/taxonomies/${taxonomyId}/tags/?page=${page + 1}`, | ||
getApiBaseUrl(), | ||
).href; | ||
|
||
// ToDo: fix types | ||
/** | ||
* @param {number} taxonomyId | ||
* @param {import('./types.mjs').QueryOptions} options | ||
* @returns {import('@tanstack/react-query').UseQueryResult<import('./types.mjs').TagListData>} | ||
*/ // eslint-disable-next-line import/prefer-default-export | ||
export const useTagListData = (taxonomyId, options) => { | ||
const { pageIndex } = options; | ||
return useQuery({ | ||
queryKey: ['tagList', taxonomyId, pageIndex], | ||
queryFn: async () => { | ||
const { data } = await getAuthenticatedHttpClient().get(getTagListApiUrl(taxonomyId, pageIndex)); | ||
return camelCaseObject(data); | ||
}, | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { useQuery } from '@tanstack/react-query'; | ||
import { | ||
useTagListData, | ||
} from './api'; | ||
|
||
const mockHttpClient = { | ||
get: jest.fn(), | ||
}; | ||
|
||
jest.mock('@tanstack/react-query', () => ({ | ||
useQuery: jest.fn(), | ||
})); | ||
|
||
jest.mock('@edx/frontend-platform/auth', () => ({ | ||
getAuthenticatedHttpClient: jest.fn(() => mockHttpClient), | ||
})); | ||
|
||
describe('useTagListData', () => { | ||
it('should call useQuery with the correct parameters', () => { | ||
useTagListData('1', { pageIndex: 3 }); | ||
|
||
expect(useQuery).toHaveBeenCalledWith({ | ||
queryKey: ['tagList', '1', 3], | ||
queryFn: expect.any(Function), | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// @ts-check | ||
import { | ||
useTagListData, | ||
} from './api'; | ||
|
||
/* eslint-disable max-len */ | ||
/** | ||
* @param {number} taxonomyId | ||
* @param {import("./types.mjs").QueryOptions} options | ||
* @returns {Pick<import('@tanstack/react-query').UseQueryResult, "error" | "isError" | "isFetched" | "isLoading" | "isSuccess" >} | ||
*/ /* eslint-enable max-len */ | ||
export const useTagListDataStatus = (taxonomyId, options) => { | ||
const { | ||
error, | ||
isError, | ||
isFetched, | ||
isLoading, | ||
isSuccess, | ||
} = useTagListData(taxonomyId, options); | ||
return { | ||
error, | ||
isError, | ||
isFetched, | ||
isLoading, | ||
isSuccess, | ||
}; | ||
}; | ||
|
||
/** | ||
* @param {number} taxonomyId | ||
* @param {import("./types.mjs").QueryOptions} options | ||
* @returns {import("./types.mjs").TagListData | undefined} | ||
*/ | ||
export const useTagListDataResponse = (taxonomyId, options) => { | ||
const { isSuccess, data } = useTagListData(taxonomyId, options); | ||
if (isSuccess) { | ||
return data; | ||
} | ||
|
||
return undefined; | ||
}; |
Oops, something went wrong.