diff --git a/src/taxonomy/TaxonomyLayout.test.jsx b/src/taxonomy/TaxonomyLayout.test.tsx
similarity index 68%
rename from src/taxonomy/TaxonomyLayout.test.jsx
rename to src/taxonomy/TaxonomyLayout.test.tsx
index dcea6c2c3c..09f2398885 100644
--- a/src/taxonomy/TaxonomyLayout.test.jsx
+++ b/src/taxonomy/TaxonomyLayout.test.tsx
@@ -1,14 +1,9 @@
import React, { useContext } 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 { initializeMocks, render } from '../testUtils';
import { TaxonomyContext } from './common/context';
-import TaxonomyLayout from './TaxonomyLayout';
+import { TaxonomyLayout } from './TaxonomyLayout';
-let store;
const toastMessage = 'Hello, this is a toast!';
const alertErrorTitle = 'Error title';
const alertErrorDescription = 'Error description';
@@ -20,14 +15,14 @@ const MockChildComponent = () => {
@@ -93,6 +91,11 @@ const OrganizationFilterSelector = ({
organizationListData,
selectedOrgFilter,
setSelectedOrgFilter,
+}: {
+ isOrganizationListLoaded: boolean;
+ organizationListData?: string[];
+ selectedOrgFilter: string;
+ setSelectedOrgFilter: (org: string) => void,
}) => {
const intl = useIntl();
const isOrgSelected = (value) => (value === selectedOrgFilter ?
: null);
@@ -152,9 +155,9 @@ const OrganizationFilterSelector = ({
);
};
-const TaxonomyListPage = () => {
+export const TaxonomyListPage = () => {
const intl = useIntl();
- const [selectedOrgFilter, setSelectedOrgFilter] = useState(ALL_TAXONOMIES);
+ const [selectedOrgFilter, setSelectedOrgFilter] = useState
(ALL_TAXONOMIES);
const {
data: organizationListData,
@@ -242,22 +245,3 @@ const TaxonomyListPage = () => {
>
);
};
-
-TaxonomyListHeaderButtons.propTypes = {
- canAddTaxonomy: PropTypes.bool.isRequired,
-};
-
-OrganizationFilterSelector.propTypes = {
- isOrganizationListLoaded: PropTypes.bool.isRequired,
- organizationListData: PropTypes.arrayOf(PropTypes.string),
- selectedOrgFilter: PropTypes.string.isRequired,
- setSelectedOrgFilter: PropTypes.func.isRequired,
-};
-
-OrganizationFilterSelector.defaultProps = {
- organizationListData: null,
-};
-
-TaxonomyListPage.propTypes = {};
-
-export default TaxonomyListPage;
diff --git a/src/taxonomy/data/api.test.js b/src/taxonomy/data/api.test.ts
similarity index 75%
rename from src/taxonomy/data/api.test.js
rename to src/taxonomy/data/api.test.ts
index b287df0156..ed6ef8cdce 100644
--- a/src/taxonomy/data/api.test.js
+++ b/src/taxonomy/data/api.test.ts
@@ -1,8 +1,4 @@
-// @ts-check
-import MockAdapter from 'axios-mock-adapter';
-import { initializeMockApp } from '@edx/frontend-platform';
-import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
-
+import { initializeMocks } from '../../testUtils';
import { taxonomyListMock } from '../__mocks__';
import {
@@ -13,32 +9,14 @@ import {
deleteTaxonomy,
} from './api';
-let axiosMock;
-
describe('taxonomy api calls', () => {
- beforeEach(() => {
- initializeMockApp({
- authenticatedUser: {
- userId: 3,
- username: 'abc123',
- administrator: true,
- roles: [],
- },
- });
-
- axiosMock = new MockAdapter(getAuthenticatedHttpClient());
- });
-
- afterEach(() => {
- jest.clearAllMocks();
- });
-
it.each([
undefined,
'All taxonomies',
'Unassigned',
'testOrg',
])('should get taxonomy list data for \'%s\' org filter', async (org) => {
+ const { axiosMock } = initializeMocks();
axiosMock.onGet(apiUrls.taxonomyList(org)).reply(200, taxonomyListMock);
const result = await getTaxonomyListData(org);
@@ -47,6 +25,7 @@ describe('taxonomy api calls', () => {
});
it('should delete a taxonomy', async () => {
+ const { axiosMock } = initializeMocks();
const taxonomyId = 123;
axiosMock.onDelete(apiUrls.taxonomy(taxonomyId)).reply(200);
await deleteTaxonomy(taxonomyId);
@@ -55,6 +34,7 @@ describe('taxonomy api calls', () => {
});
it('should call get taxonomy', async () => {
+ const { axiosMock } = initializeMocks();
axiosMock.onGet(apiUrls.taxonomy(1)).reply(200);
await getTaxonomy(1);
@@ -62,6 +42,7 @@ describe('taxonomy api calls', () => {
});
it('Export should set window.location.href correctly', () => {
+ initializeMocks();
const origLocation = window.location;
// @ts-ignore
delete window.location;
diff --git a/src/taxonomy/data/api.js b/src/taxonomy/data/api.ts
similarity index 51%
rename from src/taxonomy/data/api.js
rename to src/taxonomy/data/api.ts
index a36cf6d513..60ad85b5c7 100644
--- a/src/taxonomy/data/api.js
+++ b/src/taxonomy/data/api.ts
@@ -1,15 +1,15 @@
-// @ts-check
import { camelCaseObject, getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
+import type { TaxonomyData, TaxonomyListData } from './types';
const getApiBaseUrl = () => getConfig().STUDIO_BASE_URL;
const getTaxonomiesV1Endpoint = () => new URL('api/content_tagging/v1/taxonomies/', getApiBaseUrl()).href;
/**
* Helper method for creating URLs for the tagging/taxonomy API. Used only in this file.
- * @param {string} path The subpath within the taxonomies "v1" REST API namespace
- * @param {Record} [searchParams] Query parameters to include
+ * @param path The subpath within the taxonomies "v1" REST API namespace
+ * @param searchParams Query parameters to include
*/
-const makeUrl = (path, searchParams) => {
+const makeUrl = (path: string, searchParams?: Record): string => {
const url = new URL(path, getTaxonomiesV1Endpoint());
if (searchParams) {
Object.entries(searchParams).forEach(([k, v]) => url.searchParams.append(k, String(v)));
@@ -20,14 +20,13 @@ const makeUrl = (path, searchParams) => {
export const ALL_TAXONOMIES = '__all';
export const UNASSIGNED = '__unassigned';
-/** @satisfies {Record string>} */
export const apiUrls = {
/**
* Get the URL of the "list all taxonomies" endpoint
- * @param {string} [org] Optionally, Filter the list to only show taxonomies assigned to this org
+ * @param org Optionally, Filter the list to only show taxonomies assigned to this org
*/
- taxonomyList(org) {
- const params = {};
+ taxonomyList(org?: string) {
+ const params: Record = {};
if (org !== undefined) {
if (org === UNASSIGNED) {
params.unassigned = 'true';
@@ -39,87 +38,74 @@ export const apiUrls = {
},
/**
* Get the URL of the API endpoint to download a taxonomy as a CSV/JSON file.
- * @param {number} taxonomyId The ID of the taxonomy
- * @param {'json'|'csv'} format Which format to use for the export
+ * @param taxonomyId The ID of the taxonomy
+ * @param format Which format to use for the export
*/
- exportTaxonomy: (taxonomyId, format) => makeUrl(`${taxonomyId}/export/`, { output_format: format, download: 1 }),
+ exportTaxonomy: (taxonomyId: number, format: 'json' | 'csv') => (
+ makeUrl(`${taxonomyId}/export/`, { output_format: format, download: 1 })
+ ),
/**
* The the URL of the downloadable template file that shows how to format a
* taxonomy file.
- * @param {'json'|'csv'} format The format requested
+ * @param format The format requested
*/
- taxonomyTemplate: (format) => makeUrl(`import/template.${format}`),
- /**
- * Get the URL for a Taxonomy
- * @param {number} taxonomyId The ID of the taxonomy
- */
- taxonomy: (taxonomyId) => makeUrl(`${taxonomyId}/`),
+ taxonomyTemplate: (format: 'json' | 'csv') => makeUrl(`import/template.${format}`),
+ /** Get the URL for a Taxonomy */
+ taxonomy: (taxonomyId: number) => makeUrl(`${taxonomyId}/`),
/**
* Get the URL for listing the tags of a taxonomy
- * @param {number} taxonomyId
- * @param {number} pageIndex Zero-indexed page number
- * @param {*} pageSize How many tags per page to load
+ * @param pageIndex Zero-indexed page number
+ * @param pageSize How many tags per page to load
*/
- tagList: (taxonomyId, pageIndex, pageSize) => makeUrl(`${taxonomyId}/tags/`, {
+ tagList: (taxonomyId: number, pageIndex: number, pageSize: number) => makeUrl(`${taxonomyId}/tags/`, {
page: (pageIndex + 1), page_size: pageSize,
}),
/**
* Get _all_ tags below a given parent tag. This may be replaced with something more scalable in the future.
- * @param {number} taxonomyId
- * @param {string} parentTagValue
*/
- allSubtagsOf: (taxonomyId, parentTagValue) => makeUrl(`${taxonomyId}/tags/`, {
+ allSubtagsOf: (taxonomyId: number, parentTagValue: string) => makeUrl(`${taxonomyId}/tags/`, {
// Load as deeply as we can
full_depth_threshold: 10000,
parent_tag: parentTagValue,
}),
/** URL to create a new taxonomy from an import file. */
createTaxonomyFromImport: () => makeUrl('import/'),
- /**
- * @param {number} taxonomyId
- */
+ /** URL to import tags into an existing taxonomy */
tagsImport: (taxonomyId) => makeUrl(`${taxonomyId}/tags/import/`),
- /**
- * @param {number} taxonomyId
- */
- tagsPlanImport: (taxonomyId) => makeUrl(`${taxonomyId}/tags/import/plan/`),
-};
+ /** URL to plan (preview what would happen) a taxonomy import */
+ tagsPlanImport: (taxonomyId: number) => makeUrl(`${taxonomyId}/tags/import/plan/`),
+} satisfies Record string>;
/**
* Get list of taxonomies.
- * @param {string} [org] Filter the list to only show taxonomies assigned to this org
- * @returns {Promise}
+ * @param org Optionally, filter the list to only show taxonomies assigned to this org
*/
-export async function getTaxonomyListData(org) {
+export async function getTaxonomyListData(org?: string): Promise {
const { data } = await getAuthenticatedHttpClient().get(apiUrls.taxonomyList(org));
return camelCaseObject(data);
}
/**
* Delete a Taxonomy
- * @param {number} taxonomyId
- * @returns {Promise}
*/
-export async function deleteTaxonomy(taxonomyId) {
+export async function deleteTaxonomy(taxonomyId: number): Promise {
await getAuthenticatedHttpClient().delete(apiUrls.taxonomy(taxonomyId));
}
/**
* Get metadata about a Taxonomy
- * @param {number} taxonomyId The ID of the taxonomy to get
- * @returns {Promise}
+ * @param taxonomyId The ID of the taxonomy to get
*/
-export async function getTaxonomy(taxonomyId) {
+export async function getTaxonomy(taxonomyId: number): Promise {
const { data } = await getAuthenticatedHttpClient().get(apiUrls.taxonomy(taxonomyId));
return camelCaseObject(data);
}
/**
* Downloads the file of the exported taxonomy
- * @param {number} taxonomyId The ID of the taxonomy
- * @param {'json'|'csv'} format Which format to use for the export file.
- * @returns {void}
+ * @param taxonomyId The ID of the taxonomy
+ * @param format Which format to use for the export file.
*/
-export function getTaxonomyExportFile(taxonomyId, format) {
+export function getTaxonomyExportFile(taxonomyId: number, format: 'json' | 'csv'): void {
window.location.href = apiUrls.exportTaxonomy(taxonomyId, format);
}
diff --git a/src/taxonomy/data/apiHooks.js b/src/taxonomy/data/apiHooks.ts
similarity index 64%
rename from src/taxonomy/data/apiHooks.js
rename to src/taxonomy/data/apiHooks.ts
index 8b2037d390..f8856c86ba 100644
--- a/src/taxonomy/data/apiHooks.js
+++ b/src/taxonomy/data/apiHooks.ts
@@ -1,4 +1,3 @@
-// @ts-check
/**
* This is a file used especially in this `taxonomy` module.
*
@@ -16,6 +15,7 @@ import { camelCaseObject } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { apiUrls, ALL_TAXONOMIES } from './api';
import * as api from './api';
+import type { QueryOptions, TagListData } from './types';
// Query key patterns. Allows an easy way to clear all data related to a given taxonomy.
// https://github.com/openedx/frontend-app-admin-portal/blob/2ba315d/docs/decisions/0006-tanstack-react-query.rst
@@ -24,46 +24,46 @@ export const taxonomyQueryKeys = {
all: ['taxonomies'],
/**
* Key for the list of taxonomies, optionally filtered by org.
- * @param {string} [org] Which org we fetched the taxonomy list for (optional)
+ * @param org Which org we fetched the taxonomy list for (optional)
*/
- taxonomyList: (org) => [
+ taxonomyList: (org?: string) => [
...taxonomyQueryKeys.all, 'taxonomyList', ...(org && org !== ALL_TAXONOMIES ? [org] : []),
],
/**
* Base key for data specific to a single taxonomy. No data is stored directly in this key.
- * @param {number} taxonomyId ID of the taxonomy
+ * @param taxonomyId ID of the taxonomy
*/
- taxonomy: (taxonomyId) => [...taxonomyQueryKeys.all, 'taxonomy', taxonomyId],
+ taxonomy: (taxonomyId: number) => [...taxonomyQueryKeys.all, 'taxonomy', taxonomyId],
/**
- * @param {number} taxonomyId ID of the taxonomy
+ * @param taxonomyId ID of the taxonomy
*/
- taxonomyMetadata: (taxonomyId) => [...taxonomyQueryKeys.taxonomy(taxonomyId), 'metadata'],
+ taxonomyMetadata: (taxonomyId: number) => [...taxonomyQueryKeys.taxonomy(taxonomyId), 'metadata'],
/**
- * @param {number} taxonomyId ID of the taxonomy
+ * @param taxonomyId ID of the taxonomy
*/
- taxonomyTagList: (taxonomyId) => [...taxonomyQueryKeys.taxonomy(taxonomyId), 'tags'],
+ taxonomyTagList: (taxonomyId: number) => [...taxonomyQueryKeys.taxonomy(taxonomyId), 'tags'],
/**
- * @param {number} taxonomyId ID of the taxonomy
- * @param {number} pageIndex Which page of tags to load (zero-based)
- * @param {number} pageSize
+ * @param taxonomyId ID of the taxonomy
+ * @param pageIndex Which page of tags to load (zero-based)
+ * @param pageSize
*/
- taxonomyTagListPage: (taxonomyId, pageIndex, pageSize) => [
+ taxonomyTagListPage: (taxonomyId: number, pageIndex: number, pageSize: number) => [
...taxonomyQueryKeys.taxonomyTagList(taxonomyId), 'page', pageIndex, pageSize,
],
/**
* Query for loading _all_ the subtags of a particular parent tag
- * @param {number} taxonomyId ID of the taxonomy
- * @param {string} parentTagValue
+ * @param taxonomyId ID of the taxonomy
+ * @param parentTagValue
*/
- taxonomyTagSubtagsList: (taxonomyId, parentTagValue) => [
+ taxonomyTagSubtagsList: (taxonomyId: number, parentTagValue: string) => [
...taxonomyQueryKeys.taxonomyTagList(taxonomyId), 'subtags', parentTagValue,
],
/**
- * @param {number} taxonomyId ID of the taxonomy
- * @param {string} fileId Some string to uniquely identify the file we want to upload
+ * @param taxonomyId ID of the taxonomy
+ * @param fileId Some string to uniquely identify the file we want to upload
*/
- importPlan: (taxonomyId, fileId) => [...taxonomyQueryKeys.all, 'importPlan', taxonomyId, fileId],
-};
+ importPlan: (taxonomyId: number, fileId: string) => [...taxonomyQueryKeys.all, 'importPlan', taxonomyId, fileId],
+} satisfies Record (string | number)[])>;
/**
* Builds the query to get the taxonomy list
@@ -83,8 +83,7 @@ export const useTaxonomyList = (org) => (
export const useDeleteTaxonomy = () => {
const queryClient = useQueryClient();
const { mutateAsync } = useMutation({
- /** @type {import("@tanstack/react-query").MutateFunction} */
- mutationFn: async ({ pk }) => api.deleteTaxonomy(pk),
+ mutationFn: async ({ pk }: { pk: number }) => api.deleteTaxonomy(pk),
onSettled: (_d, _e, args) => {
queryClient.invalidateQueries({ queryKey: taxonomyQueryKeys.taxonomyList() });
queryClient.removeQueries({ queryKey: taxonomyQueryKeys.taxonomy(args.pk) });
@@ -93,10 +92,8 @@ export const useDeleteTaxonomy = () => {
return mutateAsync;
};
-/** Builds the query to get the taxonomy detail
- * @param {number} taxonomyId
- */
-export const useTaxonomyDetails = (taxonomyId) => useQuery({
+/** Builds the query to get the taxonomy detail */
+export const useTaxonomyDetails = (taxonomyId: number) => useQuery({
queryKey: taxonomyQueryKeys.taxonomyMetadata(taxonomyId),
queryFn: () => api.getTaxonomy(taxonomyId),
});
@@ -107,20 +104,9 @@ export const useTaxonomyDetails = (taxonomyId) => useQuery({
export const useImportNewTaxonomy = () => {
const queryClient = useQueryClient();
return useMutation({
- /**
- * @type {import("@tanstack/react-query").MutateFunction<
- * import("./types.js").TaxonomyData,
- * any,
- * {
- * name: string,
- * description: string,
- * file: File,
- * }
- * >}
- */
mutationFn: async ({
name, description, file,
- }) => {
+ }: { name: string, description: string, file: File }) => {
const formData = new FormData();
formData.append('taxonomy_name', name);
formData.append('taxonomy_description', description);
@@ -145,25 +131,15 @@ export const useImportNewTaxonomy = () => {
export const useImportTags = () => {
const queryClient = useQueryClient();
return useMutation({
- /**
- * @type {import("@tanstack/react-query").MutateFunction<
- * import("./types.js").TaxonomyData,
- * any,
- * {
- * taxonomyId: number,
- * file: File,
- * }
- * >}
- */
- mutationFn: async ({ taxonomyId, file }) => {
+ mutationFn: async ({ taxonomyId, file }: { taxonomyId: number, file: File }) => {
const formData = new FormData();
formData.append('file', file);
try {
const { data } = await getAuthenticatedHttpClient().put(apiUrls.tagsImport(taxonomyId), formData);
return camelCaseObject(data);
- } catch (/** @type {any} */ err) {
- throw new Error(err.response?.data?.error || err.message);
+ } catch (err) {
+ throw new Error((err as any).response?.data?.error || (err as any).message);
}
},
onSuccess: (data) => {
@@ -178,15 +154,12 @@ export const useImportTags = () => {
/**
* Preview the results of importing the given file into an existing taxonomy.
- * @param {number} taxonomyId The ID of the taxonomy whose tags we're updating.
- * @param {File|null} file The file that we want to import
+ * @param taxonomyId The ID of the taxonomy whose tags we're updating.
+ * @param file The file that we want to import
*/
-export const useImportPlan = (taxonomyId, file) => useQuery({
+export const useImportPlan = (taxonomyId: number, file: File | null) => useQuery({
queryKey: taxonomyQueryKeys.importPlan(taxonomyId, file ? `${file.name}${file.lastModified}${file.size}` : ''),
- /**
- * @type {import("@tanstack/react-query").QueryFunction}
- */
- queryFn: async () => {
+ queryFn: async (): Promise => {
if (!taxonomyId || file === null) {
return null;
}
@@ -195,26 +168,24 @@ export const useImportPlan = (taxonomyId, file) => useQuery({
try {
const { data } = await getAuthenticatedHttpClient().put(apiUrls.tagsPlanImport(taxonomyId), formData);
- return /** @type {string} */(data.plan);
- } catch (/** @type {any} */ err) {
- throw new Error(err.response?.data?.error || err.message);
+ return data.plan as string;
+ } catch (err) {
+ throw new Error((err as any).response?.data?.error || (err as any).message);
}
},
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}
+ * Use the list of tags in a taxonomy.
*/
-export const useTagListData = (taxonomyId, options) => {
+export const useTagListData = (taxonomyId: number, options: QueryOptions) => {
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);
+ return camelCaseObject(data) as TagListData;
},
});
};
@@ -223,14 +194,11 @@ export const useTagListData = (taxonomyId, options) => {
* 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}
*/
-export const useSubTags = (taxonomyId, parentTagValue) => useQuery({
+export const useSubTags = (taxonomyId: number, parentTagValue: string) => useQuery({
queryKey: taxonomyQueryKeys.taxonomyTagSubtagsList(taxonomyId, parentTagValue),
queryFn: async () => {
const response = await getAuthenticatedHttpClient().get(apiUrls.allSubtagsOf(taxonomyId, parentTagValue));
- return camelCaseObject(response.data);
+ return camelCaseObject(response.data) as TagListData;
},
});
diff --git a/src/taxonomy/delete-dialog/messages.js b/src/taxonomy/delete-dialog/messages.ts
similarity index 100%
rename from src/taxonomy/delete-dialog/messages.js
rename to src/taxonomy/delete-dialog/messages.ts
diff --git a/src/taxonomy/export-modal/messages.js b/src/taxonomy/export-modal/messages.ts
similarity index 100%
rename from src/taxonomy/export-modal/messages.js
rename to src/taxonomy/export-modal/messages.ts
diff --git a/src/taxonomy/import-tags/ImportTagsWizard.jsx b/src/taxonomy/import-tags/ImportTagsWizard.jsx
index 7b3ff10df6..8a67fef46e 100644
--- a/src/taxonomy/import-tags/ImportTagsWizard.jsx
+++ b/src/taxonomy/import-tags/ImportTagsWizard.jsx
@@ -566,4 +566,4 @@ ImportTagsWizard.propTypes = {
reimport: PropTypes.bool,
};
-export default ImportTagsWizard;
+export { ImportTagsWizard };
diff --git a/src/taxonomy/import-tags/ImportTagsWizard.test.jsx b/src/taxonomy/import-tags/ImportTagsWizard.test.jsx
index bbf0d22028..b77dc045b5 100644
--- a/src/taxonomy/import-tags/ImportTagsWizard.test.jsx
+++ b/src/taxonomy/import-tags/ImportTagsWizard.test.jsx
@@ -17,7 +17,7 @@ import PropTypes from 'prop-types';
import initializeStore from '../../store';
import { getTaxonomyExportFile } from '../data/api';
import { TaxonomyContext } from '../common/context';
-import ImportTagsWizard from './ImportTagsWizard';
+import { ImportTagsWizard } from './ImportTagsWizard';
let store;
diff --git a/src/taxonomy/import-tags/index.js b/src/taxonomy/import-tags/index.js
deleted file mode 100644
index 031f798af0..0000000000
--- a/src/taxonomy/import-tags/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-// @ts-check
-export { default as ImportTagsWizard } from './ImportTagsWizard';
diff --git a/src/taxonomy/import-tags/index.ts b/src/taxonomy/import-tags/index.ts
new file mode 100644
index 0000000000..72f293f4b0
--- /dev/null
+++ b/src/taxonomy/import-tags/index.ts
@@ -0,0 +1 @@
+export { ImportTagsWizard } from './ImportTagsWizard';
diff --git a/src/taxonomy/import-tags/messages.js b/src/taxonomy/import-tags/messages.ts
similarity index 99%
rename from src/taxonomy/import-tags/messages.js
rename to src/taxonomy/import-tags/messages.ts
index 05f79659c5..1561c8a694 100644
--- a/src/taxonomy/import-tags/messages.js
+++ b/src/taxonomy/import-tags/messages.ts
@@ -1,4 +1,3 @@
-// @ts-check
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
diff --git a/src/taxonomy/index.js b/src/taxonomy/index.js
deleted file mode 100644
index 356c532411..0000000000
--- a/src/taxonomy/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export { default as TaxonomyListPage } from './TaxonomyListPage';
-export { default as TaxonomyLayout } from './TaxonomyLayout';
-export { TaxonomyDetailPage } from './taxonomy-detail';
diff --git a/src/taxonomy/index.ts b/src/taxonomy/index.ts
new file mode 100644
index 0000000000..17c09351c8
--- /dev/null
+++ b/src/taxonomy/index.ts
@@ -0,0 +1,3 @@
+export { TaxonomyListPage } from './TaxonomyListPage';
+export { TaxonomyLayout } from './TaxonomyLayout';
+export { TaxonomyDetailPage } from './taxonomy-detail';
diff --git a/src/taxonomy/manage-orgs/index.js b/src/taxonomy/manage-orgs/index.ts
similarity index 100%
rename from src/taxonomy/manage-orgs/index.js
rename to src/taxonomy/manage-orgs/index.ts
diff --git a/src/taxonomy/manage-orgs/messages.js b/src/taxonomy/manage-orgs/messages.ts
similarity index 99%
rename from src/taxonomy/manage-orgs/messages.js
rename to src/taxonomy/manage-orgs/messages.ts
index e7b13c7363..b422549815 100644
--- a/src/taxonomy/manage-orgs/messages.js
+++ b/src/taxonomy/manage-orgs/messages.ts
@@ -1,4 +1,3 @@
-// @ts-check
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
diff --git a/src/taxonomy/messages.js b/src/taxonomy/messages.ts
similarity index 100%
rename from src/taxonomy/messages.js
rename to src/taxonomy/messages.ts
diff --git a/src/taxonomy/system-defined-badge/messages.js b/src/taxonomy/system-defined-badge/messages.ts
similarity index 100%
rename from src/taxonomy/system-defined-badge/messages.js
rename to src/taxonomy/system-defined-badge/messages.ts
diff --git a/src/taxonomy/taxonomy-card/messages.js b/src/taxonomy/taxonomy-card/messages.ts
similarity index 100%
rename from src/taxonomy/taxonomy-card/messages.js
rename to src/taxonomy/taxonomy-card/messages.ts
diff --git a/src/taxonomy/taxonomy-detail/index.js b/src/taxonomy/taxonomy-detail/index.ts
similarity index 84%
rename from src/taxonomy/taxonomy-detail/index.js
rename to src/taxonomy/taxonomy-detail/index.ts
index f379eb991b..0b27a5d2a5 100644
--- a/src/taxonomy/taxonomy-detail/index.js
+++ b/src/taxonomy/taxonomy-detail/index.ts
@@ -1,2 +1 @@
-// @ts-check
export { default as TaxonomyDetailPage } from './TaxonomyDetailPage';
diff --git a/src/taxonomy/taxonomy-detail/messages.js b/src/taxonomy/taxonomy-detail/messages.ts
similarity index 98%
rename from src/taxonomy/taxonomy-detail/messages.js
rename to src/taxonomy/taxonomy-detail/messages.ts
index 922474216d..3aa7968399 100644
--- a/src/taxonomy/taxonomy-detail/messages.js
+++ b/src/taxonomy/taxonomy-detail/messages.ts
@@ -1,4 +1,3 @@
-// ts-check
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
diff --git a/src/taxonomy/taxonomy-menu/index.js b/src/taxonomy/taxonomy-menu/index.ts
similarity index 81%
rename from src/taxonomy/taxonomy-menu/index.js
rename to src/taxonomy/taxonomy-menu/index.ts
index 9e5393396e..ca59b994a5 100644
--- a/src/taxonomy/taxonomy-menu/index.js
+++ b/src/taxonomy/taxonomy-menu/index.ts
@@ -1,2 +1 @@
-// @ts-check
export { default as TaxonomyMenu } from './TaxonomyMenu';
diff --git a/src/taxonomy/taxonomy-menu/messages.js b/src/taxonomy/taxonomy-menu/messages.ts
similarity index 98%
rename from src/taxonomy/taxonomy-menu/messages.js
rename to src/taxonomy/taxonomy-menu/messages.ts
index 3a71118ccb..8e8daa8e74 100644
--- a/src/taxonomy/taxonomy-menu/messages.js
+++ b/src/taxonomy/taxonomy-menu/messages.ts
@@ -1,4 +1,3 @@
-// @ts-check
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({