Skip to content

Commit 1633564

Browse files
global nav (#195)
- Added global nav - Added account dropdown list menu - Removed old login button - Added new `--list` modifier to dropdown so styling can be reused - Testing for `GlobalNav` and `LoginMenu` closes #144 Authored-by: Lois Wells <lois.wells@raspberrypi.org>
1 parent 29a1adc commit 1633564

18 files changed

Lines changed: 349 additions & 73 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
1010
- Functionality for renaming a file (#193)
1111
- Styling for a secondary button (#193)
1212
- The web component `runCompleted` event now returns `duration: null` if the host page's tab loses focus during the code run (#192)
13+
- Global nav on editor site with link to `raspberrypi.org` and account menu
1314

1415
### Changed
1516

src/App.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Routes from './components/Routes'
55
import { useCookies } from 'react-cookie';
66
import { BrowserRouter } from 'react-router-dom';
77
import { useSelector } from 'react-redux';
8+
import GlobalNav from './components/GlobalNav/GlobalNav';
89

910
function App() {
1011
const isEmbedded = useSelector((state) => state.editor.isEmbedded);
@@ -15,7 +16,7 @@ function App() {
1516
id='app'
1617
className = {`--${cookies.theme || themeDefault } font-size-${cookies.fontSize || 'small' }`}>
1718
<BrowserRouter>
18-
{ isEmbedded ? null : <Header /> }
19+
{ isEmbedded ? null : <><GlobalNav/><Header/></> }
1920
<Routes />
2021
</BrowserRouter>
2122
</div>

src/assets/raspberrypi_logo.svg

Lines changed: 22 additions & 0 deletions
Loading
Lines changed: 4 additions & 0 deletions
Loading

src/colours.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,5 @@ $editor-light-turquoise: #B8EBE6;
2121
$editor-turquoise: #14BCAC;
2222
$editor-dark-turquoise: #11A99A;
2323
$editor-turquoise-grey: #72D7CD1A;
24+
25+
$rpf-dark-blue: #2A3E4F;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import React from "react";
2+
import { useSelector } from "react-redux";
3+
import { ChevronDown } from "../../Icons";
4+
import LoginMenu from "../Login/LoginMenu";
5+
import Dropdown from "../Menus/Dropdown/Dropdown";
6+
import './GlobalNav.scss';
7+
import rpf_logo from '../../assets/raspberrypi_logo.svg'
8+
import user_logo from '../../assets/unauthenticated_user.svg'
9+
10+
const GlobalNav = () => {
11+
12+
const user = useSelector((state) => state.auth.user)
13+
return (
14+
<div className="editor-global-nav-wrapper">
15+
<div className='editor-global-nav'>
16+
<a className='editor-global-nav__home' href='https://www.raspberrypi.org/'>
17+
<img src={rpf_logo} alt="Raspberry Pi Logo" />
18+
<span>Raspberry Pi Foundation</span>
19+
</a>
20+
<div className='editor-global-nav__account'>
21+
<Dropdown
22+
buttonImage={user ? user.profile.picture : user_logo}
23+
buttonImageAltText={user ? `${user.profile.name}'s account` : 'Account menu'}
24+
ButtonIcon={ChevronDown}
25+
MenuContent={LoginMenu}
26+
/>
27+
</div>
28+
</div>
29+
</div>
30+
)
31+
}
32+
33+
export default GlobalNav;
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
@import '../../colours.scss';
2+
3+
.editor-global-nav-wrapper {
4+
z-index: 2;
5+
}
6+
7+
.editor-global-nav {
8+
display: flex;
9+
align-items: center;
10+
background-color: $rpf-dark-blue;
11+
height: 100%;
12+
z-index: 2;
13+
14+
&__account {
15+
margin-left: auto;
16+
margin-right: var(--spacing-3);
17+
height: 100%;
18+
19+
svg {
20+
fill: $editor-white;
21+
margin: 0;
22+
}
23+
24+
img {
25+
height: var(--spacing-4);
26+
margin: var(--spacing-half) 0;
27+
}
28+
}
29+
30+
&__home {
31+
display: flex;
32+
align-items: center;
33+
margin-left: var(--spacing-8);
34+
padding: var(--spacing-1) 0;
35+
font-weight: var(--font-weight-bold);
36+
text-decoration: none;
37+
color: $editor-white;
38+
39+
span {
40+
margin-left: var(--spacing-2);
41+
}
42+
43+
&:visited {
44+
color: $editor-white;
45+
}
46+
}
47+
}
48+
49+
.font-size-medium {
50+
.editor-global-nav {
51+
&__account {
52+
img {
53+
margin: var(--spacing-1) var(--spacing-half);
54+
}
55+
}
56+
}
57+
}
58+
59+
.font-size-large {
60+
.editor-global-nav {
61+
&__account {
62+
img {
63+
margin: var(--spacing-1) var(--spacing-1);
64+
}
65+
}
66+
}
67+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import React from "react";
2+
import { render, screen } from "@testing-library/react"
3+
import { Provider } from 'react-redux';
4+
import configureStore from 'redux-mock-store';
5+
import GlobalNav from "./GlobalNav";
6+
7+
test('Global nav renders', () => {
8+
const middlewares = []
9+
const mockStore = configureStore(middlewares)
10+
const initialState = {
11+
auth: {}
12+
}
13+
const store = mockStore(initialState);
14+
render(<Provider store={store}><GlobalNav/></Provider>);
15+
expect(screen.queryByText("Raspberry Pi Foundation")).toBeInTheDocument()
16+
})
17+
18+
test('When not logged in renders generic profile image', () => {
19+
const middlewares = []
20+
const mockStore = configureStore(middlewares)
21+
const initialState = {
22+
auth: {}
23+
}
24+
const store = mockStore(initialState);
25+
render(<Provider store={store}><GlobalNav/></Provider>);
26+
expect(screen.queryByAltText(`Account menu`)).toHaveAttribute('src', 'unauthenticated_user.svg')
27+
})
28+
29+
test('When logged in renders user\'s profile image', () => {
30+
const name = "Joe Bloggs"
31+
const middlewares = []
32+
const mockStore = configureStore(middlewares)
33+
const initialState = {
34+
auth: {
35+
user: {
36+
profile: {
37+
name: name,
38+
picture: 'image_url'
39+
}
40+
}
41+
}
42+
}
43+
const store = mockStore(initialState);
44+
render(<Provider store={store}><GlobalNav/></Provider>);
45+
expect(screen.queryByAltText(`${name}'s account`)).toHaveAttribute('src', 'image_url')
46+
})

src/components/Header/Header.js

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import './Header.scss'
22
import { useSelector, connect, useDispatch } from 'react-redux'
33
import { toast } from 'react-toastify';
4-
import Login from '../Login/Login'
54
import Button from '../Button/Button';
65
import { SettingsIcon, SquaresIcon } from '../../Icons';
76
import { saveProject, updateProject } from '../../utils/apiCallHandler';
@@ -11,10 +10,11 @@ import Dropdown from '../Menus/Dropdown/Dropdown';
1110
import SettingsMenu from '../Menus/SettingsMenu/SettingsMenu';
1211
import ProjectName from './ProjectName';
1312

13+
import editor_logo from '../../assets/editor_logo.svg'
14+
1415

1516
const Header = (props) => {
1617
const { user } = props;
17-
const isEmbedded = useSelector((state) => state.editor.isEmbedded);
1818
const project = useSelector((state) => state.editor.project);
1919

2020
const dispatch = useDispatch();
@@ -41,15 +41,9 @@ const Header = (props) => {
4141
}
4242

4343
return (
44-
<>
45-
{ isEmbedded === false ? (
46-
<div className='main-container'>
47-
<Login user={user} />
48-
</div>
49-
): null }
5044
<div className='editor-header-wrapper'>
5145
<header className='editor-header'>
52-
<img className='editor-logo' src='/editor_logo.svg' alt='Editor logo'/>
46+
<img className='editor-logo' src={editor_logo} alt='Editor logo'/>
5347
{ user !== null ? (
5448
<a href='/projects' className='project-gallery-link'>
5549
{<><SquaresIcon />
@@ -69,7 +63,6 @@ const Header = (props) => {
6963
</div>
7064
</header>
7165
</div>
72-
</>
7366
)
7467
};
7568

0 commit comments

Comments
 (0)