Skip to content

Commit

Permalink
feat(client): redesigned navigation (freeCodeCamp#40709)
Browse files Browse the repository at this point in the history
Co-authored-by: moT01 <[email protected]>
  • Loading branch information
2 people authored and raisedadead committed Jan 31, 2021
1 parent 9014fff commit 58c6c54
Show file tree
Hide file tree
Showing 17 changed files with 459 additions and 277 deletions.
4 changes: 4 additions & 0 deletions client/i18n/locales/chinese/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@
"sign-out": "退出",
"curriculum": "课程",
"forum": "论坛",
"radio": "Radio",
"profile": "个人资料",
"news": "News",
"donate": "Donate",
"update-settings": "更新我的账号设置",
"sign-me-out": "退出登录 freeCodeCamp",
"flag-user": "标记该用户的账号为滥用",
Expand Down Expand Up @@ -314,6 +317,7 @@
"donate": {
"title": "支持我们的非营利组织",
"processing": "我们正在处理你的捐款。",
"thanks": "Thanks for donating",
"thank-you": "谢谢你成为我们的支持者。",
"thank-you-2": "谢谢你成为 freeCodeCamp 的支持者。现在你已设置定期捐款。",
"additional": "你可以使用这个链接 <0>{{url}}</0> 额外进行一次性捐款:",
Expand Down
6 changes: 5 additions & 1 deletion client/i18n/locales/english/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@
"sign-out": "Sign out",
"curriculum": "Curriculum",
"forum": "Forum",
"radio": "Radio",
"profile": "Profile",
"news": "News",
"donate": "Donate",
"update-settings": "Update my account settings",
"sign-me-out": "Sign me out of freeCodeCamp",
"flag-user": "Flag This User's Account for Abuse",
Expand Down Expand Up @@ -316,6 +319,7 @@
"donate": {
"title": "Support our nonprofit",
"processing": "We are processing your donation.",
"thanks": "Thanks for donating",
"thank-you": "Thank you for being a supporter.",
"thank-you-2": "Thank you for being a supporter of freeCodeCamp. You currently have a recurring donation.",
"additional": "You can make an additional one-time donation of any amount using this link: <0>{{url}}</0>",
Expand Down Expand Up @@ -376,7 +380,7 @@
},
"search": {
"label": "Search",
"placeholder": "Search 6,000+ tutorial",
"placeholder": "Search 6,000+ tutorials",
"see-results": "See all results for {{searchQuery}}",
"no-tutorials": "No tutorials found",
"try": "Looking for something? Try the search bar on this page.",
Expand Down
4 changes: 4 additions & 0 deletions client/i18n/locales/espanol/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@
"sign-out": "Cerrar sesión",
"curriculum": "Plan de estudio",
"forum": "Foro",
"radio": "Radio",
"profile": "Perfil",
"news": "News",
"donate": "Donate",
"update-settings": "Actualizar la configuración de mi cuenta",
"sign-me-out": "Cerrar sesión en freeCodeCamp",
"flag-user": "Marcar la cuenta de este usuario por abuso",
Expand Down Expand Up @@ -316,6 +319,7 @@
"donate": {
"title": "Apoya a nuestra organización sin fines de lucro",
"processing": "Estamos procesando tu donación.",
"thanks": "Thanks for donating",
"thank-you": "Gracias por tu apoyo.",
"thank-you-2": "Gracias por apoyar a freeCodeCamp. Actualmente tienes una donación recurrente.",
"additional": "Puede hacer una donación adicional por única vez de cualquier monto utilizando este enlace: <0>{{url}}</0>",
Expand Down
4 changes: 4 additions & 0 deletions client/i18n/translations-schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ const translationsSchema = {
'sign-out': 'Sign out',
curriculum: 'Curriculum',
forum: 'Forum',
radio: 'Radio',
profile: 'Profile',
news: 'News',
donate: 'Donate',
'update-settings': 'Update my account settings',
'sign-me-out': 'Sign me out of freeCodeCamp',
'flag-user': "Flag This User's Account for Abuse",
Expand Down Expand Up @@ -365,6 +368,7 @@ const translationsSchema = {
donate: {
title: 'Support our nonprofit',
processing: 'We are processing your donation.',
thanks: 'Thanks for donating',
'thank-you': 'Thank you for being a supporter.',
'thank-you-2':
'Thank you for being a supporter of freeCodeCamp. You currently have a recurring donation.',
Expand Down
101 changes: 69 additions & 32 deletions client/src/components/Header/Header.test.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
/* global expect */
import React from 'react';
import ShallowRenderer from 'react-test-renderer/shallow';
import renderer from 'react-test-renderer';
/* import { useTranslation } from 'react-i18next';
import { I18nextProvider } from 'react-i18next';
import i18n from '../../../i18n/configForTests';*/
import { UniversalNav } from './components/UniversalNav';
import { AuthOrProfile } from './components/NavLinks';
import { NavLinks } from './components/NavLinks';
import AuthOrProfile from './components/AuthOrProfile';

describe('<UniversalNav />', () => {
const UniversalNavProps = {
Expand All @@ -26,32 +26,48 @@ describe('<UniversalNav />', () => {
});

describe('<NavLinks />', () => {
it('shows Curriculum and Sign In buttons when not signed in', () => {
it('has expected navigation links', () => {
const landingPageProps = {
pending: false
fetchState: {
pending: false
},
user: {
isUserDonating: false,
username: '',
theme: 'default'
},
i18n: {
language: 'en'
},
toggleNightMode: theme => theme
};
const shallow = new ShallowRenderer();
shallow.render(<AuthOrProfile {...landingPageProps} />);
shallow.render(<NavLinks {...landingPageProps} />);
const result = shallow.getRenderOutput();
expect(
hasForumNavItem(result) &&
hasRadioNavItem(result) &&
hasForumNavItem(result) &&
hasCurriculumNavItem(result) &&
hasSignInButton(result)
hasNewsNavItem(result) &&
hasDonateNavItem(result)
).toBeTruthy();
});
});

describe('<AuthOrProfile />', () => {
it('has avatar with default border for default users', () => {
const defaultUserProps = {
user: {
username: 'test-user',
picture: 'https://freecodecamp.org/image.png'
},
pending: false
pending: false,
pathName: '/learn'
};

const componentTree = renderer
.create(<AuthOrProfile {...defaultUserProps} />)
.toJSON();
const shallow = new ShallowRenderer();
shallow.render(<AuthOrProfile {...defaultUserProps} />);
const componentTree = shallow.getRenderOutput();

expect(avatarHasClass(componentTree, 'default-border')).toBeTruthy();
});
Expand All @@ -63,11 +79,12 @@ describe('<NavLinks />', () => {
picture: 'https://freecodecamp.org/image.png',
isDonating: true
},
pending: false
pending: false,
pathName: '/learn'
};
const componentTree = renderer
.create(<AuthOrProfile {...donatingUserProps} />)
.toJSON();
const shallow = new ShallowRenderer();
shallow.render(<AuthOrProfile {...donatingUserProps} />);
const componentTree = shallow.getRenderOutput();

expect(avatarHasClass(componentTree, 'gold-border')).toBeTruthy();
});
Expand All @@ -79,12 +96,13 @@ describe('<NavLinks />', () => {
picture: 'https://freecodecamp.org/image.png',
yearsTopContributor: [2020]
},
pending: false
pending: false,
pathName: '/learn'
};

const componentTree = renderer
.create(<AuthOrProfile {...topContributorUserProps} />)
.toJSON();
const shallow = new ShallowRenderer();
shallow.render(<AuthOrProfile {...topContributorUserProps} />);
const componentTree = shallow.getRenderOutput();

expect(avatarHasClass(componentTree, 'blue-border')).toBeTruthy();
});
Expand All @@ -96,41 +114,60 @@ describe('<NavLinks />', () => {
isDonating: true,
yearsTopContributor: [2020]
},
pending: false
pending: false,
pathName: '/learn'
};
const componentTree = renderer
.create(<AuthOrProfile {...topDonatingContributorUserProps} />)
.toJSON();
const shallow = new ShallowRenderer();
shallow.render(<AuthOrProfile {...topDonatingContributorUserProps} />);
const componentTree = shallow.getRenderOutput();
expect(avatarHasClass(componentTree, 'purple-border')).toBeTruthy();
});
});

const navigationLinks = (component, navItem) => {
return component.props.children[0].props.children[navItem].props.children
.props;
return component.props.children.props.children[navItem].props.children.props;
};

const profileNavItem = component => component[2].children[0];
const profileNavItem = component => component.props.children;

const hasForumNavItem = component => {
const hasDonateNavItem = component => {
const { children, to } = navigationLinks(component, 0);
return children === 'buttons.donate' && to === '/donate';
};

const hasForumNavItem = component => {
const { children, to } = navigationLinks(component, 1);
return (
children === 'buttons.forum' && to === 'https://forum.freecodecamp.org'
);
};

const hasNewsNavItem = component => {
const { children, to } = navigationLinks(component, 2);
return (
children === 'buttons.news' && to === 'https://www.freecodecamp.org/news'
);
};

const hasCurriculumNavItem = component => {
const { children, to } = navigationLinks(component, 1);
const { children, to } = navigationLinks(component, 3);
return children === 'buttons.curriculum' && to === '/learn';
};

const hasRadioNavItem = component => {
const { children, to } = navigationLinks(component, 5);
return (
children === 'buttons.radio' && to === 'https://coderadio.freecodecamp.org'
);
};

/* TODO: Apply this to Universalnav component
const hasSignInButton = component =>
component.props.children[1].props.children === 'buttons.sign-in';

*/
const avatarHasClass = (componentTree, classes) => {
// componentTree[1].children[0].children[1].props.className
return (
profileNavItem(componentTree).children[1].props.className ===
'avatar-container ' + classes
profileNavItem(componentTree).props.className ===
'avatar-nav-link ' + classes
);
};
30 changes: 10 additions & 20 deletions client/src/components/Header/components/AuthOrProfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,26 +39,16 @@ export function AuthOrProfile({ user, pathName, pending }) {
} else {
return (
<>
<li>
<Link className='nav-link' to='/learn'>
{t('buttons.curriculum')}
</Link>
</li>
<li>
<Link className='nav-link' to={`/${user.username}`}>
{t('buttons.profile')}
</Link>
<Link
className={`avatar-nav-link ${badgeColorClass}`}
to={`/${user.username}`}
>
<AvatarRenderer
picture={user.picture}
size='sm'
userName={user.username}
/>
</Link>
</li>
<Link
className={`avatar-nav-link ${badgeColorClass}`}
to={`/${user.username}`}
>
<AvatarRenderer
picture={user.picture}
size='sm'
userName={user.username}
/>
</Link>
</>
);
}
Expand Down
30 changes: 19 additions & 11 deletions client/src/components/Header/components/MenuButton.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
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>
<>
<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>
</>
);
};

Expand All @@ -24,7 +31,8 @@ MenuButton.propTypes = {
className: PropTypes.string,
displayMenu: PropTypes.bool.isRequired,
innerRef: PropTypes.object,
onClick: PropTypes.func.isRequired
onClick: PropTypes.func.isRequired,
user: PropTypes.object
};

export default MenuButton;
Loading

0 comments on commit 58c6c54

Please sign in to comment.