Skip to content

Commit e09bf96

Browse files
osiuxMichaelDeBoey
andcommitted
feat(GIPHY): Add support for GIPHY (#55)
* Support Giphy embeds * use oembed endpoint to fetch dimensions * Update README * Update transformer * Update tests * resolve comments * missing change * one more.. * last changes * Update transformer * Update tests Co-authored-by: Michaël De Boey <info@michaeldeboey.be>
1 parent 3fd1492 commit e09bf96

File tree

7 files changed

+263
-5
lines changed

7 files changed

+263
-5
lines changed

README.md

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@
1919
## The problem
2020

2121
Trying to embed well known services (like [CodePen][codepen],
22-
[CodeSandbox][codesandbox], [Instagram][instagram], [Lichess][lichess],
23-
[Slides][slides], [SoundCloud][soundcloud], [Spotify][spotify],
24-
[Streamable][streamable], [Twitter][twitter] or [YouTube][youtube]) into your
25-
[Gatsby][gatsby] website can be hard, since you have to know how this needs to
26-
be done for all of these different services.
22+
[CodeSandbox][codesandbox], [GIPHY][giphy], [Instagram][instagram],
23+
[Lichess][lichess], [Slides][slides], [SoundCloud][soundcloud],
24+
[Spotify][spotify], [Streamable][streamable], [Twitter][twitter] or
25+
[YouTube][youtube]) into your [Gatsby][gatsby] website can be hard, since you
26+
have to know how this needs to be done for all of these different services.
2727

2828
## This solution
2929

@@ -42,6 +42,7 @@ and replace it with the proper embed-code.
4242
- [Supported services](#supported-services)
4343
- [CodePen](#codepen)
4444
- [CodeSandbox](#codesandbox)
45+
- [GIPHY](#giphy)
4546
- [Instagram](#instagram)
4647
- [Lichess](#lichess)
4748
- [Slides](#slides)
@@ -124,6 +125,30 @@ https://codesandbox.io/s/ynn88nx9x?view=split
124125
></iframe>
125126
```
126127

128+
### GIPHY
129+
130+
#### Usage
131+
132+
```md
133+
https://giphy.com/gifs/howtogiphygifs-how-to-XatG8bioEwwVO
134+
```
135+
136+
#### Result
137+
138+
```html
139+
<div style="width:100%;height:0;padding-bottom:63%;position:relative;">
140+
<iframe
141+
src="https://giphy.com/embed/XatG8bioEwwVO"
142+
width="100%"
143+
height="100%"
144+
style="position:absolute"
145+
frameborder="0"
146+
class="giphy-embed"
147+
allowfullscreen
148+
></iframe>
149+
</div>
150+
```
151+
127152
### Instagram
128153

129154
The returned HTML snippet from the Instagram transformer will only be
@@ -549,6 +574,7 @@ Thanks goes to these people ([emoji key][emojis]):
549574

550575
<!-- markdownlint-enable -->
551576
<!-- prettier-ignore-end -->
577+
552578
<!-- ALL-CONTRIBUTORS-LIST:END -->
553579

554580
This project follows the [all-contributors][all-contributors] specification.
@@ -587,6 +613,7 @@ MIT
587613
[gatsby]: https://github.com/gatsbyjs/gatsby
588614
[gatsby-plugin-instagram-embed]: https://github.com/jlengstorf/gatsby-plugin-instagram-embed
589615
[gatsby-plugin-twitter]: https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-plugin-twitter
616+
[giphy]: https://giphy.com
590617
[instagram]: https://instagram.com
591618
[kentcdodds.com-repo]: https://github.com/kentcdodds/kentcdodds.com
592619
[lichess]: https://lichess.org

src/__tests__/__fixtures__/kitchensink.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ https://codepen.io/team/codepen/pen/PNaGbb
1919

2020
https://codesandbox.io/s/ynn88nx9x?view=split
2121

22+
https://giphy.com/gifs/howtogiphygifs-how-to-XatG8bioEwwVO
23+
2224
https://instagram.com/p/B60jPE6J8U-
2325

2426
https://lichess.org/MPJcy1JW

src/__tests__/plugin.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ describe('gatsby-remark-embedder', () => {
1414

1515
test('can transform all supported links (kitchensink)', async () => {
1616
mockCache({
17+
'https://giphy.com/gifs/howtogiphygifs-how-to-XatG8bioEwwVO': `<div style="width:100%;height:0;padding-bottom:63%;position:relative;"><iframe src="https://giphy.com/embed/XatG8bioEwwVO" width="100%" height="100%" style="position:absolute" frameborder="0" class="giphy-embed-from-cache" allowfullscreen></iframe></div>`,
1718
'https://instagram.com/p/B60jPE6J8U-': `<blockquote class="instagram-media-from-cache"><div><a href="https://instagram.com/p/B60jPE6J8U-"><p>example</p></a><p>A post shared by <a href="https://instagram.com/michaeldeboey">Michaël De Boey</a> (@michaeldeboey) on<timedatetime="2020-01-02T14:45:30+00:00">Jan 2, 2020 at 6:45am PST</time></p></div></blockquote>`,
1819
'https://streamable.com/moo': `<iframe class="streamable-embed-from-cache" src="https://streamable.com/o/moo" frameborder="0" scrolling="no" width="1920" height="1080" allowfullscreen></iframe>`,
1920
'https://twitter.com/kentcdodds/status/1078755736455278592': `<blockquote class="twitter-tweet-from-cache"><p lang="en" dir="ltr">example</p>&mdash; Kent C. Dodds (@kentcdodds) <a href="https://twitter.com/kentcdodds/status/1078755736455278592">December 28, 2018</a></blockquote>`,
@@ -44,6 +45,8 @@ describe('gatsby-remark-embedder', () => {
4445
4546
<iframe src=\\"https://codesandbox.io/embed/ynn88nx9x?view=split\\" style=\\"width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;\\" allow=\\"geolocation; microphone; camera; midi; vr; accelerometer; gyroscope; payment; ambient-light-sensor; encrypted-media; usb\\" sandbox=\\"allow-modals allow-forms allow-popups allow-scripts allow-same-origin\\"></iframe>
4647
48+
<div style=\\"width:100%;height:0;padding-bottom:63%;position:relative;\\"><iframe src=\\"https://giphy.com/embed/XatG8bioEwwVO\\" width=\\"100%\\" height=\\"100%\\" style=\\"position:absolute\\" frameborder=\\"0\\" class=\\"giphy-embed-from-cache\\" allowfullscreen></iframe></div>
49+
4750
<blockquote class=\\"instagram-media-from-cache\\"><div><a href=\\"https://instagram.com/p/B60jPE6J8U-\\"><p>example</p></a><p>A post shared by <a href=\\"https://instagram.com/michaeldeboey\\">Michaël De Boey</a> (@michaeldeboey) on<timedatetime=\\"2020-01-02T14:45:30+00:00\\">Jan 2, 2020 at 6:45am PST</time></p></div></blockquote>
4851
4952
<iframe src=\\"https://lichess.org/embed/MPJcy1JW\\" width=\\"600\\" height=\\"397\\" frameborder=\\"0\\"></iframe>
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import cases from 'jest-in-case';
2+
import fetchMock from 'node-fetch';
3+
4+
import plugin from '../../';
5+
import {
6+
getGIPHYId,
7+
getGIPHYResponsivePadding,
8+
getHTML,
9+
shouldTransform,
10+
} from '../../transformers/GIPHY';
11+
12+
import { cache, getMarkdownASTForFile, parseASTToMarkdown } from '../helpers';
13+
14+
jest.mock('node-fetch', () => jest.fn());
15+
16+
const mockFetch = ({ height, width }) =>
17+
fetchMock.mockResolvedValue({
18+
json: () => Promise.resolve({ height, width }),
19+
});
20+
21+
beforeEach(() => {
22+
fetchMock.mockClear();
23+
});
24+
25+
cases(
26+
'url validation',
27+
({ url, valid }) => {
28+
expect(shouldTransform(url)).toBe(valid);
29+
},
30+
{
31+
'non-GIPHY url': {
32+
url: 'https://not-a-giphy-url.com',
33+
valid: false,
34+
},
35+
"non-GIPHY url ending with 'giphy.com'": {
36+
url: 'https://this-is-not-giphy.com',
37+
valid: false,
38+
},
39+
"non-GIPHY url ending with 'giphy.com' and having '/gifs/' in the url": {
40+
url:
41+
'https://this-is-not-giphy.com/gifs/howtogiphygifs-how-to-XatG8bioEwwVO',
42+
valid: false,
43+
},
44+
homepage: {
45+
url: 'https://giphy.com',
46+
valid: false,
47+
},
48+
'video url': {
49+
url:
50+
'https://giphy.com/videos/blesstheharts-wayne-bless-the-harts-ciwJyqlgAYkvguS2Nw',
51+
valid: false,
52+
},
53+
'gif url': {
54+
url: 'https://giphy.com/gifs/howtogiphygifs-how-to-XatG8bioEwwVO',
55+
valid: true,
56+
},
57+
"gif url having 'media' subdomain": {
58+
url: 'https://media.giphy.com/media/XatG8bioEwwVO/giphy.gif',
59+
valid: true,
60+
},
61+
"gif url having 'media' subdomain and not ending on 'giphy.gif'": {
62+
url: 'https://media.giphy.com/media/XatG8bioEwwVO',
63+
valid: true,
64+
},
65+
"gif url having 'media0' subdomain": {
66+
url: 'https://media0.giphy.com/media/XatG8bioEwwVO/giphy.gif',
67+
valid: true,
68+
},
69+
"gif url having 'media0' subdomain and not ending on 'giphy.gif'": {
70+
url: 'https://media0.giphy.com/media/XatG8bioEwwVO',
71+
valid: true,
72+
},
73+
}
74+
);
75+
76+
cases(
77+
'getGIPHYId',
78+
({ id, url }) => {
79+
expect(getGIPHYId(url)).toBe(id);
80+
},
81+
{
82+
'media subdomain': {
83+
url: 'https://media.giphy.com/media/UUi1SJNYpMguAcnKsh/giphy.gif',
84+
id: 'UUi1SJNYpMguAcnKsh',
85+
},
86+
'media numbered subdomain': {
87+
url: 'https://media0.giphy.com/media/8P7qnlQ6o0NF5R8IEB/giphy.gif',
88+
id: '8P7qnlQ6o0NF5R8IEB',
89+
},
90+
'gif url': {
91+
url: 'https://giphy.com/gifs/howtogiphygifs-how-to-XatG8bioEwwVO',
92+
id: 'XatG8bioEwwVO',
93+
},
94+
}
95+
);
96+
97+
cases(
98+
'getGIPHYResponsivePadding',
99+
({ height, width, padding }) => {
100+
expect(getGIPHYResponsivePadding({ height, width })).toBe(padding);
101+
},
102+
[
103+
{
104+
height: 314,
105+
width: 500,
106+
padding: 63,
107+
},
108+
{
109+
height: 270,
110+
width: 480,
111+
padding: 56,
112+
},
113+
{
114+
height: 375,
115+
width: 500,
116+
padding: 75,
117+
},
118+
{
119+
height: 151,
120+
width: 300,
121+
padding: 50,
122+
},
123+
]
124+
);
125+
126+
test('Gets the correct GIPHY iframe', async () => {
127+
mockFetch({ height: 314, width: 500 });
128+
129+
const html = await getHTML(
130+
'https://giphy.com/gifs/howtogiphygifs-how-to-XatG8bioEwwVO'
131+
);
132+
133+
expect(html).toMatchInlineSnapshot(
134+
`"<div style=\\"width:100%;height:0;padding-bottom:63%;position:relative;\\"><iframe src=\\"https://giphy.com/embed/XatG8bioEwwVO\\" width=\\"100%\\" height=\\"100%\\" style=\\"position:absolute\\" frameborder=\\"0\\" class=\\"giphy-embed\\" allowfullscreen></iframe></div>"`
135+
);
136+
});
137+
138+
test('Plugin can transform GIPHY links', async () => {
139+
mockFetch({ height: 314, width: 500 });
140+
141+
const markdownAST = getMarkdownASTForFile('GIPHY');
142+
143+
const processedAST = await plugin({ cache, markdownAST });
144+
145+
expect(parseASTToMarkdown(processedAST)).toMatchInlineSnapshot(`
146+
"<https://not-a-giphy-url.com>
147+
148+
<https://this-is-not-giphy.com>
149+
150+
<https://this-is-not-giphy.com/gifs/howtogiphygifs-how-to-XatG8bioEwwVO>
151+
152+
<https://giphy.com>
153+
154+
<https://giphy.com/videos/blesstheharts-wayne-bless-the-harts-ciwJyqlgAYkvguS2Nw>
155+
156+
<div style=\\"width:100%;height:0;padding-bottom:63%;position:relative;\\"><iframe src=\\"https://giphy.com/embed/XatG8bioEwwVO\\" width=\\"100%\\" height=\\"100%\\" style=\\"position:absolute\\" frameborder=\\"0\\" class=\\"giphy-embed\\" allowfullscreen></iframe></div>
157+
158+
<div style=\\"width:100%;height:0;padding-bottom:63%;position:relative;\\"><iframe src=\\"https://giphy.com/embed/XatG8bioEwwVO\\" width=\\"100%\\" height=\\"100%\\" style=\\"position:absolute\\" frameborder=\\"0\\" class=\\"giphy-embed\\" allowfullscreen></iframe></div>
159+
160+
<div style=\\"width:100%;height:0;padding-bottom:63%;position:relative;\\"><iframe src=\\"https://giphy.com/embed/XatG8bioEwwVO\\" width=\\"100%\\" height=\\"100%\\" style=\\"position:absolute\\" frameborder=\\"0\\" class=\\"giphy-embed\\" allowfullscreen></iframe></div>
161+
162+
<div style=\\"width:100%;height:0;padding-bottom:63%;position:relative;\\"><iframe src=\\"https://giphy.com/embed/XatG8bioEwwVO\\" width=\\"100%\\" height=\\"100%\\" style=\\"position:absolute\\" frameborder=\\"0\\" class=\\"giphy-embed\\" allowfullscreen></iframe></div>
163+
164+
<div style=\\"width:100%;height:0;padding-bottom:63%;position:relative;\\"><iframe src=\\"https://giphy.com/embed/XatG8bioEwwVO\\" width=\\"100%\\" height=\\"100%\\" style=\\"position:absolute\\" frameborder=\\"0\\" class=\\"giphy-embed\\" allowfullscreen></iframe></div>
165+
"
166+
`);
167+
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
https://not-a-giphy-url.com
2+
3+
https://this-is-not-giphy.com
4+
5+
https://this-is-not-giphy.com/gifs/howtogiphygifs-how-to-XatG8bioEwwVO
6+
7+
https://giphy.com
8+
9+
https://giphy.com/videos/blesstheharts-wayne-bless-the-harts-ciwJyqlgAYkvguS2Nw
10+
11+
https://giphy.com/gifs/howtogiphygifs-how-to-XatG8bioEwwVO
12+
13+
https://media.giphy.com/media/XatG8bioEwwVO/giphy.gif
14+
15+
https://media.giphy.com/media/XatG8bioEwwVO
16+
17+
https://media0.giphy.com/media/XatG8bioEwwVO/giphy.gif
18+
19+
https://media0.giphy.com/media/XatG8bioEwwVO

src/transformers/GIPHY.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { URL } from 'url';
2+
3+
import { fetchOEmbedData } from './utils';
4+
5+
const isMediaSubDomain = host => /^(media([0-9]+)?\.)giphy\.com$/.test(host);
6+
7+
export const shouldTransform = url => {
8+
const { host, pathname } = new URL(url);
9+
10+
return (
11+
(['giphy.com', 'www.giphy.com'].includes(host) &&
12+
pathname.includes('/gifs/')) ||
13+
(isMediaSubDomain(host) && pathname.includes('/media/'))
14+
);
15+
};
16+
17+
export const getGIPHYId = url => {
18+
const { host, pathname } = new URL(url);
19+
20+
if (isMediaSubDomain(host)) {
21+
return pathname.split('/')[2];
22+
}
23+
24+
return pathname.split('-').pop();
25+
};
26+
27+
export const getGIPHYResponsivePadding = ({ height, width }) =>
28+
Math.round((height / width) * 100);
29+
30+
export const getHTML = url =>
31+
fetchOEmbedData(`https://giphy.com/services/oembed?url=${url}`).then(
32+
({ height, width }) => {
33+
const GIPHYId = getGIPHYId(url);
34+
const padding = getGIPHYResponsivePadding({ height, width });
35+
36+
return `<div style="width:100%;height:0;padding-bottom:${padding}%;position:relative;"><iframe src="https://giphy.com/embed/${GIPHYId}" width="100%" height="100%" style="position:absolute" frameborder="0" class="giphy-embed" allowfullscreen></iframe></div>`;
37+
}
38+
);

src/transformers/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as CodePenTransformer from './CodePen';
22
import * as CodeSandboxTransformer from './CodeSandbox';
3+
import * as GIPHYTransformer from './GIPHY';
34
import * as InstagramTransformer from './Instagram';
45
import * as LichessTransformer from './Lichess';
56
import * as SlidesTransformer from './Slides';
@@ -12,6 +13,7 @@ import * as YouTubeTransformer from './YouTube';
1213
export const defaultTransformers = [
1314
CodePenTransformer,
1415
CodeSandboxTransformer,
16+
GIPHYTransformer,
1517
InstagramTransformer,
1618
LichessTransformer,
1719
SlidesTransformer,

0 commit comments

Comments
 (0)