From 1e3aa3e90f7162a55ed54362b8d452624b1c46b5 Mon Sep 17 00:00:00 2001 From: Braden MacDonald Date: Fri, 17 Nov 2023 15:17:18 -0800 Subject: [PATCH] feat: display all child tags in the "bare bones" taxonomy detail page --- src/generic/Loading.jsx | 31 ++++++------ src/taxonomy/tag-list/TagListTable.jsx | 47 +++++++++++++++++-- src/taxonomy/tag-list/data/api.js | 18 +++++++ src/taxonomy/tag-list/messages.js | 8 ++++ .../taxonomy-detail/TaxonomyDetailPage.jsx | 3 +- 5 files changed, 87 insertions(+), 20 deletions(-) diff --git a/src/generic/Loading.jsx b/src/generic/Loading.jsx index 7f109a245c..4a2df03cfe 100644 --- a/src/generic/Loading.jsx +++ b/src/generic/Loading.jsx @@ -2,6 +2,22 @@ import React from 'react'; import { Spinner } from '@edx/paragon'; import { FormattedMessage } from '@edx/frontend-platform/i18n'; + +export const LoadingSpinner = () => ( + + )} + /> +); + const Loading = () => (
( height: '100vh', }} > - - - - )} - /> +
); diff --git a/src/taxonomy/tag-list/TagListTable.jsx b/src/taxonomy/tag-list/TagListTable.jsx index c131beb6ae..0cd4182264 100644 --- a/src/taxonomy/tag-list/TagListTable.jsx +++ b/src/taxonomy/tag-list/TagListTable.jsx @@ -1,14 +1,38 @@ // ts-check -import { useIntl } from '@edx/frontend-platform/i18n'; -import { - DataTable, -} from '@edx/paragon'; +import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n'; +import { DataTable } from '@edx/paragon'; import _ from 'lodash'; import Proptypes from 'prop-types'; import { useState } from 'react'; +import { LoadingSpinner } from '../../generic/Loading'; import messages from './messages'; import { useTagListDataResponse, useTagListDataStatus } from './data/apiHooks'; +import { useSubTags } from './data/api'; + +const SubTagsExpanded = ({ taxonomyId, parentTagValue }) => { + + const subTagsData = useSubTags(taxonomyId, parentTagValue); + + if (subTagsData.isLoading) { + return ; + } else if (subTagsData.isError) { + return + } + + return
    + {subTagsData.data.results.map(tagData => +
  • + {tagData.value} {tagData.childCount > 0 ? `(${tagData.childCount})` : null} +
  • + )} +
; +}; + +SubTagsExpanded.propTypes = { + taxonomyId: Proptypes.number.isRequired, + parentTagValue: Proptypes.string.isRequired, +}; const TagListTable = ({ taxonomyId }) => { const intl = useIntl(); @@ -46,11 +70,24 @@ const TagListTable = ({ taxonomyId }) => { itemCount={tagList?.count || 0} pageCount={tagList?.numPages || 0} initialState={options} + isExpandable + // This is a temporary "bare bones" solution for brute-force loading all the child tags. In future we'll match + // the Figma design and do something more sophisticated. + renderRowSubComponent={({ row }) => } columns={[ { Header: intl.formatMessage(messages.tagListColumnValueHeader), accessor: 'value', }, + { + id: 'expander', + Header: DataTable.ExpandAll, + Cell: ({row}) => row.values.childCount > 0 ? : null, + }, + { + Header: intl.formatMessage(messages.tagListColumnChildCountHeader), + accessor: 'childCount', + }, ]} > @@ -62,7 +99,7 @@ const TagListTable = ({ taxonomyId }) => { }; TagListTable.propTypes = { - taxonomyId: Proptypes.string.isRequired, + taxonomyId: Proptypes.number.isRequired, }; export default TagListTable; diff --git a/src/taxonomy/tag-list/data/api.js b/src/taxonomy/tag-list/data/api.js index 456cb0020f..fa2a79f099 100644 --- a/src/taxonomy/tag-list/data/api.js +++ b/src/taxonomy/tag-list/data/api.js @@ -24,3 +24,21 @@ export const useTagListData = (taxonomyId, options) => { .then(camelCaseObject), }); }; + +/** + * @param {number} taxonomyId + * @param {string} parentTagValue + * @returns {import('@tanstack/react-query').UseQueryResult} + */ +export const useSubTags = (taxonomyId, parentTagValue) => { + return useQuery({ + queryKey: ['subtagsList', taxonomyId, parentTagValue], + queryFn: async () => { + const url = new URL(`api/content_tagging/v1/taxonomies/${taxonomyId}/tags/`, getApiBaseUrl()); + url.searchParams.set("full_depth_threshold", "10000"); // Load as deeply as we can + url.searchParams.set("parent_tag", parentTagValue); + const response = await getAuthenticatedHttpClient().get(url.href); + return camelCaseObject(response.data); + }, + }); +}; diff --git a/src/taxonomy/tag-list/messages.js b/src/taxonomy/tag-list/messages.js index 5832fdb465..82db9caba7 100644 --- a/src/taxonomy/tag-list/messages.js +++ b/src/taxonomy/tag-list/messages.js @@ -9,6 +9,14 @@ const messages = defineMessages({ id: 'course-authoring.tag-list.column.value.header', defaultMessage: 'Value', }, + tagListColumnChildCountHeader: { + id: 'course-authoring.tag-list.column.value.header', + defaultMessage: '# child tags', + }, + tagListError: { + id: 'course-authoring.tag-list.error', + defaultMessage: 'Error: unable to load child tags', + }, }); export default messages; diff --git a/src/taxonomy/taxonomy-detail/TaxonomyDetailPage.jsx b/src/taxonomy/taxonomy-detail/TaxonomyDetailPage.jsx index 4c470c96ad..cdeb36074f 100644 --- a/src/taxonomy/taxonomy-detail/TaxonomyDetailPage.jsx +++ b/src/taxonomy/taxonomy-detail/TaxonomyDetailPage.jsx @@ -20,7 +20,8 @@ import { useTaxonomyDetailDataResponse, useTaxonomyDetailDataStatus } from './da const TaxonomyDetailPage = () => { const intl = useIntl(); - const { taxonomyId } = useParams(); + const { taxonomyId: taxonomyIdString } = useParams(); + const taxonomyId = Number(taxonomyIdString); const useTaxonomyDetailData = () => { const { isError, isFetched } = useTaxonomyDetailDataStatus(taxonomyId);