Skip to content

Commit

Permalink
feat(embed): New component b-embed for responsive video embeds (#985)
Browse files Browse the repository at this point in the history
* feat(embed): New b-embed responsive embed component

* Update embed.js

* Update index.js

* Update embed.js

* Create README.md

* Create meta.json

* Update meta.json

* Create index.js

* Update index.js

* Update embed.js

* Update README.md

* Create demo.html

* Create demo.js

* Create embed.spec.js

* Update embed.spec.js
  • Loading branch information
tmorehouse committed Sep 3, 2017
1 parent 01735e6 commit e29c429
Show file tree
Hide file tree
Showing 10 changed files with 185 additions and 2 deletions.
54 changes: 54 additions & 0 deletions docs/components/embed/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Responsive Embeds

> Create responsive video or slideshow embeds based on the width of the parent by creating
an intrinsic ratio that scales on any device.

Rules are directly applied to `<iframe>`, `<embed>`, `<video>`, and `<object>` elements

```html
<div>
<b-embed type="iframe"
aspect="16by9"
src="https://www.youtube.com/embed/zpOULjyy-n8?rel=0"
allowfullscreen
></b-embed>
</div>

<!-- b-embed-1.vue -->
```

## Embed types
Supported embed types are `iframe` (default), `video`, `embed` and `object`, which
translate to the standard HTML `<iframe>`, `<video>`, `<embed>` and `<object>` elements.

Set the type of embed you would like via the `type` prop.


## Aspect ratios
Aspect ratios can be set via the `aspect` prop. Supported aspect rations are:
`21by9` (21:9), `16by9` (16:9), `4by3` (4:3) and `1by1` (1:1). The default aspect
is `16by9`.


## Wrapper element
The Responsive embed is wrapped in an outer element (default is `div`) to enforce
the responsive aspect ratio. You can change this tag via the `tag` prop.


## Attributes and Child Elements
Any additional attributes provided to `<b-embed>` (other than the above `type`,
`aspect` and `tag` props are applied to the inner embeded element (i.s. `iframe`,
`video`, `embed` and `object`).

Any children elements between the opening and closing `<b-embed>` will be placed
inside the inner embded element. Note that type `iframe` does not support any children.

**Example: Responsive embdeding of an HTML5 `<video>`**
```html
<b-embed type="video" aspect="4by3" controls poster="poster.png">
<source src="devstories.webm"
type='video/webm;codecs="vp8, vorbis"' />
<source src="devstories.mp4"
type='video/mp4;codecs="avc1.42E01E, mp4a.40.2"' />
</b-embed>
```
4 changes: 4 additions & 0 deletions docs/components/embed/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import meta from './meta.json';
import readme from './README.md';

export default {meta, readme};
5 changes: 5 additions & 0 deletions docs/components/embed/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"title": "Embeds",
"component": "bEmbed",
"new": true
}
1 change: 1 addition & 0 deletions docs/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default {
'carousel': require('./carousel').default,
'collapse': require('./collapse').default,
'dropdown': require('./dropdown').default,
'embed': require('./embed').default,
'form': require('./form').default,
'form-input': require('./form-input').default,
'form-textarea': require('./form-textarea').default,
Expand Down
34 changes: 34 additions & 0 deletions lib/components/embed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { mergeData } from "../utils";

export const props = {
type: {
type: String,
default: "iframe"
},
tag: {
type: String,
default: "div"
},
aspect: {
type: String,
default: "16by9"
}
};

export default {
functional: true,
props,
render(h, { props, data, children }) {
return h(
props.tag,
{
ref: data.ref,
staticClass: "embed-responsive",
class: {
[`embed-responsive-${props.aspect}`]: Boolean(props.aspect)
}
},
[h(props.type, mergeData(data, { ref: '', staticClass: "embed-responsive-item" }), children)]
);
}
};
2 changes: 2 additions & 0 deletions lib/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import bDropdownItem from "./dropdown-item";
import bDropdownItemButton from "./dropdown-item-button";
import bDropdownDivider from "./dropdown-divider";
import bDropdownHeader from "./dropdown-header";
import bEmbed from "./embed";
import bForm from "./form";
import bFormRow from "./form-row";
import bFormText from "./form-text";
Expand Down Expand Up @@ -100,6 +101,7 @@ export {
bDropdownDivider as bDdDivider,
bDropdownHeader,
bDropdownHeader as bDdHeader,
bEmbed,
bForm,
bFormRow,
bFormText,
Expand Down
67 changes: 67 additions & 0 deletions tests/components/embed.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { loadFixture, testVM } from "../helpers";

describe("embed", async () => {
beforeEach(loadFixture("embed"));
testVM();

it("default should be rendered with outer tag 'div'", async () => {
const { app: { $refs } } = window;
expect($refs.default).toBeElement("div");
});

it("tag should be rendered with outer tag 'aside'", async () => {
const { app: { $refs } } = window;
expect($refs.tag).toBeElement("aside");
});

it("default should be rendered with inner tag 'iframe'", async () => {
const { app: { $refs } } = window;
expect($refs.default.children[0]).toBeElement("iframe");
});

it("type should be rendered with inner tag 'video'", async () => {
const { app: { $refs } } = window;
expect($refs.type.children[0]).toBeElement("video");
});

it("all should be rendered with default outer class 'embed-responsive'", async () => {
const { app: { $refs } } = window;
["default","tag","type","aspect","attributes","children"].forEach(ref => {
expect($refs[ref]).toHaveClass("embed-responsive");
});
});

it("all should be rendered with default inner class 'embed-responsive-item'", async () => {
const { app: { $refs } } = window;
["default","tag","type","aspect","attributes","children"].forEach(ref => {
expect($refs[ref].children[0]).toHaveClass("embed-responsive-item");
});
});

it("default should be rendered with outer class 'embed-responsive-16by9'", async () => {
const { app: { $refs } } = window;
expect($refs.default).toHaveClass("embed-responsive-16by9");
});

it("aspect should be rendered with outer class 'embed-responsive-4by3'", async () => {
const { app: { $refs } } = window;
expect($refs.aspect).toHaveClass("embed-responsive-4by3");
});

it("attributes should have attribute 'foo=bar' on inner tag", async () => {
const { app: { $refs } } = window;
expect($refs.attributes.children[0].hasAttribute("foo")).toBe(true);
expect($refs.attributes.children[0].getAttribute("foo")).toBe("bar");
});

it("attributes should have attribute 'baz' on inner tag", async () => {
const { app: { $refs } } = window;
expect($refs.attributes.children[0].hasAttribute("baz")).toBe(true);
});

it("children should be rendered inside inner element", async () => {
const { app: { $refs } } = window;
expect($refs.children.children[0].children[0]).toBeElement("source");
});

});
13 changes: 13 additions & 0 deletions tests/fixtures/embed/demo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<div id="app">
<b-embed ref="default"></b-embed>
<br>
<b-embed ref="tag" tag="aside"></b-embed>
<br>
<b-embed ref="type" type="video"></b-embed>
<br>
<b-embed ref="aspect" type="video" aspect="4by3"></b-embed>
<br>
<b-embed ref="attributes" type="video" foo="bar" baz></b-embed>
<br>
<b-embed ref="children" type="video"><source></source></b-embed>
</div>
3 changes: 3 additions & 0 deletions tests/fixtures/embed/demo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
window.app = new Vue({
el: '#app'
});
4 changes: 2 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1909,9 +1909,9 @@ css-color-names@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"

"css-loader@git+https://github.com/nuxt/css-loader.git":
"css-loader@https://github.com/nuxt/css-loader.git":
version "0.28.5"
resolved "git+https://github.com/nuxt/css-loader.git#43674428e42dd208f6192cfc0b1679935a0bae4b"
resolved "https://github.com/nuxt/css-loader.git#43674428e42dd208f6192cfc0b1679935a0bae4b"
dependencies:
babel-code-frame "^6.11.0"
css-selector-tokenizer "^0.7.0"
Expand Down

0 comments on commit e29c429

Please sign in to comment.