diff --git a/cypress/integration/ms-stream-video.spec.js b/cypress/integration/ms-stream-video.spec.js new file mode 100644 index 0000000..6a98a23 --- /dev/null +++ b/cypress/integration/ms-stream-video.spec.js @@ -0,0 +1,13 @@ +/// + +context('', () => { + 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'); + cy.getIframeBody().find('#playButton'); + cy.getIframeBody().find('#loginButton'); + }); +}); diff --git a/docs/pages/components/microsoft/stream/ms-stream-video.stories.mdx b/docs/pages/components/microsoft/stream/ms-stream-video.stories.mdx new file mode 100644 index 0000000..a15357b --- /dev/null +++ b/docs/pages/components/microsoft/stream/ms-stream-video.stories.mdx @@ -0,0 +1,97 @@ +import { Meta, Story, Canvas, ArgsTable } from '@storybook/addon-docs/blocks'; + +import { MsStreamVideo } from '../../../../../packages/mdx-embed/src/components/microsoft/stream'; + + + +# Microsoft Stream Video + +Display a Microsoft Stream Video by including the component in your `.mdx`. The `videoId` prop is required. + + + +## Usage + + + + + + + +## Aspect Ratio + + + + + + + +## Skip To + + + + + + + +## Auto Play + +There is an autoplay parameter but I haven't been able to make it work so far ... + + + + + + + +## Show Info + + + + + + + +## Help + + + +### Getting the `videoId` + +To embed a Microsoft Stream video using the `` 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 + +``` + +### 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) diff --git a/packages/mdx-embed/src/components/mdx-embed-provider/components.ts b/packages/mdx-embed/src/components/mdx-embed-provider/components.ts index 15add02..a7dd164 100644 --- a/packages/mdx-embed/src/components/mdx-embed-provider/components.ts +++ b/packages/mdx-embed/src/components/mdx-embed-provider/components.ts @@ -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'; @@ -46,6 +47,7 @@ export const components = { Instagram, Lbry, LinkedInBadge, + MsStreamVideo, Pin, PinterestBoard, PinterestFollowButton, diff --git a/packages/mdx-embed/src/components/microsoft/stream/index.ts b/packages/mdx-embed/src/components/microsoft/stream/index.ts new file mode 100644 index 0000000..451691b --- /dev/null +++ b/packages/mdx-embed/src/components/microsoft/stream/index.ts @@ -0,0 +1 @@ +export { MsStreamVideo } from './ms-stream-video'; diff --git a/packages/mdx-embed/src/components/microsoft/stream/ms-stream-video.test.tsx b/packages/mdx-embed/src/components/microsoft/stream/ms-stream-video.test.tsx new file mode 100644 index 0000000..6d0d819 --- /dev/null +++ b/packages/mdx-embed/src/components/microsoft/stream/ms-stream-video.test.tsx @@ -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('', () => { + beforeEach(() => { + (window as any).addIntersectionObserver(); + }); + + test('it renders the component when provided a video id', () => { + render(); + + 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(); + + 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(); + + 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(); + + 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(); + + act(() => { + (window as any).triggerGeneralObserver(); + return undefined; + }); + + const iframe = screen.getByTestId(`ms-stream-video`); + expect(iframe).toHaveAttribute('src', expect.stringContaining(`showinfo=false`)); + }); +}); \ No newline at end of file diff --git a/packages/mdx-embed/src/components/microsoft/stream/ms-stream-video.tsx b/packages/mdx-embed/src/components/microsoft/stream/ms-stream-video.tsx new file mode 100644 index 0000000..5721c61 --- /dev/null +++ b/packages/mdx-embed/src/components/microsoft/stream/ms-stream-video.tsx @@ -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 = ({ + 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 ( + +
+