Skip to content

Commit e776478

Browse files
author
Dobromir Hristov
committed
feat: add support for @Video directive
1 parent 8d8d207 commit e776478

File tree

5 files changed

+211
-0
lines changed

5 files changed

+211
-0
lines changed

src/components/ContentNode.vue

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import InlineImage from './ContentNode/InlineImage.vue';
2020
import Reference from './ContentNode/Reference.vue';
2121
import Table from './ContentNode/Table.vue';
2222
import StrikeThrough from './ContentNode/StrikeThrough.vue';
23+
import InlineVideo from './ContentNode/InlineVideo.vue';
2324
2425
const BlockType = {
2526
aside: 'aside',
@@ -38,6 +39,7 @@ const InlineType = {
3839
codeVoice: 'codeVoice',
3940
emphasis: 'emphasis',
4041
image: 'image',
42+
video: 'video',
4143
inlineHead: 'inlineHead',
4244
link: 'link',
4345
newTerm: 'newTerm',
@@ -301,6 +303,19 @@ function renderNode(createElement, references) {
301303
null
302304
);
303305
}
306+
case InlineType.video: {
307+
if (node.metadata && node.metadata.abstract) {
308+
return renderFigure(node);
309+
}
310+
311+
return references[node.identifier] ? (
312+
createElement(InlineVideo, {
313+
props: {
314+
identifier: node.identifier,
315+
},
316+
})
317+
) : null;
318+
}
304319
case InlineType.link:
305320
// Note: `InlineType.link` has been deprecated, but may still be found in old JSON.
306321
return createElement('a', { attrs: { href: node.destination } }, (

src/components/ContentNode/FigureCaption.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ export default {
3131
3232
.caption {
3333
@include font-styles(documentation-figcaption);
34+
35+
&:last-child {
36+
margin-top: $stacked-margin-xlarge;
37+
}
3438
}
3539
3640
/deep/ p {
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<!--
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2021 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See https://swift.org/LICENSE.txt for license information
8+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
-->
10+
11+
<template>
12+
<Asset
13+
:identifier="identifier"
14+
:video-autoplays="false"
15+
:video-muted="false"
16+
:showsReplayButton="!isClientMobile"
17+
:showsVideoControls="isClientMobile"
18+
/>
19+
</template>
20+
21+
<script>
22+
import Asset from 'docc-render/components/Asset.vue';
23+
import isClientMobile from 'docc-render/mixins/isClientMobile';
24+
25+
export default {
26+
name: 'InlineVideo',
27+
mixins: [isClientMobile],
28+
components: { Asset },
29+
props: {
30+
identifier: {
31+
type: String,
32+
required: true,
33+
},
34+
},
35+
};
36+
</script>
37+
38+
<style lang="scss" scoped>
39+
/deep/ video {
40+
display: block;
41+
margin-left: auto;
42+
margin-right: auto;
43+
object-fit: contain;
44+
max-width: 100%;
45+
}
46+
</style>

tests/unit/components/ContentNode.spec.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import InlineImage from 'docc-render/components/ContentNode/InlineImage.vue';
2121
import Reference from 'docc-render/components/ContentNode/Reference.vue';
2222
import Table from 'docc-render/components/ContentNode/Table.vue';
2323
import StrikeThrough from 'docc-render/components/ContentNode/StrikeThrough.vue';
24+
import InlineVideo from '@/components/ContentNode/InlineVideo.vue';
2425

2526
const { TableHeaderStyle } = ContentNode.constants;
2627

@@ -510,6 +511,100 @@ describe('ContentNode', () => {
510511
});
511512
});
512513

514+
describe('with type="video"', () => {
515+
const identifier = 'video.mp4';
516+
const references = {
517+
[identifier]: {
518+
identifier,
519+
variants: [
520+
{
521+
traits: ['2x', 'light'],
522+
url: '',
523+
size: { width: 1202, height: 630 },
524+
},
525+
],
526+
},
527+
};
528+
529+
it('renders an `InlineVideo`', () => {
530+
const wrapper = mountWithItem({
531+
type: 'video',
532+
identifier,
533+
}, references);
534+
535+
const inlineVideo = wrapper.find('.content').find(InlineVideo);
536+
expect(inlineVideo.exists()).toBe(true);
537+
expect(inlineVideo.props('identifier')).toEqual(identifier);
538+
});
539+
540+
it('does not crash with missing video reference data', () => {
541+
expect(() => mountWithItem({
542+
type: 'video',
543+
identifier,
544+
}, {})).not.toThrow();
545+
});
546+
547+
it('renders a `Figure`/`FigureCaption` with metadata', () => {
548+
const metadata = {
549+
anchor: 'foo',
550+
abstract: [{
551+
type: 'paragraph',
552+
inlineContent: [{ type: 'text', text: 'blah' }],
553+
}],
554+
};
555+
const wrapper = mountWithItem({
556+
type: 'video',
557+
identifier,
558+
metadata,
559+
}, references);
560+
561+
const figure = wrapper.find(Figure);
562+
expect(figure.exists()).toBe(true);
563+
expect(figure.props('anchor')).toBe('foo');
564+
expect(figure.contains(InlineVideo)).toBe(true);
565+
566+
const caption = wrapper.find(FigureCaption);
567+
expect(caption.exists()).toBe(true);
568+
expect(caption.contains('p')).toBe(true);
569+
expect(caption.props('title')).toBe(metadata.title);
570+
expect(caption.text()).toContain('blah');
571+
});
572+
573+
it('renders a `Figure`/`FigureCaption` without an anchor, with text under the video', () => {
574+
const metadata = {
575+
abstract: [{
576+
type: 'paragraph',
577+
inlineContent: [{ type: 'text', text: 'blah' }],
578+
}],
579+
};
580+
const wrapper = mountWithItem({
581+
type: 'video',
582+
identifier,
583+
metadata,
584+
}, references);
585+
586+
const figure = wrapper.find(Figure);
587+
expect(figure.exists()).toBe(true);
588+
expect(figure.props('anchor')).toBeFalsy();
589+
expect(figure.contains(InlineVideo)).toBe(true);
590+
591+
const caption = wrapper.find(FigureCaption);
592+
expect(caption.exists()).toBe(true);
593+
expect(caption.contains('p')).toBe(true);
594+
expect(caption.props('title')).toBeFalsy();
595+
expect(caption.text()).toContain('blah');
596+
// assert figurerecaption is below the image
597+
expect(figure.html()).toMatchInlineSnapshot(`
598+
<figure-stub>
599+
<inlinevideo-stub identifier="video.mp4"></inlinevideo-stub>
600+
<figurecaption-stub>
601+
<p>blah</p>
602+
</figurecaption-stub>
603+
</figure-stub>
604+
`);
605+
});
606+
});
607+
513608
describe('with type="link"', () => {
514609
it('renders a <a>', () => {
515610
const wrapper = mountWithItem({
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* This source file is part of the Swift.org open source project
3+
*
4+
* Copyright (c) 2021 Apple Inc. and the Swift project authors
5+
* Licensed under Apache License v2.0 with Runtime Library Exception
6+
*
7+
* See https://swift.org/LICENSE.txt for license information
8+
* See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
import InlineVideo from '@/components/ContentNode/InlineVideo.vue';
12+
import { shallowMount } from '@vue/test-utils';
13+
import Asset from '@/components/Asset.vue';
14+
import isClientMobile from 'docc-render/mixins/isClientMobile';
15+
16+
jest.mock('docc-render/mixins/isClientMobile');
17+
18+
isClientMobile.computed.isClientMobile.mockReturnValue(false);
19+
20+
const defaultProps = {
21+
identifier: 'foo',
22+
};
23+
24+
const createWrapper = () => shallowMount(InlineVideo, {
25+
propsData: defaultProps,
26+
});
27+
28+
describe('InlineVideo', () => {
29+
it('renders an Asset on desktop', () => {
30+
const wrapper = createWrapper();
31+
expect(wrapper.find(Asset).props()).toEqual({
32+
identifier: defaultProps.identifier,
33+
videoAutoplays: false,
34+
videoMuted: false,
35+
showsReplayButton: true,
36+
showsVideoControls: false,
37+
});
38+
});
39+
40+
it('renders an Asset, on a mobile device', () => {
41+
isClientMobile.computed.isClientMobile.mockReturnValue(true);
42+
const wrapper = createWrapper();
43+
expect(wrapper.find(Asset).props()).toEqual({
44+
identifier: defaultProps.identifier,
45+
videoAutoplays: false,
46+
videoMuted: false,
47+
showsReplayButton: false,
48+
showsVideoControls: true,
49+
});
50+
});
51+
});

0 commit comments

Comments
 (0)