This repository has been archived by the owner on Mar 4, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 55
feat(Embed|Video): add components #1108
Merged
layershifter
merged 32 commits into
microsoft:master
from
stuartlong:feat/stuartlo/video
Apr 10, 2019
Merged
Changes from all commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
f431c27
feat: implements video and video gif component
stuartlong ffb49f7
Responded to PR comments
stuartlong 449c955
Create Embed component
stuartlong 92fd5c0
Merge branch 'master' into feat/stuartlo/video
layershifter 498865f
Merge branch 'feat/stuartlo/video' of https://github.com/stuartlong/r…
stuartlong f6a3110
USe AutoControlled component
stuartlong 443f2f4
Respond to code review comments
stuartlong cebb462
Add some periods
stuartlong f762d05
Remove redundant conditional in styles
stuartlong fd55afc
reorder props, add missing propTypes
layershifter 10f77dc
remove unused lodash
layershifter 7e5128d
reorder props, add missing, remove useless
layershifter 4885f72
use trySetState
layershifter 2c25207
use `v` & VideoVariables
layershifter 8667773
move control to a separate slot
layershifter 41581f3
export play and pause icons
layershifter c2186e2
add stardust icons
layershifter f71b155
use Icon component, simplify styles
layershifter 1e43a79
Merge branch 'master' of https://github.com/stardust-ui/react into fe…
layershifter 8873cd4
update case
layershifter a09438a
Merge branch 'master' of https://github.com/stardust-ui/react into fe…
layershifter 82453f5
fix UTs
layershifter 2052501
fix merge issue
layershifter 41fcec4
fix broken UTs
layershifter 15eaf66
fix assignment of muted prop
layershifter 4448b54
fix import
layershifter dbc89f4
fix condition
layershifter ae56edc
address comments
layershifter 48618ee
Merge branch 'master' of https://github.com/stardust-ui/react into fe…
layershifter fff05b3
add changelog entry
layershifter 1e6809a
Merge branch 'master' of https://github.com/stardust-ui/react into fe…
layershifter dae16b9
update styles
layershifter File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
docs/src/examples/components/Embed/Types/EmbedExample.shorthand.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import * as React from 'react' | ||
import { Embed, Flex, Text } from '@stardust-ui/react' | ||
|
||
const EmbedExample = () => ( | ||
<Flex column> | ||
<Embed | ||
placeholder="https://github.com/raw/bower-media-samples/big-buck-bunny-480p-5s/master/poster.jpg" | ||
video="https://github.com/raw/bower-media-samples/big-buck-bunny-1080p-5s/master/video.mp4" | ||
defaultActive={true} | ||
variables={{ height: '400px', width: '711.11px' }} | ||
/> | ||
<Text>(c) copyright 2008, Blender Foundation / www.bigbuckbunny.org</Text> | ||
</Flex> | ||
) | ||
|
||
export default EmbedExample |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import * as React from 'react' | ||
|
||
import ComponentExample from 'docs/src/components/ComponentDoc/ComponentExample' | ||
import ExampleSection from 'docs/src/components/ComponentDoc/ExampleSection' | ||
|
||
const Types = () => ( | ||
<ExampleSection title="Types"> | ||
<ComponentExample | ||
title="Embed" | ||
description="A basic embedded GIF." | ||
examplePath="components/Embed/Types/EmbedExample" | ||
/> | ||
</ExampleSection> | ||
) | ||
|
||
export default Types |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import * as React from 'react' | ||
|
||
import Types from './Types' | ||
|
||
const EmbedExamples = () => ( | ||
<> | ||
<Types /> | ||
</> | ||
) | ||
|
||
export default EmbedExamples |
14 changes: 14 additions & 0 deletions
14
docs/src/examples/components/Video/Types/VideoExample.shorthand.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import * as React from 'react' | ||
import { Video, Text, Flex } from '@stardust-ui/react' | ||
|
||
const VideoExample = () => ( | ||
<Flex column> | ||
<Video | ||
src="https://github.com/raw/bower-media-samples/big-buck-bunny-480p-30s/master/video.mp4" | ||
variables={{ width: '600px' }} | ||
/> | ||
<Text>(c) copyright 2008, Blender Foundation / www.bigbuckbunny.org</Text> | ||
</Flex> | ||
) | ||
|
||
export default VideoExample |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import * as React from 'react' | ||
|
||
import ComponentExample from 'docs/src/components/ComponentDoc/ComponentExample' | ||
import ExampleSection from 'docs/src/components/ComponentDoc/ExampleSection' | ||
|
||
const Types = () => ( | ||
<ExampleSection title="Types"> | ||
<ComponentExample | ||
title="Default" | ||
description="A default Video." | ||
examplePath="components/Video/Types/VideoExample" | ||
/> | ||
</ExampleSection> | ||
) | ||
|
||
export default Types |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import * as React from 'react' | ||
|
||
import Types from './Types' | ||
|
||
const VideoExamples = () => ( | ||
<> | ||
<Types /> | ||
</> | ||
) | ||
|
||
export default VideoExamples |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
import * as _ from 'lodash' | ||
import * as PropTypes from 'prop-types' | ||
import * as React from 'react' | ||
import * as customPropTypes from '@stardust-ui/react-proptypes' | ||
|
||
import { | ||
createShorthandFactory, | ||
UIComponentProps, | ||
applyAccessibilityKeyHandlers, | ||
commonPropTypes, | ||
AutoControlledComponent, | ||
} from '../../lib' | ||
import { embedBehavior } from '../../lib/accessibility' | ||
import { Accessibility, AccessibilityActionHandlers } from '../../lib/accessibility/types' | ||
import Icon, { IconProps } from '../Icon/Icon' | ||
import Image, { ImageProps } from '../Image/Image' | ||
import Video, { VideoProps } from '../Video/Video' | ||
import Box from '../Box/Box' | ||
import { ComponentEventHandler, ReactProps, ShorthandValue } from '../../types' | ||
|
||
export interface EmbedSlotClassNames { | ||
control: string | ||
} | ||
|
||
export interface EmbedProps extends UIComponentProps { | ||
/** | ||
* Accessibility behavior if overridden by the user. | ||
* @default embedBehavior | ||
* */ | ||
accessibility?: Accessibility | ||
|
||
/** Whether the embedded object should be active. */ | ||
active?: boolean | ||
|
||
/** Whether the embedded object should start active. */ | ||
defaultActive?: boolean | ||
|
||
/** Shorthand for an control. */ | ||
control?: ShorthandValue<IconProps> | ||
|
||
/** Shorthand for an embedded iframe. */ | ||
iframe?: ShorthandValue | ||
|
||
/** | ||
* Event for request to change 'active' value. | ||
* @param {SyntheticEvent} event - React's original SyntheticEvent. | ||
* @param {object} data - All props and proposed value. | ||
*/ | ||
onActiveChanged?: ComponentEventHandler<EmbedProps> | ||
|
||
/** | ||
* Called when is clicked. | ||
* | ||
* @param {SyntheticEvent} event - React's original SyntheticEvent. | ||
* @param {object} data - All item props. | ||
*/ | ||
onClick?: ComponentEventHandler<EmbedProps> | ||
|
||
/** Image source URL for when video isn't playing. */ | ||
placeholder?: ShorthandValue<ImageProps> | ||
|
||
/** Shorthand for an embedded video. */ | ||
video?: ShorthandValue<VideoProps> | ||
} | ||
|
||
export interface EmbedState { | ||
active: boolean | ||
} | ||
|
||
/** | ||
* A GIF is a muted segment of a video | ||
* @accessibility | ||
* If GIF should be visible to screen readers, textual representation needs to be provided in 'alt' or 'title' property. | ||
* | ||
* Other considerations: | ||
* - when alt and title property are empty, then Narrator in scan mode navigates to the gif and narrates it as empty paragraph | ||
*/ | ||
class Embed extends AutoControlledComponent<ReactProps<EmbedProps>, EmbedState> { | ||
static create: Function | ||
|
||
static className = 'ui-embed' | ||
|
||
static displayName = 'Embed' | ||
|
||
static propTypes = { | ||
...commonPropTypes.createCommon({ | ||
children: false, | ||
content: false, | ||
}), | ||
active: PropTypes.bool, | ||
defaultActive: PropTypes.bool, | ||
control: customPropTypes.itemShorthand, | ||
iframe: customPropTypes.itemShorthand, | ||
onActiveChanged: PropTypes.func, | ||
onClick: PropTypes.func, | ||
placeholder: PropTypes.string, | ||
video: customPropTypes.itemShorthand, | ||
} | ||
|
||
static defaultProps = { | ||
as: 'span', | ||
accessibility: embedBehavior as Accessibility, | ||
control: {}, | ||
} | ||
|
||
static autoControlledProps = ['active'] | ||
|
||
static slotClassNames: EmbedSlotClassNames = { | ||
control: `${Embed.className}__control`, | ||
} | ||
|
||
actionHandlers: AccessibilityActionHandlers = { | ||
performClick: event => this.handleClick(event), | ||
} | ||
|
||
getInitialAutoControlledState(): EmbedState { | ||
return { active: false } | ||
} | ||
|
||
handleClick = e => { | ||
e.stopPropagation() | ||
e.preventDefault() | ||
|
||
this.trySetState({ active: !this.state.active }) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is where we would invoke the |
||
|
||
_.invoke(this.props, 'onActiveChanged', e, { ...this.props, active: !this.state.active }) | ||
_.invoke(this.props, 'onClick', e, { ...this.props, active: !this.state.active }) | ||
} | ||
|
||
renderComponent({ ElementType, classes, accessibility, unhandledProps, styles, variables }) { | ||
const { control, iframe, placeholder, video } = this.props | ||
const { active } = this.state | ||
|
||
return ( | ||
<ElementType | ||
className={classes.root} | ||
onClick={this.handleClick} | ||
{...accessibility.attributes.root} | ||
{...applyAccessibilityKeyHandlers(accessibility.keyHandlers.root, unhandledProps)} | ||
{...unhandledProps} | ||
> | ||
{active ? ( | ||
<> | ||
{Video.create(video, { | ||
defaultProps: { | ||
autoPlay: true, | ||
controls: false, | ||
loop: true, | ||
muted: true, | ||
styles: styles.video, | ||
variables: { | ||
width: variables.width, | ||
height: variables.height, | ||
}, | ||
}, | ||
})} | ||
{Box.create(iframe, { defaultProps: { as: 'iframe' } })} | ||
</> | ||
) : ( | ||
Image.create(placeholder, { | ||
defaultProps: { | ||
styles: styles.image, | ||
variables: { | ||
width: variables.width, | ||
height: variables.height, | ||
}, | ||
}, | ||
}) | ||
)} | ||
|
||
{Icon.create(control, { | ||
defaultProps: { | ||
className: Embed.slotClassNames.control, | ||
circular: true, | ||
name: active ? 'stardust-pause' : 'stardust-play', | ||
size: 'largest', | ||
styles: styles.control, | ||
}, | ||
})} | ||
</ElementType> | ||
) | ||
} | ||
} | ||
|
||
Embed.create = createShorthandFactory({ Component: Embed }) | ||
|
||
export default Embed |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
import * as PropTypes from 'prop-types' | ||
import * as React from 'react' | ||
|
||
import { createShorthandFactory, UIComponent, UIComponentProps, commonPropTypes } from '../../lib' | ||
|
||
import { ReactProps } from '../../types' | ||
import { defaultBehavior } from '../../lib/accessibility' | ||
import Ref from '../Ref/Ref' | ||
|
||
export interface VideoProps extends UIComponentProps { | ||
/** Whether the video should start playing when rendered. Autoplay videos must be muted or they will not play immediately in certain browers like Chrome. */ | ||
autoPlay?: boolean | ||
|
||
/** Whether to display the native video controls. */ | ||
controls?: boolean | ||
|
||
/** Whether the video should automatically restart after it ends. */ | ||
loop?: boolean | ||
|
||
/** Whether the video should be allowed to play audio. */ | ||
muted?: boolean | ||
|
||
/** Image source URL for when video isn't playing. */ | ||
poster?: string | ||
|
||
/** Video source URL. */ | ||
src?: string | ||
} | ||
|
||
/** | ||
* An video is a graphicical and audio representation of something. | ||
*/ | ||
class Video extends UIComponent<ReactProps<VideoProps>> { | ||
static create: Function | ||
|
||
static className = 'ui-video' | ||
|
||
static displayName = 'Video' | ||
|
||
static propTypes = { | ||
...commonPropTypes.createCommon({ | ||
children: false, | ||
content: false, | ||
}), | ||
autoPlay: PropTypes.bool, | ||
controls: PropTypes.bool, | ||
loop: PropTypes.bool, | ||
muted: PropTypes.bool, | ||
poster: PropTypes.string, | ||
src: PropTypes.string, | ||
} | ||
|
||
static defaultProps = { | ||
as: 'video', | ||
accessibility: defaultBehavior, | ||
controls: true, | ||
autoPlay: false, | ||
muted: false, | ||
} | ||
|
||
videoRef = React.createRef<HTMLVideoElement>() | ||
|
||
componentDidMount() { | ||
this.setVideoAttributes() | ||
} | ||
|
||
componentDidUpdate() { | ||
this.setVideoAttributes() | ||
} | ||
|
||
setVideoAttributes = () => { | ||
// React doesn't guaranty that props will be set: | ||
// https://github.com/facebook/react/issues/10389 | ||
if (this.videoRef.current) { | ||
if (this.props.muted) { | ||
this.videoRef.current.setAttribute('muted', 'true') | ||
} else { | ||
this.videoRef.current.removeAttribute('muted') | ||
} | ||
} | ||
} | ||
|
||
renderComponent({ accessibility, ElementType, classes, unhandledProps }) { | ||
const { controls, autoPlay, loop, poster, src } = this.props | ||
|
||
return ( | ||
<Ref innerRef={this.videoRef}> | ||
<ElementType | ||
autoPlay={autoPlay} | ||
className={classes.root} | ||
controls={controls} | ||
loop={loop} | ||
poster={poster} | ||
src={src} | ||
{...accessibility.attributes.root} | ||
{...unhandledProps} | ||
/> | ||
</Ref> | ||
) | ||
} | ||
} | ||
|
||
Video.create = createShorthandFactory({ Component: Video, mappedProp: 'src' }) | ||
|
||
export default Video |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add
onActiveChanged
prop for this component, and invoke it when we change the active prop, as it will be required when we want to use it in controlled mode (see for example theonOpenChanged
in thePopup
component)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implemented as
onActiveChanged
, however I want address an inconsistency there separately becausePopup
hasonOpenChange
(without d on end).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would expect the d in the end, but agree, let's address in a separate PR, to avoid unnecessary breaking changes.