@datocms/svelte 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,6 +27,7 @@ A set of components to work faster with [DatoCMS](https://www.datocms.com/) in S
27
27
  Components:
28
28
 
29
29
  - [`<Image />`](src/lib/components/Image)
30
+ - [`<VideoPlayer />`](src/lib/components/VideoPlayer)
30
31
  - [`<StructuredText />`](src/lib/components/StructuredText)
31
32
  - [`<Head />`](src/lib/components/Head)
32
33
 
@@ -0,0 +1,127 @@
1
+ # `<VideoPlayer/>` component for easy video integration.
2
+
3
+ `<VideoPlayer />` is a Svelte component specially designed to work seamlessly
4
+ with DatoCMS’s [`video` GraphQL
5
+ query](https://www.datocms.com/docs/content-delivery-api/images-and-videos#videos)
6
+ that optimizes video streaming for your sites.
7
+
8
+ To stream videos, DatoCMS partners with MUX, a video CDN that serves optimized
9
+ streams to your users. Our component is a wrapper around [MUX's video
10
+ player](https://github.com/muxinc/elements/blob/main/packages/mux-player/README.md)
11
+ [web
12
+ component](https://developer.mozilla.org/en-US/docs/Web/API/Web_components). It
13
+ takes care of the details for you, and this is our recommended way to serve
14
+ optimal videos to your users.
15
+
16
+ ## Out-of-the-box features
17
+
18
+ - Offers optimized streaming so smartphones and tablets don’t request desktop-sized videos
19
+ - Lazy loads the underlying video player web component and the video to be
20
+ played to speed initial page load and save bandwidth
21
+ - Holds the video position so your page doesn’t jump while the player loads
22
+ - Uses blur-up technique to show a placeholder of the video while it loads
23
+
24
+ ## Installation
25
+
26
+ ```sh {"id":"01HP46D8MDP5Y76HY788MWNDMX"}
27
+ npm install --save @datocms/svelte @mux/mux-player
28
+ ```
29
+
30
+ `@mux/mux-player` is a [peer dependency](https://docs.npmjs.com/cli/v10/configuring-npm/package-json#peerdependencies) for `@datocms/svelte`: so you're expected to add it to your project.
31
+
32
+ ## Usage
33
+
34
+ 1. Import `VideoPlayer` from `@datocms/svelte` and use it in your app
35
+ 2. Write a GraphQL query to your DatoCMS project using the [`video` query](https://www.datocms.com/docs/content-delivery-api/images-and-videos#videos)
36
+
37
+ The GraphQL query returns data that the `VideoPlayer` component automatically uses to properly size the player, set up a “blur-up” placeholder as well as lazy loading the video.
38
+
39
+ ## Example
40
+
41
+ ```svelte {"id":"01HP46D8MDP5Y76HY78BNPWHB2"}
42
+ <script>
43
+
44
+ import { onMount } from 'svelte';
45
+
46
+ import { VideoPlayer } from '@datocms/svelte';
47
+
48
+ const query = gql`
49
+ query {
50
+ blogPost {
51
+ title
52
+ cover {
53
+ video {
54
+ # required: this field identifies the video to be played
55
+ muxPlaybackId
56
+
57
+ # all the other fields are not required but:
58
+
59
+ # if provided, title is displayed in the upper left corner of the video
60
+ title
61
+
62
+ # if provided, width and height are used to define the aspect ratio of the
63
+ # player, so to avoid layout jumps during the rendering.
64
+ width
65
+ height
66
+
67
+ # if provided, it shows a blurred placeholder for the video
68
+ blurUpThumb
69
+
70
+ # you can include more data here: they will be ignored by the component
71
+ }
72
+ }
73
+ }
74
+ }
75
+ `;
76
+
77
+ export let data = null;
78
+
79
+ onMount(async () => {
80
+ const response = await fetch('https://graphql.datocms.com/', {
81
+ method: 'POST',
82
+ headers: {
83
+ 'Content-Type': 'application/json',
84
+ Authorization: "Bearer faeb9172e232a75339242faafb9e56de8c8f13b735f7090964",
85
+ },
86
+ body: JSON.stringify({ query })
87
+ })
88
+
89
+ const json = await response.json()
90
+
91
+ data = json.data;
92
+ });
93
+
94
+ </script>
95
+
96
+ <article>
97
+ {#if data}
98
+ <h1>{{ data.blogPost.title }}</h1>
99
+ <VideoPlayer data={data.blogPost.cover.video} />
100
+ {/if}
101
+ </article>
102
+ ```
103
+
104
+ ## Props
105
+
106
+ The `<VideoPlayer />` component supports as props all the [attributes](https://github.com/muxinc/elements/blob/main/packages/mux-player/REFERENCE.md)
107
+ of the `<mux-player />` web component, plus
108
+ `data`, which is meant to receive data directly in the shape they are provided
109
+ by DatoCMS GraphQL API.
110
+
111
+ `<VideoPlayer />` uses the `data` prop to generate a set of attributes for the
112
+ inner `<mux-player />`.
113
+
114
+ | prop | type | required | description | default |
115
+ | ---- | -------------- | ------------------ | ---------------------------------------------------------------- | ------- |
116
+ | data | `Video` object | :white_check_mark: | The actual response you get from a DatoCMS `video` GraphQL query | |
117
+
118
+ `<VideoPlayer />` generate some default attributes:
119
+
120
+ - when not declared, the `disableCookies` prop is true, unless you explicitly
121
+ set the prop to `false` (therefore it generates a `disable-cookies` attribute)
122
+ - `preload` defaults to `metadata`, for an optimal UX experience together with saved bandwidth
123
+ - the video height and width, when available in the `data` props, are used to
124
+ set a default `aspect-ratio: [width] / [height];` for the `<mux-player />`'s
125
+ `style` attribute
126
+
127
+ All the other props are forwarded to the `<mux-player />` web component that is used internally.
@@ -0,0 +1,138 @@
1
+ <script context="module">const computedTitle = (title) => {
2
+ return title ? { title } : void 0;
3
+ };
4
+ const computedPlaybackId = (muxPlaybackId, playbackId) => {
5
+ if (!(muxPlaybackId || playbackId))
6
+ return void 0;
7
+ return { playbackId: `${muxPlaybackId || playbackId}` };
8
+ };
9
+ const computedAspectRatio = (width, height) => {
10
+ if (!(width && height))
11
+ return void 0;
12
+ return `aspect-ratio: ${width} / ${height}`;
13
+ };
14
+ const computedStyle = (style, width, height) => {
15
+ const styleRules = [computedAspectRatio(width, height), style].filter(Boolean);
16
+ if (styleRules.length === 0)
17
+ return void 0;
18
+ return { style: styleRules.join("; ") };
19
+ };
20
+ const computedPlaceholder = (blurUpThumb) => {
21
+ return blurUpThumb ? { placeholder: blurUpThumb } : void 0;
22
+ };
23
+ export const isNil = (x) => x == void 0;
24
+ export const isKeyOf = (k, o) => {
25
+ if (isNil(o))
26
+ return false;
27
+ return k in o;
28
+ };
29
+ const PropToAttrNameMap = {
30
+ crossOrigin: "crossorigin",
31
+ viewBox: "viewBox",
32
+ playsInline: "playsinline",
33
+ autoPlay: "autoplay",
34
+ playbackRate: "playbackrate",
35
+ playbackRates: "playbackrates"
36
+ };
37
+ const toKebabCase = (string) => string.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
38
+ export const toNativeAttrName = (propName, propValue) => {
39
+ if (typeof propValue === "boolean" && !propValue)
40
+ return void 0;
41
+ if (isKeyOf(propName, PropToAttrNameMap))
42
+ return PropToAttrNameMap[propName];
43
+ if (typeof propValue == void 0)
44
+ return void 0;
45
+ if (/[A-Z]/.test(propName))
46
+ return toKebabCase(propName);
47
+ return propName;
48
+ };
49
+ export const toNativeAttrValue = (propValue, propName) => {
50
+ if (Array.isArray(propValue))
51
+ return propValue.join(" ");
52
+ if (typeof propValue === "boolean")
53
+ return propValue;
54
+ return propValue;
55
+ };
56
+ const toHTMLAttrs = (props = {}) => {
57
+ return Object.entries(props).reduce(
58
+ (transformedProps, [propName, propValue]) => {
59
+ const attrName = toNativeAttrName(propName, propValue);
60
+ if (!attrName) {
61
+ return transformedProps;
62
+ }
63
+ const attrValue = toNativeAttrValue(propValue, propName);
64
+ transformedProps[attrName] = attrValue;
65
+ return transformedProps;
66
+ },
67
+ {}
68
+ );
69
+ };
70
+ </script>
71
+
72
+ <script>import { onMount } from "svelte";
73
+ import mux from "mux-embed";
74
+ export let data = {};
75
+ export let style = void 0;
76
+ export let preload = "metadata";
77
+ export let disableCookies = true;
78
+ let muxPlayerElementImported = false;
79
+ let muxPlayerElement;
80
+ let computedProps;
81
+ $: {
82
+ const { muxPlaybackId, playbackId, title, width, height, blurUpThumb } = data || {};
83
+ computedProps = {
84
+ ...computedTitle(title) || {},
85
+ ...computedPlaybackId(muxPlaybackId, playbackId) || {},
86
+ ...computedStyle(style, width, height) || {},
87
+ ...computedPlaceholder(blurUpThumb) || {},
88
+ disableCookies,
89
+ preload
90
+ };
91
+ }
92
+ $: {
93
+ if (muxPlayerElementImported && muxPlayerElement && Object.hasOwn($$props, "paused")) {
94
+ const paused = $$props.paused;
95
+ if (paused) {
96
+ muxPlayerElement.pause();
97
+ } else {
98
+ muxPlayerElement.play();
99
+ }
100
+ }
101
+ }
102
+ onMount(async () => {
103
+ await import("@mux/mux-player");
104
+ muxPlayerElementImported = true;
105
+ });
106
+ </script>
107
+
108
+ <mux-player
109
+ bind:this={muxPlayerElement}
110
+ stream-type="on-demand"
111
+ {...toHTMLAttrs(computedProps)}
112
+ {...toHTMLAttrs($$restProps)}
113
+ on:abort
114
+ on:canplay
115
+ on:canplaythrough
116
+ on:emptied
117
+ on:loadstart
118
+ on:loadeddata
119
+ on:loadedmetadata
120
+ on:progress
121
+ on:durationchange
122
+ on:volumechange
123
+ on:ratechange
124
+ on:resize
125
+ on:waiting
126
+ on:play
127
+ on:playing
128
+ on:timeupdate
129
+ on:pause
130
+ on:seeking
131
+ on:seeked
132
+ on:stalled
133
+ on:suspend
134
+ on:ended
135
+ on:error
136
+ on:cuepointchange
137
+ on:cuepointschange
138
+ />
@@ -0,0 +1,134 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ import type { CmcdTypes, MaxResolutionValue, MinResolutionValue, PlaybackTypes, RenditionOrderValue, StreamTypes } from '@mux/playback-core/.';
3
+ import type { Tokens } from '@mux/mux-player';
4
+ type ValueOf<T> = T[keyof T];
5
+ type Maybe<T> = T | null;
6
+ type VideoApiAttributes = {
7
+ currentTime: number;
8
+ volume: number;
9
+ paused: boolean;
10
+ src: string | null;
11
+ poster: string;
12
+ playbackRate: number;
13
+ playsInline: boolean;
14
+ preload: string;
15
+ crossOrigin: string;
16
+ autoPlay: boolean | string;
17
+ loop: boolean;
18
+ muted: boolean;
19
+ style: string;
20
+ };
21
+ type MuxMediaPropTypes = {
22
+ audio: boolean;
23
+ envKey: string;
24
+ debug: boolean;
25
+ disableCookies: boolean;
26
+ disablePictureInPicture?: boolean;
27
+ metadata: {
28
+ [k: string]: any;
29
+ };
30
+ extraSourceParams: Record<string, any>;
31
+ _hlsConfig: MuxPlayerElement['_hlsConfig'];
32
+ beaconCollectionDomain: string;
33
+ customDomain: string;
34
+ playbackId: string;
35
+ preferPlayback: ValueOf<PlaybackTypes> | undefined;
36
+ streamType: ValueOf<StreamTypes> | 'll-live' | 'live:dvr' | 'll-live:dvr';
37
+ defaultStreamType: ValueOf<StreamTypes>;
38
+ targetLiveWindow: number;
39
+ startTime: number;
40
+ storyboardSrc: string;
41
+ preferCmcd: ValueOf<CmcdTypes> | undefined;
42
+ };
43
+ export type MuxPlayerProps = {
44
+ class?: string;
45
+ hotkeys?: string[];
46
+ nohotkeys?: boolean;
47
+ defaultHiddenCaptions?: boolean;
48
+ playerSoftwareVersion?: string;
49
+ playerSoftwareName?: string;
50
+ forwardSeekOffset?: number;
51
+ backwardSeekOffset?: number;
52
+ maxResolution?: MaxResolutionValue;
53
+ minResolution?: MinResolutionValue;
54
+ renditionOrder?: RenditionOrderValue;
55
+ metadataVideoId?: string;
56
+ metadataVideoTitle?: string;
57
+ metadataViewerUserId?: string;
58
+ primaryColor?: string;
59
+ secondaryColor?: string;
60
+ accentColor?: string;
61
+ placeholder?: string;
62
+ playbackRates?: number[];
63
+ defaultShowRemainingTime?: boolean;
64
+ defaultDuration?: number;
65
+ noVolumePref?: boolean;
66
+ thumbnailTime?: number;
67
+ title?: string;
68
+ tokens?: Tokens;
69
+ theme?: string;
70
+ themeProps?: {
71
+ [k: string]: any;
72
+ };
73
+ } & Partial<MuxMediaPropTypes> & Partial<VideoApiAttributes>;
74
+ export type Video = {
75
+ /** Title attribute (`title`) for the video */
76
+ title?: Maybe<string>;
77
+ /** The height of the video */
78
+ height?: Maybe<number>;
79
+ /** The width of the video */
80
+ width?: Maybe<number>;
81
+ /** The MUX playbaack ID */
82
+ muxPlaybackId?: Maybe<string>;
83
+ /** The MUX playbaack ID */
84
+ playbackId?: Maybe<string>;
85
+ /** A data: URI containing a blurhash for the video */
86
+ blurUpThumb?: Maybe<string>;
87
+ };
88
+ type KeyTypes = string | number | symbol;
89
+ export declare const isNil: (x: unknown) => x is null | undefined;
90
+ export declare const isKeyOf: <T extends {} = any>(k: KeyTypes, o: Maybe<T> | undefined) => k is keyof T;
91
+ export declare const toNativeAttrName: (propName: string, propValue: any) => string | undefined;
92
+ export declare const toNativeAttrValue: (propValue: any, propName: string) => any;
93
+ import type MuxPlayerElement from '@mux/mux-player';
94
+ declare const __propDef: {
95
+ props: Partial<MuxPlayerProps> & {
96
+ data?: Video | undefined;
97
+ };
98
+ events: {
99
+ abort: UIEvent;
100
+ canplay: Event;
101
+ canplaythrough: Event;
102
+ emptied: Event;
103
+ loadstart: Event;
104
+ loadeddata: Event;
105
+ loadedmetadata: Event;
106
+ progress: ProgressEvent<EventTarget>;
107
+ durationchange: Event;
108
+ volumechange: Event;
109
+ ratechange: Event;
110
+ resize: UIEvent;
111
+ waiting: Event;
112
+ play: Event;
113
+ playing: Event;
114
+ timeupdate: Event;
115
+ pause: Event;
116
+ seeking: Event;
117
+ seeked: Event;
118
+ stalled: Event;
119
+ suspend: Event;
120
+ ended: Event;
121
+ error: ErrorEvent;
122
+ cuepointchange: Event | UIEvent | ProgressEvent<EventTarget> | ErrorEvent | MouseEvent | FocusEvent | AnimationEvent | InputEvent | CompositionEvent | ClipboardEvent | DragEvent | FormDataEvent | PointerEvent | KeyboardEvent | SecurityPolicyViolationEvent | SubmitEvent | TouchEvent | TransitionEvent | WheelEvent;
123
+ cuepointschange: Event | UIEvent | ProgressEvent<EventTarget> | ErrorEvent | MouseEvent | FocusEvent | AnimationEvent | InputEvent | CompositionEvent | ClipboardEvent | DragEvent | FormDataEvent | PointerEvent | KeyboardEvent | SecurityPolicyViolationEvent | SubmitEvent | TouchEvent | TransitionEvent | WheelEvent;
124
+ } & {
125
+ [evt: string]: CustomEvent<any>;
126
+ };
127
+ slots: {};
128
+ };
129
+ type VideoPlayerProps_ = typeof __propDef.props;
130
+ export { VideoPlayerProps_ as VideoPlayerProps };
131
+ export type VideoPlayerEvents = typeof __propDef.events;
132
+ export type VideoPlayerSlots = typeof __propDef.slots;
133
+ export default class VideoPlayer extends SvelteComponentTyped<VideoPlayerProps_, VideoPlayerEvents, VideoPlayerSlots> {
134
+ }
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';
@@ -0,0 +1,132 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import '@testing-library/jest-dom';
3
+ import { render } from '@testing-library/svelte';
4
+ import { VideoPlayer } from '../../..';
5
+ describe('VideoPlayer', () => {
6
+ describe('when data object', () => {
7
+ describe('is complete', () => {
8
+ const data = {
9
+ muxPlaybackId: 'ip028MAXF026dU900bKiyNDttjonw7A1dFY',
10
+ title: 'Title',
11
+ width: 1080,
12
+ height: 1920,
13
+ blurUpThumb: 'data:image/bmp;base64,Qk0eAAAAAAAAABoAAAAMAAAAAQABAAEAGAAAAP8A'
14
+ };
15
+ it('unwraps data into props ready for MUX player', () => {
16
+ const props = { data };
17
+ const { container } = render(VideoPlayer, { props });
18
+ expect(container).toMatchSnapshot();
19
+ });
20
+ describe('and `class` is passed', () => {
21
+ it('uses it for the <mux-player /> element', () => {
22
+ const props = { data, class: 'main-player' };
23
+ const { container } = render(VideoPlayer, { props });
24
+ expect(container).toMatchSnapshot();
25
+ });
26
+ });
27
+ describe('and `autoplay` is passed', () => {
28
+ it('uses it for the <mux-player /> element', () => {
29
+ const props = { data, autoplay: true };
30
+ const { container } = render(VideoPlayer, { props });
31
+ expect(container).toMatchSnapshot();
32
+ });
33
+ describe('with value `false`', () => {
34
+ it("doesn't use it for the <mux-player /> element", () => {
35
+ const props = { data, autoplay: false };
36
+ const { container } = render(VideoPlayer, { props });
37
+ expect(container).toMatchSnapshot();
38
+ });
39
+ });
40
+ });
41
+ describe('and `disableCookies` is passed', () => {
42
+ it('uses it for the <mux-player /> element', () => {
43
+ const props = { data, disableCookies: true };
44
+ const { container } = render(VideoPlayer, { props });
45
+ expect(container).toMatchSnapshot();
46
+ });
47
+ describe('with value `false`', () => {
48
+ it("doesn't use it for the <mux-player /> element", () => {
49
+ const props = { data, disableCookies: false };
50
+ const { container } = render(VideoPlayer, { props });
51
+ expect(container).toMatchSnapshot();
52
+ });
53
+ });
54
+ });
55
+ describe('and `preload` is passed', () => {
56
+ it('uses it for the <mux-player /> element', () => {
57
+ const props = { data, preload: "auto" };
58
+ const { container } = render(VideoPlayer, { props });
59
+ expect(container).toMatchSnapshot();
60
+ });
61
+ describe('with value `none`', () => {
62
+ it("doesn't use it for the <mux-player /> element", () => {
63
+ const props = { data, preload: "none" };
64
+ const { container } = render(VideoPlayer, { props });
65
+ expect(container).toMatchSnapshot();
66
+ });
67
+ });
68
+ });
69
+ describe('and a style string is passed', () => {
70
+ describe("that doesn't include aspect ratio", () => {
71
+ it('adds computed aspect ratio', () => {
72
+ const props = { data, style: 'margin: auto;' };
73
+ const { container } = render(VideoPlayer, { props });
74
+ expect(container).toMatchSnapshot();
75
+ });
76
+ });
77
+ describe('that defines the aspect ratio property', () => {
78
+ describe('as a valid CSS value', () => {
79
+ it('uses the passed value to override default aspect ratio', () => {
80
+ const props = { data, style: 'aspect-ratio: auto' };
81
+ const { container } = render(VideoPlayer, { props });
82
+ expect(container).toMatchSnapshot();
83
+ });
84
+ });
85
+ });
86
+ });
87
+ });
88
+ describe('contains `muxPlaybackId`', () => {
89
+ const data = {
90
+ muxPlaybackId: 'ip028MAXF026dU900bKiyNDttjonw7A1dFY'
91
+ };
92
+ it('uses it for `playbackId`', () => {
93
+ const props = { data };
94
+ const { container } = render(VideoPlayer, { props });
95
+ expect(container).toMatchSnapshot();
96
+ });
97
+ });
98
+ describe('contains `playbackId`', () => {
99
+ const data = {
100
+ playbackId: 'ip028MAXF026dU900bKiyNDttjonw7A1dFY'
101
+ };
102
+ it('uses it for `playbackId`', () => {
103
+ const props = { data };
104
+ const { container } = render(VideoPlayer, { props });
105
+ expect(container).toMatchSnapshot();
106
+ });
107
+ });
108
+ describe('lacks of `width` and `height` values', () => {
109
+ const data = {
110
+ muxPlaybackId: 'ip028MAXF026dU900bKiyNDttjonw7A1dFY',
111
+ title: 'Title'
112
+ };
113
+ it('avoids adding aspect ratio', () => {
114
+ const props = { data };
115
+ const { container } = render(VideoPlayer, { props });
116
+ expect(container).toMatchSnapshot();
117
+ });
118
+ });
119
+ describe('lacks of `title` value', () => {
120
+ const data = {
121
+ muxPlaybackId: 'ip028MAXF026dU900bKiyNDttjonw7A1dFY',
122
+ width: 1080,
123
+ height: 1920
124
+ };
125
+ it('avoids adding a title', () => {
126
+ const props = { data };
127
+ const { container } = render(VideoPlayer, { props });
128
+ expect(container).toMatchSnapshot();
129
+ });
130
+ });
131
+ });
132
+ });
@@ -0,0 +1,230 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`VideoPlayer > when data object > contains \`muxPlaybackId\` > uses it for \`playbackId\` 1`] = `
4
+ <body>
5
+ <div>
6
+ <mux-player
7
+ disable-cookies="true"
8
+ playback-id="ip028MAXF026dU900bKiyNDttjonw7A1dFY"
9
+ preload="metadata"
10
+ stream-type="on-demand"
11
+ />
12
+ <!--&lt;VideoPlayer&gt;-->
13
+ </div>
14
+ </body>
15
+ `;
16
+
17
+ exports[`VideoPlayer > when data object > contains \`playbackId\` > uses it for \`playbackId\` 1`] = `
18
+ <body>
19
+ <div>
20
+ <mux-player
21
+ disable-cookies="true"
22
+ playback-id="ip028MAXF026dU900bKiyNDttjonw7A1dFY"
23
+ preload="metadata"
24
+ stream-type="on-demand"
25
+ />
26
+ <!--&lt;VideoPlayer&gt;-->
27
+ </div>
28
+ </body>
29
+ `;
30
+
31
+ exports[`VideoPlayer > when data object > is complete > and \`autoplay\` is passed > uses it for the <mux-player /> element 1`] = `
32
+ <body>
33
+ <div>
34
+ <mux-player
35
+ autoplay="true"
36
+ disable-cookies="true"
37
+ placeholder="data:image/bmp;base64,Qk0eAAAAAAAAABoAAAAMAAAAAQABAAEAGAAAAP8A"
38
+ playback-id="ip028MAXF026dU900bKiyNDttjonw7A1dFY"
39
+ preload="metadata"
40
+ stream-type="on-demand"
41
+ style="aspect-ratio: 1080 / 1920;"
42
+ title="Title"
43
+ />
44
+ <!--&lt;VideoPlayer&gt;-->
45
+ </div>
46
+ </body>
47
+ `;
48
+
49
+ exports[`VideoPlayer > when data object > is complete > and \`autoplay\` is passed > with value \`false\` > doesn't use it for the <mux-player /> element 1`] = `
50
+ <body>
51
+ <div>
52
+ <mux-player
53
+ disable-cookies="true"
54
+ placeholder="data:image/bmp;base64,Qk0eAAAAAAAAABoAAAAMAAAAAQABAAEAGAAAAP8A"
55
+ playback-id="ip028MAXF026dU900bKiyNDttjonw7A1dFY"
56
+ preload="metadata"
57
+ stream-type="on-demand"
58
+ style="aspect-ratio: 1080 / 1920;"
59
+ title="Title"
60
+ />
61
+ <!--&lt;VideoPlayer&gt;-->
62
+ </div>
63
+ </body>
64
+ `;
65
+
66
+ exports[`VideoPlayer > when data object > is complete > and \`class\` is passed > uses it for the <mux-player /> element 1`] = `
67
+ <body>
68
+ <div>
69
+ <mux-player
70
+ class="main-player"
71
+ disable-cookies="true"
72
+ placeholder="data:image/bmp;base64,Qk0eAAAAAAAAABoAAAAMAAAAAQABAAEAGAAAAP8A"
73
+ playback-id="ip028MAXF026dU900bKiyNDttjonw7A1dFY"
74
+ preload="metadata"
75
+ stream-type="on-demand"
76
+ style="aspect-ratio: 1080 / 1920;"
77
+ title="Title"
78
+ />
79
+ <!--&lt;VideoPlayer&gt;-->
80
+ </div>
81
+ </body>
82
+ `;
83
+
84
+ exports[`VideoPlayer > when data object > is complete > and \`disableCookies\` is passed > uses it for the <mux-player /> element 1`] = `
85
+ <body>
86
+ <div>
87
+ <mux-player
88
+ disable-cookies="true"
89
+ placeholder="data:image/bmp;base64,Qk0eAAAAAAAAABoAAAAMAAAAAQABAAEAGAAAAP8A"
90
+ playback-id="ip028MAXF026dU900bKiyNDttjonw7A1dFY"
91
+ preload="metadata"
92
+ stream-type="on-demand"
93
+ style="aspect-ratio: 1080 / 1920;"
94
+ title="Title"
95
+ />
96
+ <!--&lt;VideoPlayer&gt;-->
97
+ </div>
98
+ </body>
99
+ `;
100
+
101
+ exports[`VideoPlayer > when data object > is complete > and \`disableCookies\` is passed > with value \`false\` > doesn't use it for the <mux-player /> element 1`] = `
102
+ <body>
103
+ <div>
104
+ <mux-player
105
+ placeholder="data:image/bmp;base64,Qk0eAAAAAAAAABoAAAAMAAAAAQABAAEAGAAAAP8A"
106
+ playback-id="ip028MAXF026dU900bKiyNDttjonw7A1dFY"
107
+ preload="metadata"
108
+ stream-type="on-demand"
109
+ style="aspect-ratio: 1080 / 1920;"
110
+ title="Title"
111
+ />
112
+ <!--&lt;VideoPlayer&gt;-->
113
+ </div>
114
+ </body>
115
+ `;
116
+
117
+ exports[`VideoPlayer > when data object > is complete > and \`preload\` is passed > uses it for the <mux-player /> element 1`] = `
118
+ <body>
119
+ <div>
120
+ <mux-player
121
+ disable-cookies="true"
122
+ placeholder="data:image/bmp;base64,Qk0eAAAAAAAAABoAAAAMAAAAAQABAAEAGAAAAP8A"
123
+ playback-id="ip028MAXF026dU900bKiyNDttjonw7A1dFY"
124
+ preload="auto"
125
+ stream-type="on-demand"
126
+ style="aspect-ratio: 1080 / 1920;"
127
+ title="Title"
128
+ />
129
+ <!--&lt;VideoPlayer&gt;-->
130
+ </div>
131
+ </body>
132
+ `;
133
+
134
+ exports[`VideoPlayer > when data object > is complete > and \`preload\` is passed > with value \`none\` > doesn't use it for the <mux-player /> element 1`] = `
135
+ <body>
136
+ <div>
137
+ <mux-player
138
+ disable-cookies="true"
139
+ placeholder="data:image/bmp;base64,Qk0eAAAAAAAAABoAAAAMAAAAAQABAAEAGAAAAP8A"
140
+ playback-id="ip028MAXF026dU900bKiyNDttjonw7A1dFY"
141
+ preload="none"
142
+ stream-type="on-demand"
143
+ style="aspect-ratio: 1080 / 1920;"
144
+ title="Title"
145
+ />
146
+ <!--&lt;VideoPlayer&gt;-->
147
+ </div>
148
+ </body>
149
+ `;
150
+
151
+ exports[`VideoPlayer > when data object > is complete > and a style string is passed > that defines the aspect ratio property > as a valid CSS value > uses the passed value to override default aspect ratio 1`] = `
152
+ <body>
153
+ <div>
154
+ <mux-player
155
+ disable-cookies="true"
156
+ placeholder="data:image/bmp;base64,Qk0eAAAAAAAAABoAAAAMAAAAAQABAAEAGAAAAP8A"
157
+ playback-id="ip028MAXF026dU900bKiyNDttjonw7A1dFY"
158
+ preload="metadata"
159
+ stream-type="on-demand"
160
+ style="aspect-ratio: auto;"
161
+ title="Title"
162
+ />
163
+ <!--&lt;VideoPlayer&gt;-->
164
+ </div>
165
+ </body>
166
+ `;
167
+
168
+ exports[`VideoPlayer > when data object > is complete > and a style string is passed > that doesn't include aspect ratio > adds computed aspect ratio 1`] = `
169
+ <body>
170
+ <div>
171
+ <mux-player
172
+ disable-cookies="true"
173
+ placeholder="data:image/bmp;base64,Qk0eAAAAAAAAABoAAAAMAAAAAQABAAEAGAAAAP8A"
174
+ playback-id="ip028MAXF026dU900bKiyNDttjonw7A1dFY"
175
+ preload="metadata"
176
+ stream-type="on-demand"
177
+ style="aspect-ratio: 1080 / 1920; margin: auto;"
178
+ title="Title"
179
+ />
180
+ <!--&lt;VideoPlayer&gt;-->
181
+ </div>
182
+ </body>
183
+ `;
184
+
185
+ exports[`VideoPlayer > when data object > is complete > unwraps data into props ready for MUX player 1`] = `
186
+ <body>
187
+ <div>
188
+ <mux-player
189
+ disable-cookies="true"
190
+ placeholder="data:image/bmp;base64,Qk0eAAAAAAAAABoAAAAMAAAAAQABAAEAGAAAAP8A"
191
+ playback-id="ip028MAXF026dU900bKiyNDttjonw7A1dFY"
192
+ preload="metadata"
193
+ stream-type="on-demand"
194
+ style="aspect-ratio: 1080 / 1920;"
195
+ title="Title"
196
+ />
197
+ <!--&lt;VideoPlayer&gt;-->
198
+ </div>
199
+ </body>
200
+ `;
201
+
202
+ exports[`VideoPlayer > when data object > lacks of \`title\` value > avoids adding a title 1`] = `
203
+ <body>
204
+ <div>
205
+ <mux-player
206
+ disable-cookies="true"
207
+ playback-id="ip028MAXF026dU900bKiyNDttjonw7A1dFY"
208
+ preload="metadata"
209
+ stream-type="on-demand"
210
+ style="aspect-ratio: 1080 / 1920;"
211
+ />
212
+ <!--&lt;VideoPlayer&gt;-->
213
+ </div>
214
+ </body>
215
+ `;
216
+
217
+ exports[`VideoPlayer > when data object > lacks of \`width\` and \`height\` values > avoids adding aspect ratio 1`] = `
218
+ <body>
219
+ <div>
220
+ <mux-player
221
+ disable-cookies="true"
222
+ playback-id="ip028MAXF026dU900bKiyNDttjonw7A1dFY"
223
+ preload="metadata"
224
+ stream-type="on-demand"
225
+ title="Title"
226
+ />
227
+ <!--&lt;VideoPlayer&gt;-->
228
+ </div>
229
+ </body>
230
+ `;
@@ -3,6 +3,7 @@ import type { SvelteComponent } from 'svelte';
3
3
  export { default as StructuredText } from './components/StructuredText/StructuredText.svelte';
4
4
  export { default as Image } from './components/Image/Image.svelte';
5
5
  export { default as Head } from './components/Head/Head.svelte';
6
+ export { default as VideoPlayer } from './components/VideoPlayer/VideoPlayer.svelte';
6
7
  export type PredicateComponentTuple = [
7
8
  (n: Node) => boolean,
8
9
  new (...any: any) => SvelteComponent
package/package/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { default as StructuredText } from './components/StructuredText/StructuredText.svelte';
2
2
  export { default as Image } from './components/Image/Image.svelte';
3
3
  export { default as Head } from './components/Head/Head.svelte';
4
+ export { default as VideoPlayer } from './components/VideoPlayer/VideoPlayer.svelte';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datocms/svelte",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "A set of components and utilities to work faster with DatoCMS in Svelte",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -24,7 +24,13 @@
24
24
  "lint": "prettier --plugin-search-dir . --check . && eslint .",
25
25
  "format": "prettier --plugin-search-dir . --write ."
26
26
  },
27
+ "peerDependencies": {
28
+ "@mux/mux-player": "*",
29
+ "svelte": "^3.59.2 || ^4.0.0"
30
+ },
27
31
  "devDependencies": {
32
+ "@mux/mux-player": "*",
33
+ "@mux/playback-core": "^0.22.1",
28
34
  "@sveltejs/adapter-auto": "^3.0.0",
29
35
  "@sveltejs/kit": "^2.0.0",
30
36
  "@sveltejs/package": "^2.0.0",
@@ -38,8 +44,9 @@
38
44
  "eslint": "^8.56.0",
39
45
  "eslint-config-prettier": "^8.10.0",
40
46
  "eslint-plugin-svelte": "^2.35.1",
41
- "jsdom": "^21.1.2",
47
+ "jsdom": "^24.0.0",
42
48
  "jsdom-testing-mocks": "^1.11.0",
49
+ "np": "^10.0.0",
43
50
  "prettier": "^2.8.8",
44
51
  "prettier-plugin-svelte": "^2.10.1",
45
52
  "svelte": "^4.0.0",
@@ -49,9 +56,6 @@
49
56
  "vite": "^5.0.0",
50
57
  "vitest": "^1.0.0"
51
58
  },
52
- "peerDependencies": {
53
- "svelte": "^3.59.2 || ^4.0.0"
54
- },
55
59
  "type": "module",
56
60
  "dependencies": {
57
61
  "datocms-structured-text-utils": "^2.0.4",
@@ -221,4 +225,4 @@
221
225
  ]
222
226
  }
223
227
  }
224
- }
228
+ }