feat(client): ts-migrate client/utils/** (#42823)
* rename js files * update imports and references * migrate build-challenges * migrate challenge-types * migrate utils/index * migrate state-management * install @types/psl for tags * migrate tags * migrate tags.test * migrate challenge-page-creator * migrate utils/gatsby/index * migrate layout-selector * migrate layout-selector.test * revert challenge-types Curriculum can't handle TS or modules * convert arrow functions * revert build-challenges * revert utils/gatsby/index * revert challenge-page-creator * revert challenge-types reference * Delete state-management Deleted in #42960 * Disable render-result-naming-convention (for now) * update layout-selector.test comment * reorder imports in build-challenges * change ts-ignore to ts-expect-error
This commit is contained in:
@ -1,4 +1,5 @@
|
||||
const path = require('path');
|
||||
|
||||
const _ = require('lodash');
|
||||
|
||||
const envData = require('../../config/env.json');
|
@ -1,7 +1,7 @@
|
||||
const path = require('path');
|
||||
const { dasherize } = require('../../../utils/slugs');
|
||||
|
||||
const { viewTypes } = require('../challengeTypes');
|
||||
const { viewTypes } = require('../challenge-types');
|
||||
|
||||
const backend = path.resolve(
|
||||
__dirname,
|
||||
@ -42,21 +42,22 @@ const views = {
|
||||
// quiz: Quiz
|
||||
};
|
||||
|
||||
const getNextChallengePath = (node, index, nodeArray) => {
|
||||
function getNextChallengePath(_node, index, nodeArray) {
|
||||
const next = nodeArray[index + 1];
|
||||
return next ? next.node.fields.slug : '/learn';
|
||||
};
|
||||
}
|
||||
|
||||
const getPrevChallengePath = (node, index, nodeArray) => {
|
||||
function getPrevChallengePath(_node, index, nodeArray) {
|
||||
const prev = nodeArray[index - 1];
|
||||
return prev ? prev.node.fields.slug : '/learn';
|
||||
};
|
||||
}
|
||||
|
||||
const getTemplateComponent = challengeType => views[viewTypes[challengeType]];
|
||||
function getTemplateComponent(challengeType) {
|
||||
return views[viewTypes[challengeType]];
|
||||
}
|
||||
|
||||
exports.createChallengePages =
|
||||
createPage =>
|
||||
({ node }, index, thisArray) => {
|
||||
exports.createChallengePages = function (createPage) {
|
||||
return function ({ node }, index, thisArray) {
|
||||
const {
|
||||
superBlock,
|
||||
block,
|
||||
@ -69,7 +70,7 @@ exports.createChallengePages =
|
||||
// TODO: challengeType === 7 and isPrivate are the same, right? If so, we
|
||||
// should remove one of them.
|
||||
|
||||
return createPage({
|
||||
createPage({
|
||||
path: slug,
|
||||
component: getTemplateComponent(challengeType),
|
||||
context: {
|
||||
@ -86,35 +87,40 @@ exports.createChallengePages =
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.createBlockIntroPages = createPage => edge => {
|
||||
const {
|
||||
fields: { slug },
|
||||
frontmatter: { block }
|
||||
} = edge.node;
|
||||
|
||||
return createPage({
|
||||
path: slug,
|
||||
component: intro,
|
||||
context: {
|
||||
block: dasherize(block),
|
||||
slug
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.createSuperBlockIntroPages = createPage => edge => {
|
||||
const {
|
||||
fields: { slug },
|
||||
frontmatter: { superBlock }
|
||||
} = edge.node;
|
||||
exports.createBlockIntroPages = function (createPage) {
|
||||
return function (edge) {
|
||||
const {
|
||||
fields: { slug },
|
||||
frontmatter: { block }
|
||||
} = edge.node;
|
||||
|
||||
return createPage({
|
||||
path: slug,
|
||||
component: superBlockIntro,
|
||||
context: {
|
||||
superBlock,
|
||||
slug
|
||||
}
|
||||
});
|
||||
createPage({
|
||||
path: slug,
|
||||
component: intro,
|
||||
context: {
|
||||
block: dasherize(block),
|
||||
slug
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
exports.createSuperBlockIntroPages = function (createPage) {
|
||||
return function (edge) {
|
||||
const {
|
||||
fields: { slug },
|
||||
frontmatter: { superBlock }
|
||||
} = edge.node;
|
||||
|
||||
createPage({
|
||||
path: slug,
|
||||
component: superBlockIntro,
|
||||
context: {
|
||||
superBlock,
|
||||
slug
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
const challengePageCreators = require('./challengePageCreator');
|
||||
const challengePageCreators = require('./challenge-page-creator');
|
||||
|
||||
module.exports = {
|
||||
...challengePageCreators
|
||||
|
@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import ShallowRenderer from 'react-test-renderer/shallow';
|
||||
@ -11,10 +12,19 @@ import layoutSelector from './layout-selector';
|
||||
jest.mock('../../src/analytics');
|
||||
|
||||
const store = createStore();
|
||||
function getComponentNameAndProps(elementType, pathname) {
|
||||
const shallow = new ShallowRenderer();
|
||||
|
||||
interface NameAndProps {
|
||||
props: Record<string, unknown>;
|
||||
name: string;
|
||||
}
|
||||
function getComponentNameAndProps(
|
||||
elementType: React.JSXElementConstructor<never>,
|
||||
pathname: string
|
||||
): NameAndProps {
|
||||
// eslint-disable-next-line testing-library/render-result-naming-convention
|
||||
const shallow = ShallowRenderer.createRenderer();
|
||||
const LayoutReactComponent = layoutSelector({
|
||||
element: { type: elementType },
|
||||
element: { type: elementType, props: {}, key: '' },
|
||||
props: {
|
||||
location: {
|
||||
pathname
|
||||
@ -24,8 +34,13 @@ function getComponentNameAndProps(elementType, pathname) {
|
||||
shallow.render(<Provider store={store}>{LayoutReactComponent}</Provider>);
|
||||
const view = shallow.getRenderOutput();
|
||||
return {
|
||||
props: view.props,
|
||||
props: view.props as Record<string, unknown>,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
name: view.type.WrappedComponent.displayName
|
||||
// TODO: Revisit this when react-test-renderer is replaced with
|
||||
// react-testing-library
|
||||
};
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
CertificationLayout,
|
||||
DefaultLayout
|
||||
@ -6,21 +7,14 @@ import {
|
||||
import FourOhFourPage from '../../src/pages/404';
|
||||
import { isChallenge } from '../../src/utils/path-parsers';
|
||||
|
||||
interface Location {
|
||||
pathname: string;
|
||||
}
|
||||
|
||||
interface LayoutSelectorProps {
|
||||
props: {
|
||||
location: Location;
|
||||
};
|
||||
element: React.ReactElement;
|
||||
element: JSX.Element;
|
||||
props: { location: { pathname: string } };
|
||||
}
|
||||
|
||||
export default function layoutSelector({
|
||||
element,
|
||||
props
|
||||
}: LayoutSelectorProps): React.ReactElement {
|
||||
}: LayoutSelectorProps): JSX.Element {
|
||||
const {
|
||||
location: { pathname }
|
||||
} = props;
|
||||
|
@ -1,3 +0,0 @@
|
||||
exports.isBrowser = function isBrowser() {
|
||||
return typeof window !== 'undefined';
|
||||
};
|
3
client/utils/index.ts
Normal file
3
client/utils/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export function isBrowser(): boolean {
|
||||
return typeof window !== 'undefined';
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import { injectConditionalTags } from './tags';
|
||||
|
||||
describe('Tags', () => {
|
||||
it('injectConditionalTags should inject gap dev homelocation', () => {
|
||||
let injectedTags = injectConditionalTags(
|
||||
const injectedTags = injectConditionalTags(
|
||||
[],
|
||||
'https://www.freecodecamp.dev'
|
||||
);
|
||||
@ -10,7 +11,7 @@ describe('Tags', () => {
|
||||
expect(injectedTags[0].props.id === 'gap-dev').toBeTruthy();
|
||||
});
|
||||
it('injectConditionalTags should inject gap for english homeLocation', () => {
|
||||
let injectedTags = injectConditionalTags(
|
||||
const injectedTags = injectConditionalTags(
|
||||
[],
|
||||
'https://www.freecodecamp.org'
|
||||
);
|
||||
@ -18,7 +19,7 @@ describe('Tags', () => {
|
||||
expect(injectedTags[0].props.id === 'gap-org').toBeTruthy();
|
||||
});
|
||||
it('injectConditionalTags should inject gap for espanol homeLocation', () => {
|
||||
let injectedTags = injectConditionalTags(
|
||||
const injectedTags = injectConditionalTags(
|
||||
[],
|
||||
'https://www.freecodecamp.org/espanol'
|
||||
);
|
||||
@ -26,7 +27,7 @@ describe('Tags', () => {
|
||||
expect(injectedTags[0].props.id === 'gap-org').toBeTruthy();
|
||||
});
|
||||
it('injectConditionalTags should inject cap and chinese gap for chinese homeLocation', () => {
|
||||
let injectedTags = injectConditionalTags(
|
||||
const injectedTags = injectConditionalTags(
|
||||
[],
|
||||
'https://chinese.freecodecamp.org'
|
||||
);
|
||||
@ -35,7 +36,7 @@ describe('Tags', () => {
|
||||
expect(injectedTags[1].props.id === 'gap-org-chinese').toBeTruthy();
|
||||
});
|
||||
it('injectConditionalTags should not inject tags for localhost homeLocation', () => {
|
||||
let injectedTags = injectConditionalTags([], 'http://localhost:8000/');
|
||||
const injectedTags = injectConditionalTags([], 'http://localhost:8000/');
|
||||
expect(injectedTags.length === 0).toBeTruthy();
|
||||
});
|
||||
});
|
@ -6,13 +6,13 @@ import env from '../../config/env.json';
|
||||
|
||||
const { homeLocation } = env;
|
||||
|
||||
export const getheadTagComponents = () => {
|
||||
export function getheadTagComponents(): JSX.Element[] {
|
||||
const socialImage =
|
||||
'https://cdn.freecodecamp.org/platform/universal/fcc_meta_1920X1080-indigo.png';
|
||||
|
||||
const pathToBootstrap = withPrefix('/css/bootstrap.min.css');
|
||||
|
||||
let headTags = [
|
||||
const headTags = [
|
||||
<link
|
||||
as='style'
|
||||
href={pathToBootstrap}
|
||||
@ -54,19 +54,25 @@ export const getheadTagComponents = () => {
|
||||
/>
|
||||
];
|
||||
return injectConditionalTags(headTags, homeLocation);
|
||||
};
|
||||
}
|
||||
|
||||
// strips subpath and protocol
|
||||
|
||||
export const injectConditionalTags = (tagsArray, homeLocation) => {
|
||||
export function injectConditionalTags(
|
||||
tagsArray: JSX.Element[],
|
||||
homeLocation: string
|
||||
): JSX.Element[] {
|
||||
if (homeLocation.includes('localhost')) return tagsArray;
|
||||
|
||||
const parsedHomeUrl = psl.parse(new URL(homeLocation).host);
|
||||
const parsedHomeUrl = psl.parse(
|
||||
new URL(homeLocation).host
|
||||
) as psl.ParsedDomain;
|
||||
|
||||
// inject gap all production languages except Chinese
|
||||
if (parsedHomeUrl.subdomain === 'www' && parsedHomeUrl.tld === 'org') {
|
||||
tagsArray.push(
|
||||
<script
|
||||
// @ts-expect-error TODO: check use of href/rel on <script>
|
||||
href={withPrefix('/misc/gap-org.js')}
|
||||
id='gap-org'
|
||||
key='gap-org'
|
||||
@ -79,6 +85,7 @@ export const injectConditionalTags = (tagsArray, homeLocation) => {
|
||||
if (parsedHomeUrl.subdomain === 'www' && parsedHomeUrl.tld === 'dev') {
|
||||
tagsArray.push(
|
||||
<script
|
||||
// @ts-expect-error See above
|
||||
href={withPrefix('/misc/gap-dev.js')}
|
||||
id='gap-dev'
|
||||
key='gap-dev'
|
||||
@ -90,13 +97,15 @@ export const injectConditionalTags = (tagsArray, homeLocation) => {
|
||||
// inject cap and Chinese gap for production Chinese
|
||||
if (parsedHomeUrl.subdomain === 'chinese' && parsedHomeUrl.tld === 'org') {
|
||||
tagsArray.push(
|
||||
<scripts
|
||||
<script
|
||||
// @ts-expect-error See above
|
||||
href={withPrefix('/misc/cap.js')}
|
||||
id='cap'
|
||||
key='cap'
|
||||
rel='stylesheet'
|
||||
/>,
|
||||
<script
|
||||
// @ts-expect-error See above
|
||||
href={withPrefix('/misc/gap-org-chinese.js')}
|
||||
id='gap-org-chinese'
|
||||
key='gap-org-chinese'
|
||||
@ -105,10 +114,10 @@ export const injectConditionalTags = (tagsArray, homeLocation) => {
|
||||
);
|
||||
}
|
||||
return tagsArray;
|
||||
};
|
||||
}
|
||||
|
||||
export const getPostBodyComponents = pathname => {
|
||||
let scripts = [];
|
||||
export function getPostBodyComponents(pathname: string): JSX.Element[] {
|
||||
const scripts = [];
|
||||
const mathJaxScriptElement = (
|
||||
<script
|
||||
async={false}
|
||||
@ -127,4 +136,4 @@ export const getPostBodyComponents = pathname => {
|
||||
}
|
||||
|
||||
return scripts.filter(Boolean);
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user