Bug 1635278 - Adding video playhead treatment r=thecount
authorGavin Lazar Suntop <gavin@gsuntop.com>
Wed, 20 May 2020 17:35:30 +0000
changeset 531295 d3447a07c59055c21b468f6c035726a09501592f
parent 531294 1535739232489fc7ecef080a84e3e8ce75d11942
child 531296 9c6d6a6348256a1e350b08751bcbad7c75440ce2
push id37436
push userncsoregi@mozilla.com
push dateWed, 20 May 2020 21:30:50 +0000
treeherdermozilla-central@6c10970490f3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersthecount
bugs1635278
milestone78.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1635278 - Adding video playhead treatment r=thecount Differential Revision: https://phabricator.services.mozilla.com/D76163
browser/components/newtab/content-src/components/DiscoveryStreamBase/DiscoveryStreamBase.jsx
browser/components/newtab/content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid.jsx
browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx
browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/_DSCard.scss
browser/components/newtab/content-src/styles/_variables.scss
browser/components/newtab/css/activity-stream-linux.css
browser/components/newtab/css/activity-stream-mac.css
browser/components/newtab/css/activity-stream-windows.css
browser/components/newtab/data/content/activity-stream.bundle.js
browser/components/newtab/data/content/assets/glyph-playhead.svg
--- a/browser/components/newtab/content-src/components/DiscoveryStreamBase/DiscoveryStreamBase.jsx
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamBase/DiscoveryStreamBase.jsx
@@ -188,16 +188,19 @@ export class _DiscoveryStreamBase extend
             display_engagement_labels={ENGAGEMENT_LABEL_ENABLED}
             dismissible={this.props.DiscoveryStream.isCollectionDismissible}
             dispatch={this.props.dispatch}
           />
         );
       case "CardGrid":
         return (
           <CardGrid
+            enable_video_playheads={
+              !!component.properties.enable_video_playheads
+            }
             title={component.header && component.header.title}
             display_variant={component.properties.display_variant}
             data={component.data}
             feed={component.feed}
             border={component.properties.border}
             type={component.type}
             dispatch={this.props.dispatch}
             items={component.properties.items}
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid.jsx
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid.jsx
@@ -35,16 +35,17 @@ export class CardGrid extends React.Pure
             source={rec.domain}
             pocket_id={rec.pocket_id}
             context_type={rec.context_type}
             bookmarkGuid={rec.bookmarkGuid}
             engagement={rec.engagement}
             display_engagement_labels={this.props.display_engagement_labels}
             cta={rec.cta}
             cta_variant={this.props.cta_variant}
+            is_video={this.props.enable_video_playheads && rec.is_video}
           />
         )
       );
     }
 
     // Used for CSS overrides to default styling (eg: "hero")
     const variantClass = this.props.display_variant
       ? `ds-card-grid-${this.props.display_variant}`
@@ -94,9 +95,10 @@ export class CardGrid extends React.Pure
       </div>
     );
   }
 }
 
 CardGrid.defaultProps = {
   border: `border`,
   items: 4, // Number of stories to display
+  enable_video_playheads: false,
 };
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx
@@ -211,32 +211,38 @@ export class _DSCard extends React.PureC
 
   render() {
     if (this.props.placeholder || !this.state.isSeen) {
       return (
         <div className="ds-card placeholder" ref={this.setPlaceholderRef} />
       );
     }
     const isButtonCTA = this.props.cta_variant === "button";
+    const baseClass = `ds-card ${this.props.is_video ? `video-card` : ``}`;
 
     return (
-      <div className="ds-card">
+      <div className={baseClass}>
         <SafeAnchor
           className="ds-card-link"
           dispatch={this.props.dispatch}
           onLinkClick={!this.props.placeholder ? this.onLinkClick : undefined}
           url={this.props.url}
         >
           <div className="img-wrapper">
             <DSImage
               extraClassNames="img"
               source={this.props.image_src}
               rawSource={this.props.raw_image_src}
               sizes={this.dsImageSizes}
             />
+            {this.props.is_video && (
+              <div className="playhead">
+                <span>Video Content</span>
+              </div>
+            )}
           </div>
           {isButtonCTA ? (
             <CTAButtonMeta
               display_engagement_labels={this.props.display_engagement_labels}
               source={this.props.source}
               title={this.props.title}
               excerpt={this.props.excerpt}
               context={this.props.context}
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/_DSCard.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/_DSCard.scss
@@ -4,25 +4,59 @@
 $excerpt-font-size: 14;
 $excerpt-line-height: 20;
 
 .ds-card {
   display: flex;
   flex-direction: column;
   position: relative;
 
+  .playhead {
+    background: $blue-60 url('../data/content/assets/glyph-playhead.svg') no-repeat 12px center;
+    border-radius: 20px;
+    bottom: -16px;
+    color: $white-0;
+    display: flex;
+    flex-direction: column;
+    height: 40px;
+    justify-content: center;
+    left: 16px;
+    min-width: 40px;
+    padding: 0 0 0 40px;
+    position: absolute;
+    transition: padding 100ms ease-in-out 0ms, color 100ms linear 100ms;
+    z-index: 1;
+
+    &:hover {
+      color: $white-100;
+      padding: 0 20px 0 40px;
+    }
+
+    span {
+      display: none;
+    }
+
+    &:hover span {
+      display: inline;
+      font-style: normal;
+      font-weight: 600;
+      font-size: 13px;
+    }
+  }
+
   &.placeholder {
     background: transparent;
     box-shadow: inset $inner-box-shadow;
     border-radius: 4px;
     min-height: 300px;
   }
 
   .img-wrapper {
     width: 100%;
+    position: relative;
   }
 
   .img {
     height: 0;
     padding-top: 50%; // 2:1 aspect ratio
 
     img {
       border-radius: 4px;
@@ -79,16 +113,20 @@
           color: $blue-50;
         }
 
         color: $blue-70;
       }
     }
   }
 
+  &.video-card .meta {
+    margin-top: 4px;
+  }
+
   .meta {
     display: flex;
     flex-direction: column;
     padding: 12px 16px;
     flex-grow: 1;
 
     .info-wrap {
       flex-grow: 1;
--- a/browser/components/newtab/content-src/styles/_variables.scss
+++ b/browser/components/newtab/content-src/styles/_variables.scss
@@ -61,20 +61,22 @@
 $black-12: rgba($black, 0.12);
 $black-15: rgba($black, 0.15);
 $black-20: rgba($black, 0.2);
 $black-25: rgba($black, 0.25);
 $black-30: rgba($black, 0.3);
 
 // Other colors
 $white: #FFF;
+$white-0: rgba($white, 0);
 $white-10: rgba($white, 0.1);
 $white-50: rgba($white, 0.5);
 $white-60: rgba($white, 0.6);
 $white-70: rgba($white, 0.7);
+$white-100: rgba($white, 1);
 $ghost-white: #FAFAFC;
 $pocket-teal: #50BCB6;
 $pocket-red: #EF4056;
 $shadow-10: rgba(12, 12, 13, 0.1);
 $bookmark-icon-fill: #0A84FF;
 $download-icon-fill: #12BC00;
 $pocket-icon-fill: #D70022;
 $email-input-focus: rgba($blue-50, 0.3);
--- a/browser/components/newtab/css/activity-stream-linux.css
+++ b/browser/components/newtab/css/activity-stream-linux.css
@@ -2759,23 +2759,49 @@ main {
   .ds-signup:-moz-any(:hover, :focus, .active).ds-card-grid-border {
     box-shadow: 0 0 0 5px var(--newtab-card-active-outline-color);
     transition: box-shadow 150ms; }
 
 .ds-card {
   display: flex;
   flex-direction: column;
   position: relative; }
+  .ds-card .playhead {
+    background: #0060DF url("../data/content/assets/glyph-playhead.svg") no-repeat 12px center;
+    border-radius: 20px;
+    bottom: -16px;
+    color: rgba(255, 255, 255, 0);
+    display: flex;
+    flex-direction: column;
+    height: 40px;
+    justify-content: center;
+    left: 16px;
+    min-width: 40px;
+    padding: 0 0 0 40px;
+    position: absolute;
+    transition: padding 100ms ease-in-out 0ms, color 100ms linear 100ms;
+    z-index: 1; }
+    .ds-card .playhead:hover {
+      color: white;
+      padding: 0 20px 0 40px; }
+    .ds-card .playhead span {
+      display: none; }
+    .ds-card .playhead:hover span {
+      display: inline;
+      font-style: normal;
+      font-weight: 600;
+      font-size: 13px; }
   .ds-card.placeholder {
     background: transparent;
     box-shadow: inset 0 0 0 1px var(--newtab-inner-box-shadow-color);
     border-radius: 4px;
     min-height: 300px; }
   .ds-card .img-wrapper {
-    width: 100%; }
+    width: 100%;
+    position: relative; }
   .ds-card .img {
     height: 0;
     padding-top: 50%; }
     .ds-card .img img {
       border-radius: 4px;
       box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.15); }
   .ds-card .ds-card-link {
     height: 100%;
@@ -2818,16 +2844,18 @@ main {
         box-shadow: 0 0 0 5px #4A4A4F;
         transition: box-shadow 150ms;
         border-radius: 4px;
         outline: none; }
       .ds-card .ds-card-link:active header {
         color: #003EAA; }
         [lwt-newtab-brighttext] .ds-card .ds-card-link:active header {
           color: #0A84FF; }
+  .ds-card.video-card .meta {
+    margin-top: 4px; }
   .ds-card .meta {
     display: flex;
     flex-direction: column;
     padding: 12px 16px;
     flex-grow: 1; }
     .ds-card .meta .info-wrap {
       flex-grow: 1; }
     .ds-card .meta .title {
--- a/browser/components/newtab/css/activity-stream-mac.css
+++ b/browser/components/newtab/css/activity-stream-mac.css
@@ -2762,23 +2762,49 @@ main {
   .ds-signup:-moz-any(:hover, :focus, .active).ds-card-grid-border {
     box-shadow: 0 0 0 5px var(--newtab-card-active-outline-color);
     transition: box-shadow 150ms; }
 
 .ds-card {
   display: flex;
   flex-direction: column;
   position: relative; }
+  .ds-card .playhead {
+    background: #0060DF url("../data/content/assets/glyph-playhead.svg") no-repeat 12px center;
+    border-radius: 20px;
+    bottom: -16px;
+    color: rgba(255, 255, 255, 0);
+    display: flex;
+    flex-direction: column;
+    height: 40px;
+    justify-content: center;
+    left: 16px;
+    min-width: 40px;
+    padding: 0 0 0 40px;
+    position: absolute;
+    transition: padding 100ms ease-in-out 0ms, color 100ms linear 100ms;
+    z-index: 1; }
+    .ds-card .playhead:hover {
+      color: white;
+      padding: 0 20px 0 40px; }
+    .ds-card .playhead span {
+      display: none; }
+    .ds-card .playhead:hover span {
+      display: inline;
+      font-style: normal;
+      font-weight: 600;
+      font-size: 13px; }
   .ds-card.placeholder {
     background: transparent;
     box-shadow: inset 0 0 0 1px var(--newtab-inner-box-shadow-color);
     border-radius: 4px;
     min-height: 300px; }
   .ds-card .img-wrapper {
-    width: 100%; }
+    width: 100%;
+    position: relative; }
   .ds-card .img {
     height: 0;
     padding-top: 50%; }
     .ds-card .img img {
       border-radius: 4px;
       box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.15); }
   .ds-card .ds-card-link {
     height: 100%;
@@ -2821,16 +2847,18 @@ main {
         box-shadow: 0 0 0 5px #4A4A4F;
         transition: box-shadow 150ms;
         border-radius: 4px;
         outline: none; }
       .ds-card .ds-card-link:active header {
         color: #003EAA; }
         [lwt-newtab-brighttext] .ds-card .ds-card-link:active header {
           color: #0A84FF; }
+  .ds-card.video-card .meta {
+    margin-top: 4px; }
   .ds-card .meta {
     display: flex;
     flex-direction: column;
     padding: 12px 16px;
     flex-grow: 1; }
     .ds-card .meta .info-wrap {
       flex-grow: 1; }
     .ds-card .meta .title {
--- a/browser/components/newtab/css/activity-stream-windows.css
+++ b/browser/components/newtab/css/activity-stream-windows.css
@@ -2759,23 +2759,49 @@ main {
   .ds-signup:-moz-any(:hover, :focus, .active).ds-card-grid-border {
     box-shadow: 0 0 0 5px var(--newtab-card-active-outline-color);
     transition: box-shadow 150ms; }
 
 .ds-card {
   display: flex;
   flex-direction: column;
   position: relative; }
+  .ds-card .playhead {
+    background: #0060DF url("../data/content/assets/glyph-playhead.svg") no-repeat 12px center;
+    border-radius: 20px;
+    bottom: -16px;
+    color: rgba(255, 255, 255, 0);
+    display: flex;
+    flex-direction: column;
+    height: 40px;
+    justify-content: center;
+    left: 16px;
+    min-width: 40px;
+    padding: 0 0 0 40px;
+    position: absolute;
+    transition: padding 100ms ease-in-out 0ms, color 100ms linear 100ms;
+    z-index: 1; }
+    .ds-card .playhead:hover {
+      color: white;
+      padding: 0 20px 0 40px; }
+    .ds-card .playhead span {
+      display: none; }
+    .ds-card .playhead:hover span {
+      display: inline;
+      font-style: normal;
+      font-weight: 600;
+      font-size: 13px; }
   .ds-card.placeholder {
     background: transparent;
     box-shadow: inset 0 0 0 1px var(--newtab-inner-box-shadow-color);
     border-radius: 4px;
     min-height: 300px; }
   .ds-card .img-wrapper {
-    width: 100%; }
+    width: 100%;
+    position: relative; }
   .ds-card .img {
     height: 0;
     padding-top: 50%; }
     .ds-card .img img {
       border-radius: 4px;
       box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.15); }
   .ds-card .ds-card-link {
     height: 100%;
@@ -2818,16 +2844,18 @@ main {
         box-shadow: 0 0 0 5px #4A4A4F;
         transition: box-shadow 150ms;
         border-radius: 4px;
         outline: none; }
       .ds-card .ds-card-link:active header {
         color: #003EAA; }
         [lwt-newtab-brighttext] .ds-card .ds-card-link:active header {
           color: #0A84FF; }
+  .ds-card.video-card .meta {
+    margin-top: 4px; }
   .ds-card .meta {
     display: flex;
     flex-direction: column;
     padding: 12px 16px;
     flex-grow: 1; }
     .ds-card .meta .info-wrap {
       flex-grow: 1; }
     .ds-card .meta .title {
--- a/browser/components/newtab/data/content/activity-stream.bundle.js
+++ b/browser/components/newtab/data/content/activity-stream.bundle.js
@@ -4364,16 +4364,17 @@ class _DiscoveryStreamBase extends react
           cta_variant: component.cta_variant,
           display_engagement_labels: ENGAGEMENT_LABEL_ENABLED,
           dismissible: this.props.DiscoveryStream.isCollectionDismissible,
           dispatch: this.props.dispatch
         });
 
       case "CardGrid":
         return react__WEBPACK_IMPORTED_MODULE_14___default.a.createElement(content_src_components_DiscoveryStreamComponents_CardGrid_CardGrid__WEBPACK_IMPORTED_MODULE_1__["CardGrid"], {
+          enable_video_playheads: !!component.properties.enable_video_playheads,
           title: component.header && component.header.title,
           display_variant: component.properties.display_variant,
           data: component.data,
           feed: component.feed,
           border: component.properties.border,
           type: component.type,
           dispatch: this.props.dispatch,
           items: component.properties.items,
@@ -4604,17 +4605,18 @@ class CardGrid extends react__WEBPACK_IM
         dispatch: this.props.dispatch,
         source: rec.domain,
         pocket_id: rec.pocket_id,
         context_type: rec.context_type,
         bookmarkGuid: rec.bookmarkGuid,
         engagement: rec.engagement,
         display_engagement_labels: this.props.display_engagement_labels,
         cta: rec.cta,
-        cta_variant: this.props.cta_variant
+        cta_variant: this.props.cta_variant,
+        is_video: this.props.enable_video_playheads && rec.is_video
       }));
     } // Used for CSS overrides to default styling (eg: "hero")
 
 
     const variantClass = this.props.display_variant ? `ds-card-grid-${this.props.display_variant}` : ``;
     return react__WEBPACK_IMPORTED_MODULE_2___default.a.createElement("div", {
       className: `ds-card-grid ds-card-grid-${this.props.border} ${variantClass}`
     }, cards);
@@ -4644,18 +4646,19 @@ class CardGrid extends react__WEBPACK_IM
       dispatch: this.props.dispatch,
       feed: this.props.feed
     })) : this.renderCards());
   }
 
 }
 CardGrid.defaultProps = {
   border: `border`,
-  items: 4 // Number of stories to display
-
+  items: 4,
+  // Number of stories to display
+  enable_video_playheads: false
 };
 
 /***/ }),
 /* 27 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
@@ -4871,31 +4874,34 @@ class _DSCard extends react__WEBPACK_IMP
     if (this.props.placeholder || !this.state.isSeen) {
       return react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("div", {
         className: "ds-card placeholder",
         ref: this.setPlaceholderRef
       });
     }
 
     const isButtonCTA = this.props.cta_variant === "button";
+    const baseClass = `ds-card ${this.props.is_video ? `video-card` : ``}`;
     return react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("div", {
-      className: "ds-card"
+      className: baseClass
     }, react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement(_SafeAnchor_SafeAnchor__WEBPACK_IMPORTED_MODULE_5__["SafeAnchor"], {
       className: "ds-card-link",
       dispatch: this.props.dispatch,
       onLinkClick: !this.props.placeholder ? this.onLinkClick : undefined,
       url: this.props.url
     }, react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("div", {
       className: "img-wrapper"
     }, react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement(_DSImage_DSImage_jsx__WEBPACK_IMPORTED_MODULE_1__["DSImage"], {
       extraClassNames: "img",
       source: this.props.image_src,
       rawSource: this.props.raw_image_src,
       sizes: this.dsImageSizes
-    })), isButtonCTA ? react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement(CTAButtonMeta, {
+    }), this.props.is_video && react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("div", {
+      className: "playhead"
+    }, react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("span", null, "Video Content"))), isButtonCTA ? react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement(CTAButtonMeta, {
       display_engagement_labels: this.props.display_engagement_labels,
       source: this.props.source,
       title: this.props.title,
       excerpt: this.props.excerpt,
       context: this.props.context,
       context_type: this.props.context_type,
       engagement: this.props.engagement,
       cta: this.props.cta,
new file mode 100644
--- /dev/null
+++ b/browser/components/newtab/data/content/assets/glyph-playhead.svg
@@ -0,0 +1,6 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg width="19" height="22" viewBox="0 0 19 22" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M2.333 1.7651L2.33302 1.76511L17.333 10.4251C17.4343 10.4836 17.5184 10.5678 17.577 10.6691C17.6355 10.7704 17.6663 10.8854 17.6663 11.0024C17.6663 11.1194 17.6355 11.2344 17.577 11.3357L18.443 11.8357L17.577 11.3357C17.5184 11.4371 17.4343 11.5212 17.333 11.5797L17.3327 11.5799L2.33301 20.2464C2.33292 20.2464 2.33282 20.2465 2.33273 20.2465C2.23145 20.305 2.1166 20.3357 1.99968 20.3357C1.88266 20.3357 1.76769 20.3049 1.66635 20.2464C1.565 20.1879 1.48084 20.1037 1.42233 20.0024C1.36382 19.901 1.33301 19.7861 1.33301 19.6691V2.33821C1.33375 2.22195 1.36488 2.1079 1.42332 2.00737C1.48202 1.9064 1.56622 1.82262 1.66749 1.76443C1.76876 1.70623 1.88354 1.67566 2.00034 1.67578C2.11713 1.6759 2.23185 1.7067 2.333 1.7651Z" stroke="white" stroke-width="2"/>
+</svg>