mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-29 13:52:10 +03:00
Added POC for resizing iframe to fit outer container
refs https://github.com/TryGhost/Team/issues/3295
This commit is contained in:
parent
990395594b
commit
17e9b803e4
@ -42,17 +42,21 @@ export default class SignupFormEmbed extends Component {
|
||||
|
||||
const options = {
|
||||
site: siteUrl,
|
||||
color: this.settings.accentColor
|
||||
'button-color': this.settings.accentColor
|
||||
};
|
||||
|
||||
for (const [i, label] of this.labels.entries()) {
|
||||
options[`label-${i + 1}`] = label.name;
|
||||
}
|
||||
|
||||
let style = 'height: 58px';
|
||||
|
||||
if (this.style === 'all-in-one') {
|
||||
options.logo = this.settings.icon;
|
||||
options.title = this.settings.title;
|
||||
options.description = this.settings.description;
|
||||
options['background-color'] = '#f9f9f9';
|
||||
style = 'height: 60vh; min-height: 400px;';
|
||||
}
|
||||
|
||||
let dataOptionsString = '';
|
||||
@ -60,7 +64,7 @@ export default class SignupFormEmbed extends Component {
|
||||
dataOptionsString += ` data-${key}="${escapeHtml(value)}"`;
|
||||
}
|
||||
|
||||
return `<script src="${encodeURI(scriptUrl)}"${dataOptionsString}></script>`;
|
||||
return `<div style="${escapeHtml(style)}"><script src="${encodeURI(scriptUrl)}"${dataOptionsString}></script></div>`;
|
||||
}
|
||||
|
||||
@task
|
||||
|
@ -18,55 +18,64 @@
|
||||
</p>
|
||||
|
||||
<!-- Because we need to use ESM modules during develoment, the src should be different to force reexecution of each script -->
|
||||
<script
|
||||
type="module"
|
||||
src="/src/index.tsx"
|
||||
data-title="My site name"
|
||||
data-description="An independent publication about embeddable signup forms."
|
||||
data-logo="https://user-images.githubusercontent.com/65487235/157884383-1b75feb1-45d8-4430-b636-3f7e06577347.png"
|
||||
data-background-color="#eeeeee"
|
||||
data-button-color="#4664dd"
|
||||
data-site="%VITE_SITE_URL%"
|
||||
data-label-1="Signup form"
|
||||
data-label-2="With logo"
|
||||
></script>
|
||||
<div style="height: 60vh; min-height: 400px;">
|
||||
<script
|
||||
type="module"
|
||||
src="/src/index.tsx"
|
||||
data-title="My site name"
|
||||
data-description="An independent publication about embeddable signup forms."
|
||||
data-logo="https://user-images.githubusercontent.com/65487235/157884383-1b75feb1-45d8-4430-b636-3f7e06577347.png"
|
||||
data-background-color="#eeeeee"
|
||||
data-button-color="#4664dd"
|
||||
data-site="%VITE_SITE_URL%"
|
||||
data-label-1="Signup form"
|
||||
data-label-2="With logo"
|
||||
></script>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<h1>Without logo</h1>
|
||||
<script
|
||||
type="module"
|
||||
src="/src/index.tsx?withoutlogo"
|
||||
data-title="Site without logo"
|
||||
data-description="An independent publication about embeddable signup forms."
|
||||
data-background-color="#eeeeee"
|
||||
data-button-color="#4664dd"
|
||||
data-site="%VITE_SITE_URL%"
|
||||
data-label-1="Signup form"
|
||||
data-label-2="Without logo"
|
||||
></script>
|
||||
|
||||
<div style="height: 60vh; min-height: 400px;">
|
||||
<script
|
||||
type="module"
|
||||
src="/src/index.tsx?withoutlogo"
|
||||
data-title="Site without logo"
|
||||
data-description="An independent publication about embeddable signup forms."
|
||||
data-background-color="#eeeeee"
|
||||
data-button-color="#4664dd"
|
||||
data-site="%VITE_SITE_URL%"
|
||||
data-label-1="Signup form"
|
||||
data-label-2="Without logo"
|
||||
></script>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<h1>Minimal</h1>
|
||||
|
||||
<script
|
||||
type="module"
|
||||
src="/src/index.tsx?other"
|
||||
data-button-color="#ff0095"
|
||||
data-site="%VITE_SITE_URL%"
|
||||
data-label-1="Signup form"
|
||||
data-label-2="Minimal"
|
||||
></script>
|
||||
<div style="height: 58px">
|
||||
<script
|
||||
type="module"
|
||||
src="/src/index.tsx?other"
|
||||
data-button-color="#ff0095"
|
||||
data-site="%VITE_SITE_URL%"
|
||||
data-label-1="Signup form"
|
||||
data-label-2="Minimal"
|
||||
></script>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<h1>With invalid configuration</h1>
|
||||
<p>When you submit this one, it will throw an error.</p>
|
||||
|
||||
<script
|
||||
type="module"
|
||||
src="/src/index.tsx?other2"
|
||||
data-button-color="#ff0095"
|
||||
data-site="https://invalid/"
|
||||
></script>
|
||||
<div style="height: 58px">
|
||||
<script
|
||||
type="module"
|
||||
src="/src/index.tsx?other2"
|
||||
data-button-color="#ff0095"
|
||||
data-site="https://invalid/"
|
||||
></script>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, {ComponentProps} from 'react';
|
||||
import pages, {Page, PageName} from './pages';
|
||||
import {AppContextProvider} from './AppContext';
|
||||
import {AppContextProvider, AppContextType} from './AppContext';
|
||||
import {ContentBox} from './components/ContentBox';
|
||||
import {Frame} from './components/Frame';
|
||||
import {setupGhostApi} from './utils/api';
|
||||
@ -29,11 +29,12 @@ const App: React.FC<AppProps> = ({scriptTag}) => {
|
||||
} as Page);
|
||||
};
|
||||
|
||||
const context = {
|
||||
const context: AppContextType = {
|
||||
page,
|
||||
api,
|
||||
options,
|
||||
setPage: _setPage
|
||||
setPage: _setPage,
|
||||
scriptTag
|
||||
};
|
||||
|
||||
const PageComponent = pages[page.name];
|
||||
|
@ -18,6 +18,7 @@ export type AppContextType = {
|
||||
setPage: <T extends PageName>(name: T, data: ComponentProps<typeof pages[T]>) => void,
|
||||
options: SignupFormOptions,
|
||||
api: GhostApi,
|
||||
scriptTag: HTMLElement
|
||||
}
|
||||
|
||||
const AppContext = React.createContext<AppContextType>({} as any);
|
||||
|
@ -39,9 +39,10 @@ const Preview: React.FC<SignupFormOptions & {
|
||||
return simulateApiError ? false : true;
|
||||
}
|
||||
},
|
||||
options
|
||||
options,
|
||||
scriptTag: document.createElement('div')
|
||||
}}>
|
||||
<div style={{width: '100%', height: '100%', padding: '24px', backgroundColor: pageBackgroundColor, color: pageTextColor}}>
|
||||
<div style={{width: '100%', height: '100%', backgroundColor: pageBackgroundColor, color: pageTextColor}}>
|
||||
<ContentBox>
|
||||
<PageComponent {...data} />
|
||||
</ContentBox>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import IFrame from './IFrame';
|
||||
import React, {useCallback, useState} from 'react';
|
||||
import styles from '../styles/iframe.css?inline';
|
||||
import {useAppContext} from '../AppContext';
|
||||
|
||||
type FrameProps = {
|
||||
children: React.ReactNode
|
||||
@ -15,9 +16,9 @@ export const Frame: React.FC<FrameProps> = ({children}) => {
|
||||
height: '0px' // = default height
|
||||
};
|
||||
return (
|
||||
<ResizableFrame style={style} title="signup frame">
|
||||
<FullHeightFrame style={style} title="signup frame">
|
||||
{children}
|
||||
</ResizableFrame>
|
||||
</FullHeightFrame>
|
||||
);
|
||||
};
|
||||
|
||||
@ -27,28 +28,46 @@ type ResizableFrameProps = FrameProps & {
|
||||
};
|
||||
|
||||
/**
|
||||
* This TailwindFrame has the same height as it contents and mimics a shadow DOM component
|
||||
* This TailwindFrame has the same height as its container
|
||||
*/
|
||||
const ResizableFrame: React.FC<ResizableFrameProps> = ({children, style, title}) => {
|
||||
const FullHeightFrame: React.FC<ResizableFrameProps> = ({children, style, title}) => {
|
||||
const {scriptTag} = useAppContext();
|
||||
const [iframeStyle, setIframeStyle] = useState(style);
|
||||
const onResize = useCallback((iframeRoot: HTMLElement) => {
|
||||
|
||||
const onResize = useCallback((element: HTMLElement) => {
|
||||
setIframeStyle((current) => {
|
||||
return {
|
||||
...current,
|
||||
height: `${iframeRoot.scrollHeight}px`
|
||||
height: `${element.scrollHeight}px`,
|
||||
width: `${element.scrollWidth}px`
|
||||
};
|
||||
});
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
const element = scriptTag.parentElement;
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
const observer = new ResizeObserver(_ => onResize(element));
|
||||
observer.observe(element);
|
||||
|
||||
return () => {
|
||||
observer.unobserve(element);
|
||||
};
|
||||
}, [scriptTag, onResize]);
|
||||
|
||||
return (
|
||||
<TailwindFrame style={iframeStyle} title={title} onResize={onResize}>
|
||||
{children}
|
||||
</TailwindFrame>
|
||||
<div style={{position: 'absolute'}}>
|
||||
<TailwindFrame style={iframeStyle} title={title}>
|
||||
{children}
|
||||
</TailwindFrame>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type TailwindFrameProps = ResizableFrameProps & {
|
||||
onResize: (el: HTMLElement) => void
|
||||
onResize?: (el: HTMLElement) => void
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -10,7 +10,7 @@ export default class IFrame extends Component<any> {
|
||||
iframeHead: any;
|
||||
iframeRoot: any;
|
||||
|
||||
constructor(props: {onResize: (el: HTMLElement) => void, children: any}) {
|
||||
constructor(props: {onResize?: (el: HTMLElement) => void, children: any}) {
|
||||
super(props);
|
||||
this.setNode = this.setNode.bind(this);
|
||||
this.node = null;
|
||||
|
@ -16,7 +16,7 @@ export const FormView: React.FC<FormProps & {
|
||||
|
||||
return (
|
||||
<div
|
||||
className='flex h-[52vmax] min-h-[320px] flex-col items-center justify-center p-6 md:p-8'
|
||||
className='flex h-[100vh] flex-col items-center justify-center p-6 md:p-8'
|
||||
data-testid="wrapper"
|
||||
style={{backgroundColor, color: backgroundColor && textColorForBackgroundColor(backgroundColor)}}
|
||||
>
|
||||
|
@ -15,7 +15,7 @@ export const SuccessView: React.FC<{
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className='flex h-[52vmax] min-h-[320px] flex-col items-center justify-center bg-grey-200 p-6 md:p-8'
|
||||
className='flex h-[100vh] flex-col items-center justify-center bg-grey-200 p-6 md:p-8'
|
||||
data-testid="success-page"
|
||||
style={{backgroundColor, color: backgroundColor && textColorForBackgroundColor(backgroundColor)}}
|
||||
>
|
||||
|
Loading…
Reference in New Issue
Block a user