Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add Microsoft Stream video component #223

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
13 changes: 13 additions & 0 deletions cypress/integration/ms-stream-video.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/// <reference types="cypress" />

context('<MsStreamVideo />', () => {
it('it loads stream video correctly', () => {
cy.visit('/iframe.html?id=components-msstreamvideo--usage&viewMode=story');
cy.getIframeBody().then((data) => {
console.log({data});
});
cy.getIframeBody().find('#streamLoading');
Copy link
Author

Choose a reason for hiding this comment

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

I removed the .should('not.be.undefined'); used in other components' tests.
It seems to make tests pass when they shouldn't whereas simply stopping after the find() works well.

For an example, I modified the youtube tests on this branch:
https://github.com/gaelhameon/mdx-embed/blob/wip/questions/cypress/integration/youtube.spec.js

cy.getIframeBody().find('#playButton');
cy.getIframeBody().find('#loginButton');
});
});
97 changes: 97 additions & 0 deletions docs/pages/components/microsoft/stream/ms-stream-video.stories.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { Meta, Story, Canvas, ArgsTable } from '@storybook/addon-docs/blocks';

import { MsStreamVideo } from '../../../../../packages/mdx-embed/src/components/microsoft/stream';

<Meta title="Components/MsStreamVideo" component={MsStreamVideo} />

# Microsoft Stream Video

Display a Microsoft Stream Video by including the component in your `.mdx`. The `videoId` prop is required.

<ArgsTable of={MsStreamVideo} />

## Usage

<Canvas>
<Story name="usage">
<MsStreamVideo videoId="6ca9498c-c4f6-4bd5-99cc-2a44aabc3098" />
</Story>
</Canvas>

## Aspect Ratio

<Canvas>
<Story name="aspectRatio">
<MsStreamVideo videoId="6ca9498c-c4f6-4bd5-99cc-2a44aabc3098" aspectRatio="4:3" />
</Story>
</Canvas>

## Skip To

<Canvas>
<Story name="skipTo">
<MsStreamVideo videoId="6ca9498c-c4f6-4bd5-99cc-2a44aabc3098" skipTo={{ h: 0, m: 1, s: 44 }} />
</Story>
</Canvas>

## Auto Play

There is an autoplay parameter but I haven't been able to make it work so far ...

<Canvas>
<Story name="autoPlay">
<MsStreamVideo videoId="6ca9498c-c4f6-4bd5-99cc-2a44aabc3098" autoPlay="true" />
</Story>
</Canvas>

## Show Info

<Canvas>
<Story name="showInfo">
<MsStreamVideo videoId="6ca9498c-c4f6-4bd5-99cc-2a44aabc3098" showInfo="false" />
</Story>
</Canvas>

## Help

<Story name="help" />

### Getting the `videoId`

To embed a Microsoft Stream video using the `<MsStreamVideo />` component, grab the `videoId` from the URL.

```
https://web.microsoftstream.com/video/6ca9498c-c4f6-4bd5-99cc-2a44aabc3098
https://web.microsoftstream.com/video/6ca9498c-c4f6-4bd5-99cc-2a44aabc3098?channelId=55e5ec5f-f155-4477-99b1-fb1c61da6bf4
```

The `videoId` is the part between `video/` and the `?`.

Be careful, depending on how you accessed the video, there might be extra info in the url that looks like the `videoId`, but that is not. In the second URL above, for example, the last part is the channel Id.

Use the `videoId` like this:

```jsx
<MsStreamVideo videoId="6ca9498c-c4f6-4bd5-99cc-2a44aabc3098" />
```

### Permissions and public sharing

From the official Microsoft Docs (links at the end of this page):

> Only people authorized to see a video will be able to view it.

> Microsoft Stream doesn't yet support external sharing scenarios for guests or people outside your organization. We know these features are highly needed and we are working on plans for how to build these features.

They have been working on these plans for quite a while now ... maybe something will come out soon!

There is one video at the top of [this page](https://docs.microsoft.com/fr-fr/stream/portal-share-video) that looks like an embedded Stream Video that is publicly available, but upon inspection, it looks like it's only a theme and the video itself is probably not hosted on Stream.

And in other places where Microsoft shares public videos, like in their [Video Hub](https://techcommunity.microsoft.com/t5/video-hub/on-demand-learning-with-video-and-microsoft-stream/m-p/2177356), they use ... YouTube !

In conclusion, use Stream only if you want to share videos within your organization and have constraints that prevent you from using external services.

### Official Microsoft Documentation

- [Share your videos in Microsoft Stream](https://docs.microsoft.com/fr-fr/stream/portal-share-video)
- [Embed Microsoft Stream videos in other apps](https://docs.microsoft.com/fr-fr/stream/portal-embed-video)
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Gist } from '../gist';
import { Instagram } from '../instagram';
import { Lbry } from '../lbry';
import { LinkedInBadge } from '../linkedin';
import { MsStreamVideo } from '../microsoft/stream';
import { Pin, PinterestBoard, PinterestFollowButton } from '../pinterest';
import { SimplecastEpisode } from '../simplecast';
import { SoundCloud } from '../soundcloud';
Expand Down Expand Up @@ -46,6 +47,7 @@ export const components = {
Instagram,
Lbry,
LinkedInBadge,
MsStreamVideo,
Pin,
PinterestBoard,
PinterestFollowButton,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { MsStreamVideo } from './ms-stream-video';
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React from 'react';
import { render, screen, act } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';

import { MsStreamVideo } from '.';

const videoId = '6ca9498c-c4f6-4bd5-99cc-2a44aabc3098';

describe('<MsStreamVideo />', () => {
beforeEach(() => {
(window as any).addIntersectionObserver();
});

test('it renders the component when provided a video id', () => {
render(<MsStreamVideo videoId={videoId} />);

act(() => {
(window as any).triggerGeneralObserver();
return undefined;
});

const iframe = screen.getByTestId(`ms-stream-video`);
expect(iframe).toBeDefined();
});

test('it sets the autoplay property in the stream iframe src when autoplay is true', () => {
render(<MsStreamVideo videoId={videoId} autoPlay={true} />);

act(() => {
(window as any).triggerGeneralObserver();
return undefined;
});

const iframe = screen.getByTestId(`ms-stream-video`);
expect(iframe).toHaveAttribute('src', expect.stringContaining('autoplay=true'));
});

test('it sets the st property in the stream iframe src when skipTo is present', () => {
const skipTo = { h: 0, m: 1, s: 40 };
render(<MsStreamVideo videoId={videoId} skipTo={skipTo} />);

act(() => {
(window as any).triggerGeneralObserver();
return undefined;
});

const iframe = screen.getByTestId(`ms-stream-video`);
expect(iframe).toHaveAttribute('src', expect.stringContaining(`st=100`));
});
test('it sets the showinfo property in the stream iframe src to true by default', () => {
render(<MsStreamVideo videoId={videoId} />);

act(() => {
(window as any).triggerGeneralObserver();
return undefined;
});

const iframe = screen.getByTestId(`ms-stream-video`);
expect(iframe).toHaveAttribute('src', expect.stringContaining(`showinfo=true`));
});
test('it sets the showinfo property in the stream iframe src to false when specified as such', () => {
render(<MsStreamVideo videoId={videoId} showInfo={false} />);

act(() => {
(window as any).triggerGeneralObserver();
return undefined;
});

const iframe = screen.getByTestId(`ms-stream-video`);
expect(iframe).toHaveAttribute('src', expect.stringContaining(`showinfo=false`));
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React, { FunctionComponent } from 'react';
import { getPadding } from '../../../utils';
import { GeneralObserver } from '../../general-observer';

export interface IMsStreamVideoProps {
/** Stream Video Id */
videoId: string;
/** Aspect ratio of the video */
aspectRatio?: '1:1' | '16:9' | '4:3' | '3:2' | '8:5';
/** Skip to a time in the video */
skipTo?: {
h?: number;
m: number;
s: number;
};
/** Auto play the video */
autoPlay?: boolean;
/** Show info like video title in overlay */
showInfo?: boolean;
}

export const MsStreamVideo: FunctionComponent<IMsStreamVideoProps> = ({
videoId,
aspectRatio = '16:9',
autoPlay = false,
skipTo = { h: 0, m: 0, s: 0 },
showInfo = true,
}: IMsStreamVideoProps) => {
const { h, m, s } = skipTo;

const tH = h! * 60;
const tM = m * 60;

const startTime = tH + tM + s;

return (
<GeneralObserver>
<div
className="ms-stream-video-mdx-embed"
style={{
position: 'relative',
width: '100%',
...getPadding(aspectRatio),
}}
>
<iframe
data-testid="ms-stream-video"
title={`ms-stream-video-${videoId}`}
src={`https://web.microsoftstream.com/embed/video/${videoId}?autoplay=${autoPlay}&showinfo=${showInfo}&st=${startTime}`}
allowFullScreen
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
}}
/>
</div>
</GeneralObserver>
);
};
1 change: 1 addition & 0 deletions packages/mdx-embed/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export { Gist } from './components/gist';
export { Instagram } from './components/instagram';
export { Lbry } from './components/lbry';
export { LinkedInBadge } from './components/linkedin';
export { MsStreamVideo } from './components/microsoft/stream';
export { Pin, PinterestBoard, PinterestFollowButton } from './components/pinterest';
export { SimplecastEpisode } from './components/simplecast';
export { SoundCloud } from './components/soundcloud';
Expand Down