feat(client): ts-migrate header components (#42287)

pull/42624/head
Akshat Garg 2021-06-25 21:13:04 +05:30 committed by Mrugesh Mohapatra
parent e54e2588bf
commit ea4eeee49e
No known key found for this signature in database
GPG Key ID: 68BDF41E23F50DD8
11 changed files with 175 additions and 142 deletions

View File

@ -1,9 +1,9 @@
import React from 'react';
import ShallowRenderer from 'react-test-renderer/shallow';
import { UniversalNav } from './components/UniversalNav';
import { NavLinks } from './components/NavLinks';
import AuthOrProfile from './components/AuthOrProfile';
import { UniversalNav } from './components/universal-nav';
import { NavLinks } from './components/nav-links';
import AuthOrProfile from './components/auth-or-profile';
import envData from '../../../../config/env.json';

View File

@ -1,5 +1,9 @@
/* eslint-disable react/prop-types */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable import/no-unresolved */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { Button } from '@freecodecamp/react-bootstrap';
@ -16,14 +20,21 @@ const mapStateToProps = createSelector(isSignedInSelector, isSignedIn => ({
isSignedIn
}));
function Login(props) {
export interface LoginProps {
block?: boolean;
children?: unknown;
'data-test-label'?: string;
isSignedIn?: boolean;
}
const Login = ({
block,
children,
'data-test-label': dataTestLabel,
isSignedIn
}: LoginProps): JSX.Element => {
const { t } = useTranslation();
const {
block,
'data-test-label': dataTestLabel,
children,
isSignedIn
} = props;
const href = isSignedIn ? `${homeLocation}/learn` : `${apiLocation}/signin`;
return (
<Button
@ -35,14 +46,8 @@ function Login(props) {
{children || t('buttons.sign-in')}
</Button>
);
}
Login.displayName = 'Login';
Login.propTypes = {
block: PropTypes.bool,
children: PropTypes.any,
'data-test-label': PropTypes.string,
isSignedIn: PropTypes.bool
};
Login.displayName = 'Login';
export default connect(mapStateToProps)(Login);

View File

@ -1,38 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import AuthOrProfile from './AuthOrProfile';
const MenuButton = props => {
const { t } = useTranslation();
return (
<>
<button
aria-expanded={props.displayMenu}
className={
'toggle-button-nav' +
(props.displayMenu ? ' reverse-toggle-color' : '')
}
onClick={props.onClick}
ref={props.innerRef}
>
{t('buttons.menu')}
</button>
<span className='navatar'>
<AuthOrProfile user={props.user} />
</span>
</>
);
};
MenuButton.displayName = 'MenuButton';
MenuButton.propTypes = {
className: PropTypes.string,
displayMenu: PropTypes.bool.isRequired,
innerRef: PropTypes.object,
onClick: PropTypes.func.isRequired,
user: PropTypes.object
};
export default MenuButton;

View File

@ -1,16 +1,18 @@
/* eslint-disable react/sort-prop-types */
/* eslint-disable @typescript-eslint/ban-types */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
// @ts-nocheck
import React from 'react';
import { Link, borderColorPicker, AvatarRenderer } from '../../helpers';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import Login from './Login';
import Login from '../components/Login';
const propTypes = {
user: PropTypes.object
};
export function AuthOrProfile({ user }) {
export interface AuthOrProfileProps {
user?: Object;
}
const AuthOrProfile = ({ user }: AuthOrProfileProps): JSX.Element => {
const { t } = useTranslation();
const isUserDonating = user && user.isDonating;
const isUserSignedIn = user && user.username;
@ -39,8 +41,7 @@ export function AuthOrProfile({ user }) {
</>
);
}
}
};
AuthOrProfile.propTypes = propTypes;
AuthOrProfile.displayName = 'AuthOrProfile';
export default AuthOrProfile;

View File

@ -0,0 +1,44 @@
/* eslint-disable @typescript-eslint/ban-types */
/* eslint-disable react/prop-types */
import React, { RefObject } from 'react';
import { useTranslation } from 'react-i18next';
import AuthOrProfile from './auth-or-profile';
export interface MenuButtonProps {
className?: string;
displayMenu?: boolean;
innerRef?: RefObject<HTMLButtonElement>;
onClick?: React.MouseEventHandler<HTMLButtonElement> | undefined;
user?: Object;
}
const MenuButton = ({
displayMenu,
innerRef,
onClick,
user
}: MenuButtonProps): JSX.Element => {
const { t } = useTranslation();
return (
<>
<button
aria-expanded={displayMenu}
className={
'toggle-button-nav' + (displayMenu ? ' reverse-toggle-color' : '')
}
onClick={onClick}
ref={innerRef}
>
{t('buttons.menu')}
</button>
<span className='navatar'>
<AuthOrProfile user={user} />
</span>
</>
);
};
MenuButton.displayName = 'MenuButton';
export default MenuButton;

View File

@ -1,6 +1,15 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/ban-types */
/* eslint-disable react/prop-types */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
// @ts-nocheck
import React, { Component, Fragment } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { withTranslation } from 'react-i18next';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
@ -15,33 +24,33 @@ import { updateUserFlag } from '../../../redux/settings';
import envData from '../../../../../config/env.json';
import createLanguageRedirect from '../../createLanguageRedirect';
import createExternalRedirect from '../../createExternalRedirects';
const { clientLocale, radioLocation, apiLocation } = envData;
const {
import {
availableLangs,
i18nextCodes,
langDisplayNames
} = require('../../../../../config/i18n/all-langs');
} from '../../../../../config/i18n/all-langs';
const { clientLocale, radioLocation, apiLocation } = envData;
const locales = availableLangs.client;
const propTypes = {
displayMenu: PropTypes.bool,
fetchState: PropTypes.shape({ pending: PropTypes.bool }),
i18n: PropTypes.object,
t: PropTypes.func,
toggleDisplayMenu: PropTypes.func,
toggleNightMode: PropTypes.func.isRequired,
user: PropTypes.object
};
export interface NavLinksProps {
displayMenu?: boolean;
fetchState?: { pending: boolean };
i18n: Object;
t: (x: any) => any;
toggleDisplayMenu?: React.MouseEventHandler<HTMLButtonElement>;
toggleNightMode: (x: any) => any;
user?: Record<string, unknown>;
}
const mapDispatchToProps = {
toggleNightMode: theme => updateUserFlag({ theme })
toggleNightMode: (theme: unknown) => updateUserFlag({ theme })
};
export class NavLinks extends Component {
toggleTheme(currentTheme = 'default', toggleNightMode) {
export class NavLinks extends Component<NavLinksProps, {}> {
static displayName: string;
toggleTheme(currentTheme = 'default', toggleNightMode: any) {
toggleNightMode(currentTheme === 'night' ? 'default' : 'night');
}
@ -54,7 +63,7 @@ export class NavLinks extends Component {
toggleDisplayMenu,
toggleNightMode,
user: { isDonating = false, username, theme }
} = this.props;
}: NavLinksProps = this.props;
const { pending } = fetchState;
return pending ? (
@ -141,7 +150,7 @@ export class NavLinks extends Component {
}
disabled={!username}
key='theme'
onClick={() => this.toggleTheme(theme, toggleNightMode)}
onClick={() => this.toggleTheme(String(theme), toggleNightMode)}
>
{username ? (
<>
@ -165,7 +174,7 @@ export class NavLinks extends Component {
<button
className='nav-link nav-link-lang nav-link-flex'
key={'lang-' + lang}
onClick={() => toggleDisplayMenu()}
onClick={toggleDisplayMenu}
>
<span>{langDisplayNames[lang]}</span>
<FontAwesomeIcon icon={faCheck} />
@ -202,7 +211,6 @@ export class NavLinks extends Component {
}
}
NavLinks.propTypes = propTypes;
NavLinks.displayName = 'NavLinks';
export default connect(null, mapDispatchToProps)(withTranslation()(NavLinks));

View File

@ -1,9 +1,9 @@
import React from 'react';
import FreeCodeCampLogo from '../../../assets/icons/FreeCodeCamp-logo';
function NavLogo() {
const NavLogo = (): JSX.Element => {
return <FreeCodeCampLogo />;
}
};
NavLogo.displayName = 'NavLogo';
export default NavLogo;

View File

@ -1,20 +1,31 @@
import React from 'react';
import PropTypes from 'prop-types';
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable react/prop-types */
// @ts-nocheck
import React, { Ref } from 'react';
import { Link, SkeletonSprite } from '../../helpers';
import NavLogo from './NavLogo';
import MenuButton from './MenuButton';
import NavLinks from './NavLinks';
import './universalNav.css';
import NavLogo from './nav-logo';
import MenuButton from './menu-button';
import NavLinks from './nav-links';
import { isLanding } from '../../../utils/path-parsers';
import Loadable from '@loadable/component';
import './universal-nav.css';
const SearchBar = Loadable(() => import('../../search/searchBar/SearchBar'));
const SearchBarOptimized = Loadable(() =>
import('../../search/searchBar/search-bar-optimized')
const SearchBarOptimized = Loadable(
() => import('../../search/searchBar/search-bar-optimized')
);
export interface UniversalNavProps {
displayMenu?: boolean;
fetchState?: { pending: boolean };
menuButtonRef?: Ref<HTMLButtonElement> | undefined;
searchBarRef?: unknown;
toggleDisplayMenu?: React.MouseEventHandler<HTMLButtonElement> | undefined;
user?: Record<string, unknown>;
}
export const UniversalNav = ({
displayMenu,
toggleDisplayMenu,
@ -22,7 +33,7 @@ export const UniversalNav = ({
searchBarRef,
user,
fetchState
}) => {
}: UniversalNavProps): JSX.Element => {
const { pending } = fetchState;
const search =
@ -77,12 +88,3 @@ export const UniversalNav = ({
UniversalNav.displayName = 'UniversalNav';
export default UniversalNav;
UniversalNav.propTypes = {
displayMenu: PropTypes.bool,
fetchState: PropTypes.shape({ pending: PropTypes.bool }),
menuButtonRef: PropTypes.object,
searchBarRef: PropTypes.object,
toggleDisplayMenu: PropTypes.func,
user: PropTypes.object
};

View File

@ -1,5 +1,8 @@
/* eslint-disable react/prop-types */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { Link } from 'gatsby';
@ -12,21 +15,20 @@ import Login from './Login';
const mapStateToProps = createSelector(
userFetchStateSelector,
isSignedInSelector,
(fetchState, isSignedIn) => ({
(fetchState: any, isSignedIn: any) => ({
isSignedIn,
showLoading: fetchState.pending
})
);
const propTypes = {
disableSettings: PropTypes.bool,
email: PropTypes.string,
isSignedIn: PropTypes.bool,
name: PropTypes.string,
showLoading: PropTypes.bool
};
function UserState(props) {
export interface UserStateProps {
disableSettings?: boolean;
email?: string;
isSignedIn?: boolean;
name?: string;
showLoading?: boolean;
}
const UserState = (props: UserStateProps): JSX.Element => {
const { isSignedIn, showLoading, disableSettings } = props;
const { t } = useTranslation();
@ -39,7 +41,6 @@ function UserState(props) {
className='user-state-spinner'
color='white'
fadeIn='none'
height='38px'
name='line-scale'
/>
);
@ -51,9 +52,8 @@ function UserState(props) {
) : (
<Login />
);
}
};
UserState.displayName = 'UserState';
UserState.propTypes = propTypes;
export default connect(mapStateToProps)(UserState);

View File

@ -1,18 +1,28 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/unbound-method */
/* eslint-disable @typescript-eslint/ban-types */
import React from 'react';
import Helmet from 'react-helmet';
import PropTypes from 'prop-types';
import UniversalNav from './components/UniversalNav';
import UniversalNav from './components/universal-nav';
import './header.css';
const propTypes = {
fetchState: PropTypes.shape({ pending: PropTypes.bool }),
user: PropTypes.object
};
export class Header extends React.Component {
constructor(props) {
export interface HeaderProps {
fetchState: { pending: boolean };
user: Record<string, any>;
}
export class Header extends React.Component<
HeaderProps,
{ displayMenu: boolean }
> {
menuButtonRef: React.RefObject<any>;
searchBarRef: React.RefObject<any>;
static displayName: string;
constructor(props: HeaderProps) {
super(props);
this.state = {
displayMenu: false
@ -23,15 +33,15 @@ export class Header extends React.Component {
this.toggleDisplayMenu = this.toggleDisplayMenu.bind(this);
}
componentDidMount() {
componentDidMount(): void {
document.addEventListener('click', this.handleClickOutside);
}
componentWillUnmount() {
componentWillUnmount(): void {
document.removeEventListener('click', this.handleClickOutside);
}
handleClickOutside(event) {
handleClickOutside(event: any): void {
if (
this.state.displayMenu &&
this.menuButtonRef.current &&
@ -43,10 +53,12 @@ export class Header extends React.Component {
}
}
toggleDisplayMenu() {
this.setState(({ displayMenu }) => ({ displayMenu: !displayMenu }));
toggleDisplayMenu(): void {
this.setState(({ displayMenu }: { displayMenu: boolean }) => ({
displayMenu: !displayMenu
}));
}
render() {
render(): JSX.Element {
const { displayMenu } = this.state;
const { fetchState, user } = this.props;
return (
@ -69,7 +81,6 @@ export class Header extends React.Component {
}
}
Header.propTypes = propTypes;
Header.displayName = 'Header';
export default Header;