Skip to content

Commit 4fa7a54

Browse files
authored
NavBar for admin pages (fixes #807) (#932)
1 parent a987972 commit 4fa7a54

File tree

10 files changed

+146
-8
lines changed

10 files changed

+146
-8
lines changed

src/core/components/NavBar/index.js

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import React, { PropTypes } from 'react';
2+
import { Link } from 'react-router';
3+
4+
import 'core/components/NavBar/styles.scss';
5+
6+
export const NavBarItem = ({ children }) => <span className="NavBarItem">{children}</span>;
7+
NavBarItem.propTypes = {
8+
children: PropTypes.node,
9+
};
10+
11+
export const NavBarLink = ({ children, ...props }) => (
12+
<NavBarItem>
13+
<Link {...props} className="NavBarLink">{children}</Link>
14+
</NavBarItem>
15+
);
16+
NavBarLink.propTypes = {
17+
children: PropTypes.node,
18+
};
19+
20+
export const NavBar = ({ children }) => <div className="NavBar">{children}</div>;
21+
NavBar.propTypes = {
22+
children: PropTypes.node,
23+
};
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
.NavBar {
2+
align-items: center;
3+
justify-content: space-between;
4+
background: #444;
5+
display: flex;
6+
height: 50px;
7+
padding: 0 10px;
8+
width: 100%;
9+
}
10+
11+
.NavBarLink {
12+
background: transparent;
13+
border: none;
14+
border-radius: 4px;
15+
color: #fff;
16+
display: block;
17+
height: 30px;
18+
line-height: 30px;
19+
min-width: 50px;
20+
padding: 0 10px;
21+
text-decoration: none;
22+
transition: 200ms;
23+
24+
&:hover {
25+
background-color: #fff;
26+
color: #444;
27+
cursor: pointer;
28+
}
29+
}

src/search/components/NavBar/index.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React, { PropTypes } from 'react';
2+
3+
import {
4+
NavBar,
5+
NavBarLink,
6+
} from 'core/components/NavBar';
7+
8+
const SearchNavBar = () => (
9+
<NavBar>
10+
<NavBarLink to="/search">Search</NavBarLink>
11+
</NavBar>
12+
);
13+
SearchNavBar.propTypes = {
14+
logout: PropTypes.func.isRequired,
15+
};
16+
17+
export default SearchNavBar;

src/search/containers/App.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import React, { PropTypes } from 'react';
22
import Helmet from 'react-helmet';
33

4-
import 'search/css/App.scss';
54
import { gettext as _ } from 'core/utils';
5+
import NavBar from 'search/components/NavBar';
66

7+
import 'search/css/App.scss';
78

89
export default class App extends React.Component {
910
static propTypes = {
@@ -17,7 +18,10 @@ export default class App extends React.Component {
1718
<Helmet
1819
defaultTitle={_('Add-ons Search')}
1920
/>
20-
{children}
21+
<NavBar />
22+
<div className="App">
23+
{children}
24+
</div>
2125
</div>
2226
);
2327
}

src/search/containers/CurrentSearchPage.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@ import { search } from 'core/api';
55
import SearchPage from 'search/components/SearchPage';
66
import { searchStart, searchLoad, searchFail } from 'search/actions';
77

8-
export function mapStateToProps(state) {
9-
return state.search;
8+
export function mapStateToProps(state, ownProps) {
9+
const { location } = ownProps;
10+
if (location.query.q === state.search.query) {
11+
return state.search;
12+
}
13+
return {};
1014
}
1115

1216
function performSearch({ dispatch, page, query, api }) {

src/search/css/App.scss

+4-1
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44

55
body {
66
background: $body-color;
7-
margin: 20px;
87
color: $text-color-default;
98
}
109

10+
.App {
11+
margin: 20px;
12+
}
13+
1114
* {
1215
box-sizing: border-box;
1316
}
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import React from 'react';
2+
import { Link } from 'react-router';
3+
4+
import { shallowRender } from 'tests/client/helpers';
5+
import { NavBar, NavBarItem, NavBarLink } from 'core/components/NavBar';
6+
7+
describe('<NavBarItem />', () => {
8+
it('wraps its children in a span', () => {
9+
const root = shallowRender(<NavBarItem>Foo</NavBarItem>);
10+
assert.equal(root.props.className, 'NavBarItem');
11+
assert.equal(root.type, 'span');
12+
assert.equal(root.props.children, 'Foo');
13+
});
14+
});
15+
16+
describe('<NavBarLink />', () => {
17+
it('wraps its children in a span', () => {
18+
const root = shallowRender(<NavBarLink to="/bar">Baileys Taproom</NavBarLink>);
19+
assert.equal(root.type, NavBarItem);
20+
assert.equal(root.props.children.type, Link);
21+
assert.equal(root.props.children.props.to, '/bar');
22+
assert.equal(root.props.children.props.children, 'Baileys Taproom');
23+
});
24+
});
25+
26+
describe('<NavBar />', () => {
27+
it('wraps its children in a div', () => {
28+
const root = shallowRender(<NavBar>Navigate places!</NavBar>);
29+
assert.equal(root.type, 'div');
30+
assert.equal(root.props.className, 'NavBar');
31+
assert.equal(root.props.children, 'Navigate places!');
32+
});
33+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import React from 'react';
2+
3+
import { NavBar, NavBarLink } from 'core/components/NavBar';
4+
import SearchNavBar from 'search/components/NavBar';
5+
import { shallowRender } from 'tests/client/helpers';
6+
7+
describe('<SearchNavBar />', () => {
8+
it('renders a link to Search', () => {
9+
const root = shallowRender(<SearchNavBar />);
10+
assert.equal(root.type, NavBar);
11+
const link = root.props.children;
12+
assert.equal(link.type, NavBarLink);
13+
assert.equal(link.props.to, '/search');
14+
assert.equal(link.props.children, 'Search');
15+
});
16+
});

tests/client/search/containers/TestApp.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ describe('App', () => {
1313
const root = shallowRender(<App><MyComponent /></App>);
1414
assert.equal(root.type, 'div');
1515
// First child is <Helmet />.
16-
assert.equal(root.props.children[1].type, MyComponent);
16+
// Second child is <NavBar />.
17+
// Third child is the <div className="App"> wrapper.
18+
const wrapper = root.props.children[2];
19+
assert.equal(wrapper.props.className, 'App');
20+
assert.equal(wrapper.props.children.type, MyComponent);
1721
});
1822
});

tests/client/search/containers/TestCurrentSearchPage.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,16 @@ describe('CurrentSearchPage.mapStateToProps()', () => {
1313
cd: { slug: 'cd', name: 'cd-block' } },
1414
search: { query: 'ad-block', loading: false, results: [{ slug: 'ab', name: 'ad-block' }] },
1515
};
16-
const props = mapStateToProps(state);
1716

18-
it('passes the search state', () => {
17+
it('passes the search state if the URL and state query matches', () => {
18+
const props = mapStateToProps(state, { location: { query: { q: 'ad-block' } } });
1919
assert.strictEqual(props, state.search);
2020
});
21+
22+
it('does not pass search state if the URL and state query do not match', () => {
23+
const props = mapStateToProps(state, { location: { query: { q: 'more-ads' } } });
24+
assert.deepEqual(props, {});
25+
});
2126
});
2227

2328
describe('CurrentSearchPage.isLoaded()', () => {

0 commit comments

Comments
 (0)