Skip to content

Commit

Permalink
WIP categories
Browse files Browse the repository at this point in the history
  • Loading branch information
tofumatt committed Oct 21, 2016
1 parent c68bf95 commit 8ef6093
Show file tree
Hide file tree
Showing 35 changed files with 421 additions and 64 deletions.
Binary file modified .DS_Store
Binary file not shown.
Binary file modified assets/.DS_Store
Binary file not shown.
Binary file added assets/fonts/Tofino/Tofino Black.otf
Binary file not shown.
Binary file added assets/fonts/Tofino/Tofino Bold.otf
Binary file not shown.
Binary file added assets/fonts/Tofino/Tofino Book.otf
Binary file not shown.
Binary file added assets/fonts/Tofino/Tofino Light.otf
Binary file not shown.
Binary file added assets/fonts/Tofino/Tofino Medium.otf
Binary file not shown.
Binary file added assets/fonts/Tofino/Tofino Regular.otf
Binary file not shown.
Binary file added assets/fonts/Tofino/Tofino Thin.otf
Binary file not shown.
Binary file added assets/fonts/Tofino/Tofino Ultra.otf
Binary file not shown.
60 changes: 60 additions & 0 deletions src/amo/components/Categories.js
@@ -0,0 +1,60 @@
import React, { PropTypes } from 'react';

import translate from 'core/i18n/translate';
import CategoryLink from './CategoryLink';

import './Categories.scss';


export const CATEGORIES = {
firefox: {
dictionary: {
'general': 'General',
},
extensions: [
{ name: 'Alerts & Updates', slug: 'alerts-updates', },
{ name: 'Appearance', slug: 'appearance', },
{ name: 'Bookmarks', slug: 'bookmarks', },
{ name: 'Download Management', slug: 'download-management', },
{ name: 'Feeds, News & Blogging', slug: 'feeds-news-blogging', },
{ name: 'Games & Entertainment', slug: 'games-entertainment', },
],
search: {
'bookmarks': 'Bookmarks',
},
themes: {
'animals': 'Animals',
}
}
};

export class CategoriesBase extends React.Component {
static propTypes = {
i18n: PropTypes.object.isRequired,
}

render() {
const { application, i18n, lang } = this.props;

const categoriesHTML = (
<ul className="Categories-list" ref={(ref) => { this.categories = ref; }}>
{CATEGORIES.firefox.extensions.map((category) => {
return (
<li className="Categories-listItem">
<CategoryLink application={application} lang={lang}
{...category} />
</li>
);
})}
</ul>
);

return (
<div className="Categories">
{categoriesHTML}
</div>
);
}
}

export default translate({ withRef: true })(CategoriesBase);
21 changes: 21 additions & 0 deletions src/amo/components/Categories.scss
@@ -0,0 +1,21 @@
.Categories-list {
margin: 0;
padding: 0;
}

.Categories-listItem {
background: url('../img/icons/arrow.svg') 100% 50% no-repeat;
border-bottom: 2px solid #000;
display: block;
list-style: none;
margin: 0 auto;
padding: 0;
width: 95%;

a {
color: #000;
display: block;
padding: 1.1em 0 0.9em;
text-decoration: none;
}
}
41 changes: 41 additions & 0 deletions src/amo/components/CategoryInfo.js
@@ -0,0 +1,41 @@
import classNames from 'classnames';
import React, { PropTypes } from 'react';

// import translate from 'core/i18n/translate';
// import CategoryLink from './CategoryLink';
import { CATEGORIES } from './Categories';

import './CategoryInfo.scss';


export default class CategoryInfo extends React.Component {
static propTypes = {
slug: PropTypes.string.isRequired,
}

render() {
const { application, i18n, lang, slug, type } = this.props;

const match = CATEGORIES[application || 'firefox'][type || 'extensions'].filter((category) => {
return category.slug == slug;
});

if (!match) {
return null;
}

const category = match[0];

return (
<div className={classNames('CategoryInfo', category.slug)}>
<h2 className="CategoryInfo-header">{category.name}</h2>

<p className="CategoryInfo-description">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique nibh eget mi vestibulum, vel pulvinar elit.
</p>
</div>
);
}
}

// export default translate({ withRef: true })(CategoryInfoBase);
27 changes: 27 additions & 0 deletions src/amo/components/CategoryInfo.scss
@@ -0,0 +1,27 @@
@import "~core/css/inc/vars";

$padding-icon: 20px;

.CategoryInfo {
background: $padding-icon 50% no-repeat;
margin: 0 auto;
min-height: 72px;
padding: 0 0 0 ($padding-icon * 6);

&.alerts-updates {
background-image: url('../img/categories/alerts-updates.svg');
}
}

.CategoryInfo-header {
font-size: $font-size-default;
margin: $padding-icon 0 0;
padding: 0;
}

.CategoryInfo-description {
line-height: 1.2;
font-size: $font-size-s;
margin: 0;
max-width: 275px;
}
25 changes: 25 additions & 0 deletions src/amo/components/CategoryLink.js
@@ -0,0 +1,25 @@
import React, { PropTypes } from 'react';

import { Link } from 'react-router'


export class Category extends React.Component {
static propTypes = {
name: PropTypes.string.isRequired,
slug: PropTypes.string.isRequired,
}

render() {
const { application, lang, name, slug } = this.props;
const type = 'extension';

return (
<Link className="Category-link"
to={`/${lang}/${application}/search/?app=${application}&category=${slug}&type=${type}`}>
{name}
</Link>
);
}
}

export default Category;
16 changes: 11 additions & 5 deletions src/amo/components/MastHead.js
@@ -1,4 +1,5 @@
import React, { PropTypes } from 'react';
import { Link } from 'react-router';

import SearchForm from 'amo/components/SearchForm';
import translate from 'core/i18n/translate';
Expand All @@ -9,6 +10,7 @@ import './MastHead.scss';

export class MastHeadBase extends React.Component {
static propTypes = {
application: PropTypes.string.isRequired,
children: PropTypes.node,
i18n: PropTypes.object.isRequired,
isHomePage: PropTypes.bool,
Expand All @@ -23,10 +25,10 @@ export class MastHeadBase extends React.Component {
}

render() {
const { SearchFormComponent, children, i18n, isHomePage, lang,
const { SearchFormComponent, application, children, i18n, isHomePage, lang,
query } = this.props;
const headerTitle = i18n.gettext('Firefox Add-ons');
const pathname = `/${lang}/firefox/search/`;
const pathname = `/${lang}/${application}/search/`;

return (
<div className="MastHead">
Expand All @@ -38,10 +40,14 @@ export class MastHeadBase extends React.Component {
{ isHomePage ?
<h1 ref={(ref) => { this.title = ref; }}
className="MastHead-title MastHead-homepage">
{ headerTitle }
{headerTitle}
</h1> :
<a ref={(ref) => { this.title = ref; }}
href="/" className="MastHead-title">{ headerTitle }</a> }
<Link ref={(ref) => { this.title = ref; }}
to={`/${lang}/${application}/`}
className="MastHead-title">
{headerTitle}
</Link>
}
</header>
<SearchFormComponent pathname={pathname} query={query} />
</div>
Expand Down
12 changes: 9 additions & 3 deletions src/amo/components/SearchPage.js
Expand Up @@ -3,11 +3,14 @@ import React, { PropTypes } from 'react';
import Paginate from 'core/components/Paginate';
import SearchResults from 'core/components/Search/SearchResults';

import CategoryInfo from './CategoryInfo';
import SearchResult from './SearchResult';


export default class SearchPage extends React.Component {
static propTypes = {
application: PropTypes.string.isRequired,
category: PropTypes.string,
count: PropTypes.number,
lang: PropTypes.string.isRequired,
loading: PropTypes.bool.isRequired,
Expand All @@ -17,15 +20,18 @@ export default class SearchPage extends React.Component {
}

render() {
const { count, lang, loading, page, query, results } = this.props;
const pathname = `/${lang}/firefox/search/`;
const { application, category, count, lang, loading, page, query,
results } = this.props;
const pathname = `/${lang}/${application}/search/`;
const paginator = query && count > 0 ?
<Paginate count={count} pathname={pathname} query={{ q: query }}
currentPage={page} showPages={0} /> : [];
return (
<div className="search-page">
<SearchResults results={results} query={query} loading={loading}
count={count} ResultComponent={SearchResult} lang={lang} />
application={application} category={category} count={count}
lang={lang} CategoryInfoComponent={CategoryInfo}
ResultComponent={SearchResult} />
{paginator}
</div>
);
Expand Down
10 changes: 10 additions & 0 deletions src/amo/constants.js
@@ -1,2 +1,12 @@
// Action types.
export const SET_REVIEW = 'SET_REVIEW';
export const SET_USER_RATING = 'SET_USER_RATING';

// Search params that are only added to actions/reducers if set; many of
// these are optional.
export const OPTIONAL_SEARCH_PARAMS = [
'app',
'addonType',
'category',
'query',
];
9 changes: 7 additions & 2 deletions src/amo/containers/App.js
Expand Up @@ -3,6 +3,7 @@
import React, { PropTypes } from 'react';
import Helmet from 'react-helmet';
import { connect } from 'react-redux';
import { Link as ReactRouterLink } from 'react-router';
import { compose } from 'redux';

import 'core/fonts/fira.scss';
Expand All @@ -18,6 +19,7 @@ export class AppBase extends React.Component {
static propTypes = {
FooterComponent: PropTypes.node.isRequired,
MastHeadComponent: PropTypes.node.isRequired,
application: PropTypes.string.isRequired,
children: PropTypes.node,
handleLogIn: PropTypes.func.isRequired,
i18n: PropTypes.object.isRequired,
Expand Down Expand Up @@ -46,6 +48,7 @@ export class AppBase extends React.Component {
const {
FooterComponent,
MastHeadComponent,
application,
children,
i18n,
lang,
Expand All @@ -56,7 +59,7 @@ export class AppBase extends React.Component {
<div className="amo">
<Helmet defaultTitle={i18n.gettext('Add-ons for Firefox')} />
<MastHeadComponent SearchFormComponent={SearchForm} lang={lang}
query={query}>
application={application} query={query}>
{this.accountButton()}
</MastHeadComponent>
<div className="App-content">
Expand All @@ -68,8 +71,10 @@ export class AppBase extends React.Component {
}
}

export const setupMapStateToProps = (_window) => (state) => ({
export const setupMapStateToProps = (_window) => (state, ownProps) => ({
application: ownProps.params.application,
lang: state.api.lang,
params: state.params,
isAuthenticated: !!state.auth.token,
handleLogIn(location) {
// eslint-disable-next-line no-param-reassign
Expand Down
21 changes: 21 additions & 0 deletions src/amo/containers/CategoriesPage.js
@@ -0,0 +1,21 @@
import React from 'react';
import { compose } from 'redux';
import { asyncConnect } from 'redux-connect';
import { connect } from 'react-redux';

import Categories from 'amo/components/Categories';
import translate from 'core/i18n/translate';


export default class CategoriesPage extends React.Component {

render() {
const { application, lang } = this.props.params;

return (
<div className="Categories-Page">
<Categories application={application} lang={lang} />
</div>
);
}
}
22 changes: 19 additions & 3 deletions src/amo/containers/Home.js
@@ -1,11 +1,27 @@
import React from 'react';
import React, { PropTypes } from 'react';
import { Link } from 'react-router';

import translate from 'core/i18n/translate';


export class HomeBase extends React.Component {
static props = {
i18n: PropTypes.object.isRequired,
}

export default class Home extends React.Component {
render() {
const { i18n } = this.props;
const { application, lang } = this.props.params;

return (
<div>
<h1>AMO Home Page Hello World</h1>
<Link to={`/${lang}/${application}/categories/`}
ref={(ref) => { this.categoryLink = ref; }}>
{i18n.gettext('Categories')}
</Link>
</div>
);
}
}

export default translate({ withRef: true })(HomeBase);
13 changes: 13 additions & 0 deletions src/amo/img/categories/alerts-updates.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 8ef6093

Please sign in to comment.