Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/nine-bobcats-study.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/polaris': patch
---

Update the `VideoThumbnail` play button user experience
51 changes: 19 additions & 32 deletions polaris-react/src/components/VideoThumbnail/VideoThumbnail.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,11 @@ $progress-bar-height: 6px;
background-repeat: no-repeat;
width: 100%;
height: 100%;
}

&.WithPlayer {
position: absolute;
z-index: 1;
top: 0;
left: 0;
width: 100%;
height: 100%;
padding-bottom: auto;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wasn't used

Copy link
Member

@alex-page alex-page Oct 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wish there was a tool to find unused scss class names. Could be impactful across multiple repos.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was actually looking into something like that this morning :D Definitely possible and would be so useful

.ThumbnailContainer {
position: relative;
height: 100%;
}

.PlayButton {
Expand All @@ -33,50 +28,42 @@ $progress-bar-height: 6px;
padding: 0;
border: none;
background: transparent;
opacity: 0.8;
transition: opacity var(--p-duration-200) var(--p-ease-in);
cursor: pointer;

&:hover,
&:focus {
opacity: 1;
}

&:focus {
outline: none;
box-shadow: inset 2px 0 0 var(--p-focused);
background-image: linear-gradient(
var(--pc-play-button-focused-state-overlay),
var(--pc-play-button-focused-state-overlay)
);

.Timestamp {
background-color: rgba(0, 0, 0, 0.8);
}
}

&:hover .Timestamp {
background-color: rgba(0, 0, 0, 0.8);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are these custom colours ok? design had black with this opacity and no token was suitable

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are okay for now. We can find/fix them up later.

}
}

.PlayIcon {
position: absolute;
top: 50%;
left: 50%;
width: $start-button-size;
height: $start-button-size;
margin-top: -$start-button-size * 0.5;
margin-left: -$start-button-size * 0.5;
fill: var(--p-icon-on-interactive);
}

.Timestamp {
position: absolute;
bottom: 0;
padding: 0 var(--p-space-1);
margin-bottom: var(--p-space-2);
margin-left: var(--p-space-2);
padding: var(--p-space-1) var(--p-space-2) var(--p-space-1) var(--p-space-1);
margin-bottom: var(--p-space-4);
margin-left: var(--p-space-4);
border-radius: var(--p-border-radius-1);
color: var(--p-text);
background-color: var(--p-surface);
opacity: 0.8;
color: var(--p-text-on-interactive);
background-color: rgba(0, 0, 0, 0.6);
text-align: center;
}

.withProgress {
margin-bottom: var(--p-space-3);
transition: background-color var(--p-duration-200) var(--p-ease-in);
}

.Progress {
Expand Down
40 changes: 25 additions & 15 deletions polaris-react/src/components/VideoThumbnail/VideoThumbnail.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import React from 'react';
import {PlayMinor} from '@shopify/polaris-icons';

import {useI18n} from '../../utilities/i18n';
import {classNames} from '../../utilities/css';
import {
secondsToTimeComponents,
secondsToTimestamp,
secondsToDurationTranslationKey,
} from '../../utilities/duration';
import {useMediaQuery} from '../../utilities/media-query';
import {Icon} from '../Icon';
import {Stack} from '../Stack';
import {Text} from '../Text';

import {PlayIcon} from './illustrations';
import styles from './VideoThumbnail.scss';

export interface VideoThumbnailProps {
Expand Down Expand Up @@ -49,6 +52,7 @@ export function VideoThumbnail({
onBeforeStartPlaying,
}: VideoThumbnailProps) {
const i18n = useI18n();
const {isNavigationCollapsed} = useMediaQuery();
let buttonLabel;

if (accessibilityLabel) {
Expand All @@ -73,13 +77,19 @@ export function VideoThumbnail({
}

const timeStampMarkup = videoLength ? (
<p
className={classNames(
styles.Timestamp,
showVideoProgress && styles.withProgress,
)}
>
{secondsToTimestamp(videoLength)}
<p className={styles.Timestamp}>
<Stack alignment="center" spacing="extraTight">
<span className={styles.PlayIcon}>
<Icon source={PlayMinor} />
</span>
<Text
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a Text component now?! 🤯

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

variant={isNavigationCollapsed ? 'bodyLg' : 'bodyMd'}
as="p"
fontWeight="semibold"
>
{secondsToTimestamp(videoLength)}
</Text>
</Stack>
</p>
) : null;

Expand Down Expand Up @@ -109,10 +119,11 @@ export function VideoThumbnail({
}

return (
<div
className={styles.Thumbnail}
style={{backgroundImage: `url(${thumbnailUrl})`}}
>
<div className={styles.ThumbnailContainer}>
<div
className={styles.Thumbnail}
style={{backgroundImage: `url(${thumbnailUrl})`}}
/>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

separated out the background image from the wrapping div so that the image overflow wouldn't hide the play buttons when the image cover had to be cropped. For example, on mobile:

Screen Shot 2022-10-04 at 10 20 14 AM

Screen Shot 2022-10-04 at 10 20 32 AM

<button
type="button"
className={styles.PlayButton}
Expand All @@ -122,9 +133,8 @@ export function VideoThumbnail({
onFocus={onBeforeStartPlaying}
onTouchStart={onBeforeStartPlaying}
>
<img className={styles.PlayIcon} src={PlayIcon} alt="" />
{timeStampMarkup}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving this inside since it changes opacity on hover and the button is the whole thumbnail

</button>
{timeStampMarkup}
{progressMarkup}
</div>
);
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import {mountWithApp} from 'tests/utilities';

import {VideoThumbnail} from '../VideoThumbnail';
import {Text} from '../../Text';

describe('<VideoThumbnail />', () => {
const spyClick = jest.fn();
Expand Down Expand Up @@ -29,8 +30,8 @@ describe('<VideoThumbnail />', () => {
const videoThumbnail = mountWithApp(
<VideoThumbnail {...mockProps} videoLength={undefined} />,
);
expect(videoThumbnail).not.toContainReactComponent('p', {
className: 'Timestamp',
expect(videoThumbnail).not.toContainReactComponent(Text, {
fontWeight: 'semibold',
});
});

Expand All @@ -39,8 +40,8 @@ describe('<VideoThumbnail />', () => {
<VideoThumbnail {...mockProps} videoLength={45} />,
);

expect(videoThumbnail).toContainReactComponent('p', {
className: 'Timestamp',
expect(videoThumbnail).toContainReactComponent(Text, {
fontWeight: 'semibold',
children: '0:45',
});
});
Expand All @@ -49,8 +50,8 @@ describe('<VideoThumbnail />', () => {
const videoThumbnail = mountWithApp(
<VideoThumbnail {...mockProps} videoLength={135} />,
);
expect(videoThumbnail).toContainReactComponent('p', {
className: 'Timestamp',
expect(videoThumbnail).toContainReactComponent(Text, {
fontWeight: 'semibold',
children: '2:15',
});
});
Expand All @@ -59,8 +60,8 @@ describe('<VideoThumbnail />', () => {
const videoThumbnail = mountWithApp(
<VideoThumbnail {...mockProps} videoLength={3745} />,
);
expect(videoThumbnail).toContainReactComponent('p', {
className: 'Timestamp',
expect(videoThumbnail).toContainReactComponent(Text, {
fontWeight: 'semibold',
children: '1:02:25',
});
});
Expand Down