From ac8f0ebaac4bef11e94cdf23e7717d5d0664bbe7 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Tue, 15 Sep 2020 18:38:42 -0700 Subject: [PATCH] feat: Allow Google Embeds from regular (non publish-to-web) links (#1533) Improve styling to allow getting back to source document --- app/embeds/GoogleDocs.js | 19 ++++++++--- app/embeds/GoogleDocs.test.js | 15 ++++++--- app/embeds/GoogleSheets.js | 19 ++++++++--- app/embeds/GoogleSheets.test.js | 8 ++--- app/embeds/GoogleSlides.js | 19 ++++++++--- app/embeds/GoogleSlides.test.js | 8 ++--- app/embeds/components/Frame.js | 59 ++++++++++++++++++++++++++++++--- shared/styles/theme.js | 2 +- 8 files changed, 118 insertions(+), 31 deletions(-) diff --git a/app/embeds/GoogleDocs.js b/app/embeds/GoogleDocs.js index 0cbfaa1e..0402f020 100644 --- a/app/embeds/GoogleDocs.js +++ b/app/embeds/GoogleDocs.js @@ -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 { render() { return ( - + + } + canonicalUrl={this.props.attrs.href} + title="Google Docs" + border + /> ); } } diff --git a/app/embeds/GoogleDocs.test.js b/app/embeds/GoogleDocs.test.js index 426407ee..34f4b48a 100644 --- a/app/embeds/GoogleDocs.test.js +++ b/app/embeds/GoogleDocs.test.js @@ -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); diff --git a/app/embeds/GoogleSheets.js b/app/embeds/GoogleSheets.js index 78cb749c..8e38b4fb 100644 --- a/app/embeds/GoogleSheets.js +++ b/app/embeds/GoogleSheets.js @@ -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 { render() { return ( - + + } + canonicalUrl={this.props.attrs.href} + title="Google Sheets" + border + /> ); } } diff --git a/app/embeds/GoogleSheets.test.js b/app/embeds/GoogleSheets.test.js index 81a45255..e028ea13 100644 --- a/app/embeds/GoogleSheets.test.js +++ b/app/embeds/GoogleSheets.test.js @@ -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); diff --git a/app/embeds/GoogleSlides.js b/app/embeds/GoogleSlides.js index f735d049..c1af0a20 100644 --- a/app/embeds/GoogleSlides.js +++ b/app/embeds/GoogleSlides.js @@ -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 { render() { return ( + } + canonicalUrl={this.props.attrs.href} + title="Google Slides" border /> ); diff --git a/app/embeds/GoogleSlides.test.js b/app/embeds/GoogleSlides.test.js index fb0daed4..d532e9ed 100644 --- a/app/embeds/GoogleSlides.test.js +++ b/app/embeds/GoogleSlides.test.js @@ -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); diff --git a/app/embeds/components/Frame.js b/app/embeds/components/Frame.js index e80ccb52..0988e35f 100644 --- a/app/embeds/components/Frame.js +++ b/app/embeds/components/Frame.js @@ -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 { width = "100%", height = "400px", forwardedRef, + icon, + title, + canonicalUrl, ...rest } = this.props; const Component = border ? StyledIframe : "iframe"; + const withBar = !!(icon || canonicalUrl); return ( - + {this.isLoaded && ( { {...rest} /> )} + {withBar && ( + + {icon} {title} + {canonicalUrl && ( + + Open + + )} + + )} ); } } 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) =>