refactor: Convert more Taxonomy code to TypeScript (#1532)
* Converts some files from .js or .mjs to .ts * Moves the API code from src/taxonomy/tag-list/data into src/taxonomy/data * Cleans up and improves some type definitions * No user-visible changes / functionality changes.
This commit is contained in:
@@ -1,15 +0,0 @@
|
||||
// @ts-check
|
||||
import React from 'react';
|
||||
|
||||
/**
|
||||
* @typedef AlertProps
|
||||
* @type {Object}
|
||||
* @property {React.ReactNode} title - title of the alert.
|
||||
* @property {React.ReactNode} description - description of the alert.
|
||||
*/
|
||||
export const TaxonomyContext = React.createContext({
|
||||
toastMessage: /** @type{null|string} */ (null),
|
||||
setToastMessage: /** @type{null|React.Dispatch<React.SetStateAction<null|string>>} */ (null),
|
||||
alertProps: /** @type{null|AlertProps} */ (null),
|
||||
setAlertProps: /** @type{null|React.Dispatch<React.SetStateAction<null|AlertProps>>} */ (null),
|
||||
});
|
||||
22
src/taxonomy/common/context.ts
Normal file
22
src/taxonomy/common/context.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
|
||||
export interface AlertProps {
|
||||
/** title of the alert */
|
||||
title: React.ReactNode;
|
||||
/** description of the alert */
|
||||
description: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface TaxonomyContextData {
|
||||
toastMessage: null | string;
|
||||
setToastMessage: null | React.Dispatch<React.SetStateAction<null | string>>;
|
||||
alertProps: null | AlertProps;
|
||||
setAlertProps: null | React.Dispatch<React.SetStateAction<null | AlertProps>>;
|
||||
}
|
||||
|
||||
export const TaxonomyContext = React.createContext<TaxonomyContextData>({
|
||||
toastMessage: null,
|
||||
setToastMessage: null,
|
||||
alertProps: null,
|
||||
setAlertProps: null,
|
||||
});
|
||||
@@ -88,7 +88,7 @@ export const apiUrls = {
|
||||
/**
|
||||
* Get list of taxonomies.
|
||||
* @param {string} [org] Filter the list to only show taxonomies assigned to this org
|
||||
* @returns {Promise<import("./types.mjs").TaxonomyListData>}
|
||||
* @returns {Promise<import("./types.js").TaxonomyListData>}
|
||||
*/
|
||||
export async function getTaxonomyListData(org) {
|
||||
const { data } = await getAuthenticatedHttpClient().get(apiUrls.taxonomyList(org));
|
||||
@@ -107,7 +107,7 @@ export async function deleteTaxonomy(taxonomyId) {
|
||||
/**
|
||||
* Get metadata about a Taxonomy
|
||||
* @param {number} taxonomyId The ID of the taxonomy to get
|
||||
* @returns {Promise<import("./types.mjs").TaxonomyData>}
|
||||
* @returns {Promise<import("./types.js").TaxonomyData>}
|
||||
*/
|
||||
export async function getTaxonomy(taxonomyId) {
|
||||
const { data } = await getAuthenticatedHttpClient().get(apiUrls.taxonomy(taxonomyId));
|
||||
|
||||
@@ -109,7 +109,7 @@ export const useImportNewTaxonomy = () => {
|
||||
return useMutation({
|
||||
/**
|
||||
* @type {import("@tanstack/react-query").MutateFunction<
|
||||
* import("./types.mjs").TaxonomyData,
|
||||
* import("./types.js").TaxonomyData,
|
||||
* any,
|
||||
* {
|
||||
* name: string,
|
||||
@@ -147,7 +147,7 @@ export const useImportTags = () => {
|
||||
return useMutation({
|
||||
/**
|
||||
* @type {import("@tanstack/react-query").MutateFunction<
|
||||
* import("./types.mjs").TaxonomyData,
|
||||
* import("./types.js").TaxonomyData,
|
||||
* any,
|
||||
* {
|
||||
* taxonomyId: number,
|
||||
@@ -202,3 +202,35 @@ export const useImportPlan = (taxonomyId, file) => useQuery({
|
||||
},
|
||||
retry: false, // If there's an error, it's probably a real problem with the file. Don't try again several times!
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {number} taxonomyId
|
||||
* @param {import('./types.js').QueryOptions} options
|
||||
* @returns {import('@tanstack/react-query').UseQueryResult<import('./types.js').TagListData>}
|
||||
*/
|
||||
export const useTagListData = (taxonomyId, options) => {
|
||||
const { pageIndex, pageSize } = options;
|
||||
return useQuery({
|
||||
queryKey: taxonomyQueryKeys.taxonomyTagListPage(taxonomyId, pageIndex, pageSize),
|
||||
queryFn: async () => {
|
||||
const { data } = await getAuthenticatedHttpClient().get(apiUrls.tagList(taxonomyId, pageIndex, pageSize));
|
||||
return camelCaseObject(data);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Temporary hook to load *all* the subtags of a given tag in a taxonomy.
|
||||
* Doesn't handle pagination or anything. This is meant to be replaced by
|
||||
* something more sophisticated later, as we improve the "taxonomy details" page.
|
||||
* @param {number} taxonomyId
|
||||
* @param {string} parentTagValue
|
||||
* @returns {import('@tanstack/react-query').UseQueryResult<import('./types.js').TagListData>}
|
||||
*/
|
||||
export const useSubTags = (taxonomyId, parentTagValue) => useQuery({
|
||||
queryKey: taxonomyQueryKeys.taxonomyTagSubtagsList(taxonomyId, parentTagValue),
|
||||
queryFn: async () => {
|
||||
const response = await getAuthenticatedHttpClient().get(apiUrls.allSubtagsOf(taxonomyId, parentTagValue));
|
||||
return camelCaseObject(response.data);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @typedef {Object} TaxonomyData Metadata about a taxonomy
|
||||
* @property {number} id
|
||||
* @property {string} name
|
||||
* @property {string} description
|
||||
* @property {string} exportId
|
||||
* @property {boolean} enabled
|
||||
* @property {boolean} allowMultiple
|
||||
* @property {boolean} allowFreeText
|
||||
* @property {boolean} systemDefined
|
||||
* @property {boolean} visibleToAuthors
|
||||
* @property {number} tagsCount
|
||||
* @property {string[]} orgs
|
||||
* @property {boolean} allOrgs
|
||||
* @property {boolean} canChangeTaxonomy
|
||||
* @property {boolean} canDeleteTaxonomy
|
||||
* @property {boolean} canTagObject
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} TaxonomyListData The list of taxonomies
|
||||
* @property {string} next
|
||||
* @property {string} previous
|
||||
* @property {number} count
|
||||
* @property {number} numPages
|
||||
* @property {number} currentPage
|
||||
* @property {number} start
|
||||
* @property {boolean} canAddTaxonomy
|
||||
* @property {TaxonomyData[]} results
|
||||
*/
|
||||
60
src/taxonomy/data/types.ts
Normal file
60
src/taxonomy/data/types.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
/** Metadata about a taxonomy */
|
||||
export interface TaxonomyData {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
exportId: string;
|
||||
enabled: boolean;
|
||||
allowMultiple: boolean;
|
||||
allowFreeText: boolean;
|
||||
systemDefined: boolean;
|
||||
visibleToAuthors: boolean;
|
||||
tagsCount: number;
|
||||
orgs: string[];
|
||||
allOrgs: boolean;
|
||||
canChangeTaxonomy: boolean;
|
||||
canDeleteTaxonomy: boolean;
|
||||
canTagObject: boolean;
|
||||
}
|
||||
|
||||
/** The list of taxonomies */
|
||||
export interface TaxonomyListData {
|
||||
next: string;
|
||||
previous: string;
|
||||
count: number;
|
||||
numPages: number;
|
||||
currentPage: number;
|
||||
start: number;
|
||||
canAddTaxonomy: boolean;
|
||||
results: TaxonomyData[];
|
||||
}
|
||||
|
||||
export interface QueryOptions {
|
||||
pageIndex: number;
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
export interface TagData {
|
||||
childCount: number;
|
||||
descendantCount: number;
|
||||
depth: number;
|
||||
externalId: string;
|
||||
id: number;
|
||||
parentValue: string | null;
|
||||
subTagsUrl: string | null;
|
||||
/** Unique ID for this tag, also its display text */
|
||||
value: string;
|
||||
usageCount?: number;
|
||||
/** Database ID. Don't rely on this, as it is not present for free-text tags. */
|
||||
_id?: string;
|
||||
}
|
||||
|
||||
export interface TagListData {
|
||||
count: number;
|
||||
currentPage: number;
|
||||
next: string;
|
||||
numPages: number;
|
||||
previous: string;
|
||||
results: TagData[];
|
||||
start: number;
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import Proptypes from 'prop-types';
|
||||
|
||||
import { LoadingSpinner } from '../../generic/Loading';
|
||||
import messages from './messages';
|
||||
import { useTagListData, useSubTags } from './data/apiHooks';
|
||||
import { useTagListData, useSubTags } from '../data/apiHooks';
|
||||
|
||||
const SubTagsExpanded = ({ taxonomyId, parentTagValue }) => {
|
||||
const subTagsData = useSubTags(taxonomyId, parentTagValue);
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
// @ts-check
|
||||
|
||||
// TODO: this file needs to be merged into src/taxonomy/data/apiHooks.js
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { camelCaseObject } from '@edx/frontend-platform';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { apiUrls } from '../../data/api';
|
||||
import { taxonomyQueryKeys } from '../../data/apiHooks';
|
||||
|
||||
/**
|
||||
* @param {number} taxonomyId
|
||||
* @param {import('./types.mjs').QueryOptions} options
|
||||
* @returns {import('@tanstack/react-query').UseQueryResult<import('./types.mjs').TagListData>}
|
||||
*/
|
||||
export const useTagListData = (taxonomyId, options) => {
|
||||
const { pageIndex, pageSize } = options;
|
||||
return useQuery({
|
||||
queryKey: taxonomyQueryKeys.taxonomyTagListPage(taxonomyId, pageIndex, pageSize),
|
||||
queryFn: async () => {
|
||||
const { data } = await getAuthenticatedHttpClient().get(apiUrls.tagList(taxonomyId, pageIndex, pageSize));
|
||||
return camelCaseObject(data);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Temporary hook to load *all* the subtags of a given tag in a taxonomy.
|
||||
* Doesn't handle pagination or anything. This is meant to be replaced by
|
||||
* something more sophisticated later, as we improve the "taxonomy details" page.
|
||||
* @param {number} taxonomyId
|
||||
* @param {string} parentTagValue
|
||||
* @returns {import('@tanstack/react-query').UseQueryResult<import('./types.mjs').TagListData>}
|
||||
*/
|
||||
export const useSubTags = (taxonomyId, parentTagValue) => useQuery({
|
||||
queryKey: taxonomyQueryKeys.taxonomyTagSubtagsList(taxonomyId, parentTagValue),
|
||||
queryFn: async () => {
|
||||
const response = await getAuthenticatedHttpClient().get(apiUrls.allSubtagsOf(taxonomyId, parentTagValue));
|
||||
return camelCaseObject(response.data);
|
||||
},
|
||||
});
|
||||
@@ -1,36 +0,0 @@
|
||||
// @ts-check
|
||||
|
||||
// TODO: this file needs to be merged into src/taxonomy/data/types.mjs
|
||||
// We are creating a mess with so many different /data/[api|types].js files in subfolders.
|
||||
// There is only one tagging/taxonomy API, and it should be implemented via a single types.mjs and api.js file.
|
||||
|
||||
/**
|
||||
* @typedef {Object} QueryOptions
|
||||
* @property {number} pageIndex
|
||||
* @property {number} pageSize
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} TagData
|
||||
* @property {number} childCount
|
||||
* @property {number} descendantCount
|
||||
* @property {number} depth
|
||||
* @property {string} externalId
|
||||
* @property {number} id
|
||||
* @property {string | null} parentValue
|
||||
* @property {string | null} subTagsUrl
|
||||
* @property {string} value Unique ID for this tag, also its display text
|
||||
* @property {number?} usageCount
|
||||
* @property {string?} _id Database ID. Don't rely on this, as it is not present for free-text tags.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} TagListData
|
||||
* @property {number} count
|
||||
* @property {number} currentPage
|
||||
* @property {string} next
|
||||
* @property {number} numPages
|
||||
* @property {string} previous
|
||||
* @property {TagData[]} results
|
||||
* @property {number} start
|
||||
*/
|
||||
@@ -21,7 +21,7 @@ import { ImportTagsWizard } from '../import-tags';
|
||||
import { ManageOrgsModal } from '../manage-orgs';
|
||||
import messages from './messages';
|
||||
|
||||
/** @typedef {import('../data/types.mjs').TaxonomyData} TaxonomyData */
|
||||
/** @typedef {import('../data/types.js').TaxonomyData} TaxonomyData */
|
||||
// Note: to make mocking easier for tests, the types below only specify the subset of TaxonomyData that we actually use.
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user