feat: Allow Google Embeds from regular (non publish-to-web) links (#1533)
Improve styling to allow getting back to source document
This commit is contained in:
parent
b3b71d2dc7
commit
ac8f0ebaac
|
@ -2,9 +2,7 @@
|
|||
import * as React from "react";
|
||||
import Frame from "./components/Frame";
|
||||
|
||||
const URL_REGEX = new RegExp(
|
||||
"^https?://docs.google.com/document/d/(.*)/pub(.*)$"
|
||||
);
|
||||
const URL_REGEX = new RegExp("^https?://docs.google.com/document/(.*)$");
|
||||
|
||||
type Props = {|
|
||||
attrs: {|
|
||||
|
@ -18,7 +16,20 @@ export default class GoogleDocs extends React.Component<Props> {
|
|||
|
||||
render() {
|
||||
return (
|
||||
<Frame src={this.props.attrs.href} title="Google Docs Embed" border />
|
||||
<Frame
|
||||
src={this.props.attrs.href.replace("/edit", "/preview")}
|
||||
icon={
|
||||
<img
|
||||
src="/images/google-docs.png"
|
||||
alt="Google Docs Icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
}
|
||||
canonicalUrl={this.props.attrs.href}
|
||||
title="Google Docs"
|
||||
border
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,14 +14,19 @@ describe("GoogleDocs", () => {
|
|||
match
|
||||
)
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
"https://docs.google.com/document/d/1SsDfWzFFTjZM2LanvpyUzjKhqVQpwpTMeiPeYxhVqOg/edit".match(
|
||||
match
|
||||
)
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
"https://docs.google.com/document/d/1SsDfWzFFTjZM2LanvpyUzjKhqVQpwpTMeiPeYxhVqOg/preview".match(
|
||||
match
|
||||
)
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
test("to not be enabled elsewhere", () => {
|
||||
expect(
|
||||
"https://docs.google.com/document/d/e/2PACX-1vTdddHPoZ5M_47wmSHCoigR/edit".match(
|
||||
match
|
||||
)
|
||||
).toBe(null);
|
||||
expect("https://docs.google.com/document".match(match)).toBe(null);
|
||||
expect("https://docs.google.com".match(match)).toBe(null);
|
||||
expect("https://www.google.com".match(match)).toBe(null);
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
import * as React from "react";
|
||||
import Frame from "./components/Frame";
|
||||
|
||||
const URL_REGEX = new RegExp(
|
||||
"^https?://docs.google.com/spreadsheets/d/(.*)/pub(.*)$"
|
||||
);
|
||||
const URL_REGEX = new RegExp("^https?://docs.google.com/spreadsheets/d/(.*)$");
|
||||
|
||||
type Props = {|
|
||||
attrs: {|
|
||||
|
@ -18,7 +16,20 @@ export default class GoogleSlides extends React.Component<Props> {
|
|||
|
||||
render() {
|
||||
return (
|
||||
<Frame src={this.props.attrs.href} title="Google Sheets Embed" border />
|
||||
<Frame
|
||||
src={this.props.attrs.href.replace("/edit", "/preview")}
|
||||
icon={
|
||||
<img
|
||||
src="/images/google-sheets.png"
|
||||
alt="Google Sheets Icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
}
|
||||
canonicalUrl={this.props.attrs.href}
|
||||
title="Google Sheets"
|
||||
border
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,14 +9,14 @@ describe("GoogleSheets", () => {
|
|||
match
|
||||
)
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
test("to not be enabled elsewhere", () => {
|
||||
expect(
|
||||
"https://docs.google.com/spreadsheets/d/e/2PACX-1vTdddHPoZ5M_47wmSHCoigR/edit".match(
|
||||
match
|
||||
)
|
||||
).toBe(null);
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
test("to not be enabled elsewhere", () => {
|
||||
expect("https://docs.google.com/spreadsheets".match(match)).toBe(null);
|
||||
expect("https://docs.google.com".match(match)).toBe(null);
|
||||
expect("https://www.google.com".match(match)).toBe(null);
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
import * as React from "react";
|
||||
import Frame from "./components/Frame";
|
||||
|
||||
const URL_REGEX = new RegExp(
|
||||
"^https?://docs.google.com/presentation/d/(.*)/pub(.*)$"
|
||||
);
|
||||
const URL_REGEX = new RegExp("^https?://docs.google.com/presentation/d/(.*)$");
|
||||
|
||||
type Props = {|
|
||||
attrs: {|
|
||||
|
@ -19,8 +17,19 @@ export default class GoogleSlides extends React.Component<Props> {
|
|||
render() {
|
||||
return (
|
||||
<Frame
|
||||
src={this.props.attrs.href.replace("/pub", "/embed")}
|
||||
title="Google Slides Embed"
|
||||
src={this.props.attrs.href
|
||||
.replace("/edit", "/preview")
|
||||
.replace("/pub", "/embed")}
|
||||
icon={
|
||||
<img
|
||||
src="/images/google-slides.png"
|
||||
alt="Google Slides Icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
}
|
||||
canonicalUrl={this.props.attrs.href}
|
||||
title="Google Slides"
|
||||
border
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -14,14 +14,14 @@ describe("GoogleSlides", () => {
|
|||
match
|
||||
)
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
test("to not be enabled elsewhere", () => {
|
||||
expect(
|
||||
"https://docs.google.com/presentation/d/e/2PACX-1vTdddHPoZ5M_47wmSHCoigR/edit".match(
|
||||
match
|
||||
)
|
||||
).toBe(null);
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
test("to not be enabled elsewhere", () => {
|
||||
expect("https://docs.google.com/presentation".match(match)).toBe(null);
|
||||
expect("https://docs.google.com".match(match)).toBe(null);
|
||||
expect("https://www.google.com".match(match)).toBe(null);
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
// @flow
|
||||
import { observable } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { OpenIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import styled from "styled-components";
|
||||
import Flex from "components/Flex";
|
||||
|
||||
type Props = {
|
||||
src?: string,
|
||||
border?: boolean,
|
||||
title?: string,
|
||||
icon?: React.Node,
|
||||
canonicalUrl?: string,
|
||||
width?: string,
|
||||
height?: string,
|
||||
};
|
||||
|
@ -40,15 +45,20 @@ class Frame extends React.Component<PropsWithRef> {
|
|||
width = "100%",
|
||||
height = "400px",
|
||||
forwardedRef,
|
||||
icon,
|
||||
title,
|
||||
canonicalUrl,
|
||||
...rest
|
||||
} = this.props;
|
||||
const Component = border ? StyledIframe : "iframe";
|
||||
const withBar = !!(icon || canonicalUrl);
|
||||
|
||||
return (
|
||||
<Rounded width={width} height={height}>
|
||||
<Rounded width={width} height={height} withBar={withBar}>
|
||||
{this.isLoaded && (
|
||||
<Component
|
||||
ref={forwardedRef}
|
||||
withBar={withBar}
|
||||
sandbox="allow-same-origin allow-scripts allow-popups allow-forms"
|
||||
width={width}
|
||||
height={height}
|
||||
|
@ -60,16 +70,56 @@ class Frame extends React.Component<PropsWithRef> {
|
|||
{...rest}
|
||||
/>
|
||||
)}
|
||||
{withBar && (
|
||||
<Bar align="center">
|
||||
{icon} <Title>{title}</Title>
|
||||
{canonicalUrl && (
|
||||
<Open
|
||||
href={canonicalUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<OpenIcon color="currentColor" size={18} /> Open
|
||||
</Open>
|
||||
)}
|
||||
</Bar>
|
||||
)}
|
||||
</Rounded>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const Rounded = styled.div`
|
||||
border-radius: 3px;
|
||||
border-radius: ${(props) => (props.withBar ? "3px 3px 0 0" : "3px")};
|
||||
overflow: hidden;
|
||||
width: ${(props) => props.width};
|
||||
height: ${(props) => props.height};
|
||||
height: ${(props) => (props.withBar ? props.height + 28 : props.height)};
|
||||
`;
|
||||
|
||||
const Open = styled.a`
|
||||
color: ${(props) => props.theme.textSecondary} !important;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
padding: 0 8px;
|
||||
`;
|
||||
|
||||
const Title = styled.span`
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
padding-left: 4px;
|
||||
`;
|
||||
|
||||
const Bar = styled(Flex)`
|
||||
background: ${(props) => props.theme.secondaryBackground};
|
||||
color: ${(props) => props.theme.textSecondary};
|
||||
padding: 0 8px;
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
// This wrapper allows us to pass non-standard HTML attributes through to the DOM element
|
||||
|
@ -79,7 +129,8 @@ const Iframe = (props) => <iframe {...props} />;
|
|||
const StyledIframe = styled(Iframe)`
|
||||
border: 1px solid;
|
||||
border-color: ${(props) => props.theme.embedBorder};
|
||||
border-radius: 3px;
|
||||
border-radius: ${(props) => (props.withBar ? "3px 3px 0 0" : "3px")};
|
||||
display: block;
|
||||
`;
|
||||
|
||||
export default React.forwardRef<Props, typeof Frame>((props, ref) => (
|
||||
|
|
|
@ -159,7 +159,7 @@ export const light = {
|
|||
quote: colors.slateLight,
|
||||
codeBackground: colors.smoke,
|
||||
codeBorder: colors.smokeDark,
|
||||
embedBorder: "#DDD #DDD #CCC",
|
||||
embedBorder: colors.slateLight,
|
||||
horizontalRule: colors.smokeDark,
|
||||
|
||||
noticeInfoBackground: colors.warmGrey,
|
||||
|
|
Reference in New Issue