diff --git a/src/amo/components/AddonDetail.js b/src/amo/components/AddonDetail.js new file mode 100644 index 00000000000..18f0ef96f75 --- /dev/null +++ b/src/amo/components/AddonDetail.js @@ -0,0 +1,74 @@ +import React, { PropTypes } from 'react'; + +import translate from 'core/i18n/translate'; + +import AddonMeta from 'amo/components/AddonMeta'; +import InstallButton from 'disco/components/InstallButton'; +import LikeButton from 'amo/components/LikeButton'; +import ScreenShots from 'amo/components/ScreenShots'; +import SearchBox from 'amo/components/SearchBox'; + + +import 'amo/css/AddonDetail.scss'; + +export class AddonDetail extends React.Component { + static propTypes = { + i18n: PropTypes.object, + } + + render() { + const { i18n } = this.props; + + return ( +
+
+ +
+ + +
+
+

Placeholder Add-on Title + by AwesomeAddons

+ +
+
+

Lorem ipsum dolor sit amet, dicat graece partiendo cu usu. + Vis recusabo accusamus et.

+
+
+ +
+

{i18n.gettext('Extension Metadata')}

+ +
+ +
+ +
+

{i18n.gettext('Screenshots')}

+ +
+ +
+ +
+

{i18n.gettext('About this extension')}

+

Lorem ipsum dolor sit amet, dicat graece partiendo cu usu. Vis + recusabo accusamus et, vitae scriptorem in vel. Sed ei eleifend + molestiae deseruisse, sit mucius noster mentitum ex. Eu pro illum + iusto nemore, te legere antiopam sit. Suas simul ad usu, ex putent + timeam fierent eum. Dicam equidem cum cu. Vel ea vidit timeam.

+ +

Eu nam dicant oportere, et per habeo euismod denique, te appetere + temporibus mea. Ad solum reprehendunt vis, sea eros accusata senserit + an, eam utinam theophrastus in. Debet consul vis ex. Mei an iusto + delicatissimi, ut timeam electram maiestatis nam, te petentium + intellegebat ius. Ei legere everti.

+
+
+ ); + } +} + +export default translate({ withRef: true })(AddonDetail); diff --git a/src/amo/components/AddonMeta.js b/src/amo/components/AddonMeta.js new file mode 100644 index 00000000000..dab33fd798e --- /dev/null +++ b/src/amo/components/AddonMeta.js @@ -0,0 +1,37 @@ +import React, { PropTypes } from 'react'; + +import translate from 'core/i18n/translate'; + +import 'amo/css/AddonMeta.scss'; + +export class AddonMeta extends React.Component { + static propTypes = { + i18n: PropTypes.object, + } + + render() { + const { i18n } = this.props; + + // This is just a placeholder. + return ( +
+
+

{i18n.gettext('Category')}

+

Security & Privacy

+
+ +
+

{i18n.gettext('Used by')}

+

1,342 users

+
+ +
+

{i18n.gettext('Sentiment')}

+

89% love it!

+
+
+ ); + } +} + +export default translate({ withRef: true })(AddonMeta); diff --git a/src/amo/components/LikeButton.js b/src/amo/components/LikeButton.js new file mode 100644 index 00000000000..9583b390c5e --- /dev/null +++ b/src/amo/components/LikeButton.js @@ -0,0 +1,24 @@ +import React, { PropTypes } from 'react'; + +import translate from 'core/i18n/translate'; + +import 'amo/css/LikeButton.scss'; + +export class LikeButton extends React.Component { + static propTypes = { + i18n: PropTypes.object, + } + + render() { + const { i18n } = this.props; + + // This is just a placeholder. + return ( + + ); + } +} + +export default translate({ withRef: true })(LikeButton); diff --git a/src/amo/components/ScreenShots.js b/src/amo/components/ScreenShots.js new file mode 100644 index 00000000000..cf8090ecf42 --- /dev/null +++ b/src/amo/components/ScreenShots.js @@ -0,0 +1,39 @@ +import React, { PropTypes } from 'react'; + +import translate from 'core/i18n/translate'; + +import 'amo/css/ScreenShots.scss'; + + +export class ScreenShots extends React.Component { + static propTypes = { + i18n: PropTypes.object, + } + + render() { + const { i18n } = this.props; + const totalImages = 5; + + // This is just a placeholder. + return ( +
+ +
    + {[...Array(totalImages)].map((x, i) => + (
  1. + + + {i18n.sprintf(i18n.gettext( + 'screenshot %(imageNumber)s of %(totalImages)s' + ), { imageNumber: i + 1, totalImages })} + + +
  2. ) + )} +
+
+ ); + } +} + +export default translate({ withRef: true })(ScreenShots); diff --git a/src/amo/components/SearchBox.js b/src/amo/components/SearchBox.js new file mode 100644 index 00000000000..c63c4c9c0b9 --- /dev/null +++ b/src/amo/components/SearchBox.js @@ -0,0 +1,25 @@ +import React, { PropTypes } from 'react'; + +import translate from 'core/i18n/translate'; + +import 'amo/css/SearchBox.scss'; + + +export class SearchBox extends React.Component { + static propTypes = { + i18n: PropTypes.object, + } + + render() { + const { i18n } = this.props; + + // This is just a placeholder. + return ( +
+ {i18n.gettext('Search')} +
+ ); + } +} + +export default translate({ withRef: true })(SearchBox); diff --git a/src/amo/containers/DetailPage.js b/src/amo/containers/DetailPage.js index 81dc7356e8d..239c349ba7f 100644 --- a/src/amo/containers/DetailPage.js +++ b/src/amo/containers/DetailPage.js @@ -1,10 +1,12 @@ import React from 'react'; +import AddonDetail from 'amo/components/AddonDetail'; + export default class DetailPage extends React.Component { render() { return (
-

Detail Page

+
); } diff --git a/src/amo/css/AddonDetail.scss b/src/amo/css/AddonDetail.scss new file mode 100644 index 00000000000..a907997a5cf --- /dev/null +++ b/src/amo/css/AddonDetail.scss @@ -0,0 +1,74 @@ +@import "~amo/css/inc/vars"; + +.AddonDetail { + header { + background: $header-background; + color: $header-font-color; + } + + hr { + margin: 0 20px; + } + + .icon, + .title, + .description, + .screenshots { + overflow: hidden; + padding: 20px; + } + + .icon { + align-items: center; + display: flex; + flex-direction: row; + + img { + background: #fff; + border-radius: 5px; + display: inline-block; + height: 64px; + width: 64px; + } + + .LikeButton { + margin-left: auto; + } + } + + .title { + background: darken($header-background, 3%); + + h1 { + margin-top: 0; + color: $header-font-color; + } + } + + .author { + display: block; + font-size: 20px; + + a { + color: $header-font-color; + font-size: 20px; + } + } + + .switch { + float: right; + } + + .description { + background: darken($header-background, 5.5%); + font-size: 18px; + + p:first-child { + margin-top: 0; + } + } + + .about { + padding: 20px; + } +} diff --git a/src/amo/css/AddonMeta.scss b/src/amo/css/AddonMeta.scss new file mode 100644 index 00000000000..8dfa53dd705 --- /dev/null +++ b/src/amo/css/AddonMeta.scss @@ -0,0 +1,34 @@ +.AddonMeta { + font-size: 10px; + padding: 20px; + + .category, + .sentiment, + .users { + display: inline-block; + margin: 5px; + padding-top: 40px; + text-align: center; + vertical-align: top; + width: 60px; + } + + p { + display: inline-block; + line-height: 14px; + max-width: 45px; + text-align: center; + } + + .category.security { + background: url(../img/categories/security.svg) no-repeat 50% 5px; + } + + .users { + background: url(../img/icons/users.svg) no-repeat 50% 5px; + } + + .sentiment.love-it { + background: url(../img/ratings/love-it-mono.svg) no-repeat 50% 5px; + } +} diff --git a/src/amo/css/App.scss b/src/amo/css/App.scss index 3989133652e..d34dabfa53e 100644 --- a/src/amo/css/App.scss +++ b/src/amo/css/App.scss @@ -1,9 +1,83 @@ @import "~normalize.css"; @import "~core/css/inc/lib"; @import "~core/css/inc/mixins"; +@import "~amo/css/inc/vars"; +@import "~amo/css/inc/mixins"; html, body { background: #fff; - font-family: "Fira Sans", sans-serif; +} + +html { + box-sizing: border-box; +} + +*, +*::before, +*::after { + box-sizing: inherit; +} + +body { + @include font-regular(); + font-size: 14px; + color: $body-font-color; + line-height: 19px; + padding-top: 50px; +} + +h1, +h2 { + @include font-bold(); + color: $primary-font-color; + font-weight: 600; +} + +h1 { + font-size: 32px; + line-height: 32px; +} + +h2 { + font-size: 12px; + color: $primary-font-color; +} + +h3 { + @include font-bold(); + font-size: 14px; + color: $primary-font-color; + line-height: 19px; + +} + +a:link { + @include font-medium(); + font-size: 14px; + color: #0996f8; +} + +em { + @include font-regular-italic(); +} + +strong { + @include font-bold(); +} + +.caption { + font-size: 10px; + color: $primary-font-color; + line-height: 11px; +} + +.date-time { + font-size: 14px; + color: #b1b1b1; +} + +button { + background: none; + border: none; } diff --git a/src/amo/css/LikeButton.scss b/src/amo/css/LikeButton.scss new file mode 100644 index 00000000000..054e0a9cc6e --- /dev/null +++ b/src/amo/css/LikeButton.scss @@ -0,0 +1,6 @@ +.LikeButton { + height: 40px; + width: 40px; + background: transparent url('../img/icons/like.svg') no-repeat 50% 50%; + float: right; +} diff --git a/src/amo/css/ScreenShots.scss b/src/amo/css/ScreenShots.scss new file mode 100644 index 00000000000..319ccf29e2d --- /dev/null +++ b/src/amo/css/ScreenShots.scss @@ -0,0 +1,41 @@ +.ScreenShots { + ol { + align-items: center; + display: flex; + justify-content: center; + list-style-type: none; + padding: 0; + width: 100%; + } + + li { + display: inline-block; + } + + li a { + display: inline-block; + padding: 5px; + + &::before { + background: #000; + border-radius: 50%; + content: ''; + display: block; + flex-direction: row; + height: 12px; + width: 12px; + } + + &.active::before { + border: 3px solid #000; + background: #fff; + } + } + + img { + width: 100%; + padding-bottom: 75%; + background: #ff211e; + display: block; + } +} diff --git a/src/amo/css/SearchBox.scss b/src/amo/css/SearchBox.scss new file mode 100644 index 00000000000..b1ea24f925d --- /dev/null +++ b/src/amo/css/SearchBox.scss @@ -0,0 +1,10 @@ +.SearchBox { + height: 50px; + position: fixed; + top: 0; + left: 0; + right: 0; + width: 100%; + background: #000 url('../img/icons/search.svg') no-repeat 98% 50%; + z-index: 20; +} diff --git a/src/amo/css/inc/mixins.scss b/src/amo/css/inc/mixins.scss new file mode 100644 index 00000000000..5b8d801aed5 --- /dev/null +++ b/src/amo/css/inc/mixins.scss @@ -0,0 +1,47 @@ +@mixin font-light() { + font-family: 'Fira Sans', sans-serif; + font-weight: 300; + font-style: normal; +} + +@mixin font-light-italic() { + font-family: 'Fira Sans', sans-serif; + font-weight: 300; + font-style: normal; +} + +@mixin font-regular() { + font-family: 'Fira Sans', sans-serif; + font-weight: 400; + font-style: normal; +} + +@mixin font-regular-italic() { + font-family: 'Fira Sans', sans-serif; + font-weight: 400; + font-style: italic; +} + +@mixin font-medium() { + font-family: 'Fira Sans', sans-serif; + font-weight: 500; + font-style: normal; +} + +@mixin font-medium-italic() { + font-family: 'Fira Sans', sans-serif; + font-weight: 500; + font-style: italic; +} + +@mixin font-bold() { + font-family: 'Fira Sans', sans-serif; + font-weight: 600; + font-style: normal; +} + +@mixin font-bold-italic() { + font-family: 'Fira Sans', sans-serif; + font-weight: 600; + font-style: italic; +} diff --git a/src/amo/css/inc/vars.scss b/src/amo/css/inc/vars.scss new file mode 100644 index 00000000000..426824a173a --- /dev/null +++ b/src/amo/css/inc/vars.scss @@ -0,0 +1,4 @@ +$primary-font-color: #000; +$body-font-color: #1d1d1d; +$header-font-color: #fff; +$header-background: #1f386e; diff --git a/src/amo/img/categories/security.svg b/src/amo/img/categories/security.svg new file mode 100644 index 00000000000..89152f89ec9 --- /dev/null +++ b/src/amo/img/categories/security.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/amo/img/icons/like.svg b/src/amo/img/icons/like.svg new file mode 100644 index 00000000000..a7926ab76a7 --- /dev/null +++ b/src/amo/img/icons/like.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/amo/img/icons/search.svg b/src/amo/img/icons/search.svg new file mode 100644 index 00000000000..34bbaa4d107 --- /dev/null +++ b/src/amo/img/icons/search.svg @@ -0,0 +1,9 @@ + + + Created with Sketch. + + + + + + diff --git a/src/amo/img/icons/users.svg b/src/amo/img/icons/users.svg new file mode 100644 index 00000000000..70bb0a9d2c1 --- /dev/null +++ b/src/amo/img/icons/users.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/amo/img/ratings/love-it-mono.svg b/src/amo/img/ratings/love-it-mono.svg new file mode 100644 index 00000000000..a72407b5c25 --- /dev/null +++ b/src/amo/img/ratings/love-it-mono.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tests/client/amo/containers/TestDetail.js b/tests/client/amo/containers/TestDetail.js index a97fa551060..837b9311a91 100644 --- a/tests/client/amo/containers/TestDetail.js +++ b/tests/client/amo/containers/TestDetail.js @@ -1,16 +1,30 @@ import React from 'react'; import { findDOMNode } from 'react-dom'; import { + findRenderedComponentWithType, renderIntoDocument, } from 'react-addons-test-utils'; +import { getFakeI18nInst } from 'tests/client/helpers'; import DetailPage from 'amo/containers/DetailPage'; +import I18nProvider from 'core/i18n/Provider'; +import translate from 'core/i18n/translate'; + +function renderDetailPage({ ...props }) { + const MyDetailPage = translate({ withRef: true })(DetailPage); + + return findRenderedComponentWithType(renderIntoDocument( + + + + ), MyDetailPage).getWrappedInstance(); +} describe('DetailPage', () => { it('renders a heading', () => { - const root = renderIntoDocument(); + const root = renderDetailPage(); const rootNode = findDOMNode(root); - assert.include(rootNode.querySelector('h1').textContent, 'Detail Page'); + assert.include(rootNode.querySelector('h1').textContent, 'Placeholder Add-on'); }); });