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);