diff --git a/client/src/components/Header/components/auth-or-profile.tsx b/client/src/components/Header/components/auth-or-profile.tsx index 1d7560076a4..e2dc8fbe321 100644 --- a/client/src/components/Header/components/auth-or-profile.tsx +++ b/client/src/components/Header/components/auth-or-profile.tsx @@ -1,11 +1,15 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import type { User } from '../../../redux/prop-types'; import { Link, AvatarRenderer } from '../../helpers'; import Login from './login'; interface AuthOrProfileProps { - user?: User; + user?: { + isDonating: boolean; + username: string; + picture: string; + yearsTopContributor: string[]; + }; } const AuthOrProfile = ({ user }: AuthOrProfileProps): JSX.Element => { const { t } = useTranslation(); diff --git a/client/src/components/Header/components/menu-button.tsx b/client/src/components/Header/components/menu-button.tsx index e7863a6ee40..7571296b250 100644 --- a/client/src/components/Header/components/menu-button.tsx +++ b/client/src/components/Header/components/menu-button.tsx @@ -2,7 +2,6 @@ import React, { RefObject } from 'react'; import { useTranslation } from 'react-i18next'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faBars } from '@fortawesome/free-solid-svg-icons'; -import { User } from '../../../redux/prop-types'; interface MenuButtonProps { className?: string; @@ -10,7 +9,6 @@ interface MenuButtonProps { innerRef?: RefObject; showMenu: () => void; hideMenu: () => void; - user?: User; } const MenuButton = ({ diff --git a/client/src/components/Header/components/nav-links.tsx b/client/src/components/Header/components/nav-links.tsx index 5a255f19d46..591a6915f75 100644 --- a/client/src/components/Header/components/nav-links.tsx +++ b/client/src/components/Header/components/nav-links.tsx @@ -13,14 +13,13 @@ import { openSignoutModal, toggleTheme } from '../../../redux/actions'; import { Link } from '../../helpers'; import { LocalStorageThemes } from '../../../redux/types'; import { themeSelector } from '../../../redux/selectors'; -import { User } from '../../../redux/prop-types'; import SupporterBadge from '../../../assets/icons/supporter-badge'; export interface NavLinksProps { displayMenu: boolean; showMenu: () => void; hideMenu: () => void; - user?: User; + user?: { isDonating: boolean; username: string }; menuButtonRef: React.RefObject; openSignoutModal: () => void; theme: LocalStorageThemes; diff --git a/client/src/components/Header/components/universal-nav.tsx b/client/src/components/Header/components/universal-nav.tsx index b2538c96afa..c94c613e5a1 100644 --- a/client/src/components/Header/components/universal-nav.tsx +++ b/client/src/components/Header/components/universal-nav.tsx @@ -7,7 +7,7 @@ import { Link, SkeletonSprite } from '../../helpers'; import { SEARCH_EXPOSED_WIDTH } from '../../../../config/misc'; import FreeCodeCampLogo from '../../../assets/icons/freecodecamp-logo'; import MenuButton from './menu-button'; -import NavLinks, { type NavLinksProps } from './nav-links'; +import NavLinks from './nav-links'; import AuthOrProfile from './auth-or-profile'; import LanguageList from './language-list'; @@ -18,10 +18,17 @@ const SearchBarOptimized = Loadable( () => import('../../search/searchBar/search-bar-optimized') ); -type UniversalNavProps = Omit< - NavLinksProps, - 'toggleTheme' | 'openSignoutModal' -> & { +type UniversalNavProps = { + displayMenu: boolean; + showMenu: () => void; + hideMenu: () => void; + menuButtonRef: React.RefObject; + user: { + isDonating: boolean; + username: string; + picture: string; + yearsTopContributor: string[]; + }; fetchState: { pending: boolean }; searchBarRef?: React.RefObject; pathname: string; @@ -81,7 +88,6 @@ const UniversalNav = ({ hideMenu={hideMenu} innerRef={menuButtonRef} showMenu={showMenu} - user={user} /> {!isSearchExposedWidth && search} ', () => { it('has avatar with default border for default users', () => { - const defaultUserProps = { - user: { - username: 'test-user', - picture: 'https://freecodecamp.org/image.png' - }, - pending: false, - pathName: '/learn' - }; - const componentTree = create( ).toJSON(); @@ -26,16 +48,6 @@ describe('', () => { }); it('has avatar with gold border for donating users', () => { - const donatingUserProps = { - user: { - username: 'test-user', - picture: 'https://freecodecamp.org/image.png', - isDonating: true - }, - pending: false, - pathName: '/learn' - }; - const componentTree = create( ).toJSON(); @@ -43,16 +55,6 @@ describe('', () => { }); it('has avatar with blue border for top contributors', () => { - const topContributorUserProps = { - user: { - username: 'test-user', - picture: 'https://freecodecamp.org/image.png', - yearsTopContributor: [2020] - }, - pending: false, - pathName: '/learn' - }; - const componentTree = create( ).toJSON(); @@ -60,17 +62,6 @@ describe('', () => { }); it('has avatar with purple border for donating top contributors', () => { - const topDonatingContributorUserProps = { - user: { - username: 'test-user', - picture: 'https://freecodecamp.org/image.png', - isDonating: true, - yearsTopContributor: [2020] - }, - pending: false, - pathName: '/learn' - }; - const componentTree = create( ).toJSON(); @@ -78,15 +69,17 @@ describe('', () => { }); }); -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const profileNavItem = (component: any) => component.children[0]; +type Component = { + children: { props: { className: string } }[]; +}; +const profileNavItem = (component: Component) => component.children[0]; const avatarHasClass = ( componentTree: ReactTestRendererJSON | ReactTestRendererJSON[] | null, classes: string ) => { return ( - profileNavItem(componentTree).props.className === + profileNavItem(componentTree as unknown as Component).props.className === 'avatar-container ' + classes ); }; diff --git a/client/src/components/Header/index.tsx b/client/src/components/Header/index.tsx index 6b3da853264..4a6ebf1fb04 100644 --- a/client/src/components/Header/index.tsx +++ b/client/src/components/Header/index.tsx @@ -5,7 +5,6 @@ import React from 'react'; import { ConnectedProps, connect } from 'react-redux'; import { createSelector } from 'reselect'; -import { User } from '../../redux/prop-types'; import { examInProgressSelector } from '../../redux/selectors'; import UniversalNav from './components/universal-nav'; @@ -26,7 +25,12 @@ type PropsFromRedux = ConnectedProps; type Props = PropsFromRedux & { fetchState: { pending: boolean }; - user: User; + user: { + isDonating: boolean; + username: string; + picture: string; + yearsTopContributor: string[]; + }; skipButtonText: string; pathname: string; }; diff --git a/client/src/components/Map/index.tsx b/client/src/components/Map/index.tsx index f405a068415..be7cca8a0fe 100644 --- a/client/src/components/Map/index.tsx +++ b/client/src/components/Map/index.tsx @@ -57,7 +57,6 @@ const superBlockHeadings: { [key in SuperBlockStage]: string } = { [SuperBlockStage.Professional]: 'landing.professional-certs-heading', [SuperBlockStage.Extra]: 'landing.interview-prep-heading', [SuperBlockStage.Legacy]: 'landing.legacy-curriculum-heading', - [SuperBlockStage.New]: '', // TODO: add translation [SuperBlockStage.Next]: 'landing.next-heading', [SuperBlockStage.NextEnglish]: 'landing.next-english-heading', [SuperBlockStage.Upcoming]: 'landing.upcoming-heading' diff --git a/client/src/components/profile/components/portfolio.tsx b/client/src/components/profile/components/portfolio.tsx index 7d45bae306b..11a3263323d 100644 --- a/client/src/components/profile/components/portfolio.tsx +++ b/client/src/components/profile/components/portfolio.tsx @@ -202,7 +202,10 @@ class PortfolioSettings extends Component { }); } - getUrlValidation(url: string) { + getUrlValidation(url: string): { + state: 'success' | 'warning' | 'error'; + message: string; + } { const { t } = this.props; const len = url.length; diff --git a/client/src/components/profile/components/utils/index.ts b/client/src/components/profile/components/utils/index.ts index fd9c688c050..29c6739f759 100644 --- a/client/src/components/profile/components/utils/index.ts +++ b/client/src/components/profile/components/utils/index.ts @@ -1 +1 @@ -export { parseDate, formatYears } from './utils'; +export { parseDate } from './utils'; diff --git a/client/src/components/profile/profile.test.tsx b/client/src/components/profile/profile.test.tsx index bf4b8c0ca99..00815962ffa 100644 --- a/client/src/components/profile/profile.test.tsx +++ b/client/src/components/profile/profile.test.tsx @@ -90,6 +90,11 @@ function renderWithRedux(ui: JSX.Element) { } describe('', () => { it('renders the report button on another persons profile', () => { + // TODO: Profile is a mess, it shouldn't depend on the entire user. Each + // component Camper, Stats, HeatMap etc should be get the relevant data from + // the store themselves. + + // @ts-expect-error - quick hack to mollify TS. renderWithRedux(); const reportButton: HTMLElement = screen.getByText('buttons.flag-user'); @@ -97,6 +102,7 @@ describe('', () => { }); it('renders correctly', () => { + // @ts-expect-error - quick hack to mollify TS. const { container } = renderWithRedux(); expect(container).toMatchSnapshot(); diff --git a/client/src/components/settings/delete-modal.tsx b/client/src/components/settings/delete-modal.tsx index 3a08d17e7f0..9cbe7e81e73 100644 --- a/client/src/components/settings/delete-modal.tsx +++ b/client/src/components/settings/delete-modal.tsx @@ -68,6 +68,7 @@ function DeleteModal(props: DeleteModalProps): JSX.Element { /> + {/* @ts-expect-error The UI lib's types don't allow this: https://github.com/freeCodeCamp/ui/issues/473 */}