-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Hard to use with CSS-in-JS libs. #793
Comments
It's not always a given that a site's CSS will automatically apply to the preview pane - doing so often requires porting the styles in via |
Hmm looks like styled components adds a data attribute to its style elements - I'll bet the others do the same. The That said, we need to give some higher level consideration to the proper abstraction for these API changes. What do you think? |
I had the same problem with CSS-Modules on GatsbyJS, I hope this helps: according to the documentation style-loader is able to inject the inline-CSS into an iframe. But in the end I was unable to set up this functionality with netlify-cms and used the Extract-Text-Plugin The relevant parts from my
|
Nice trick @zionis137. I'm still hoping for some more official support, but this is a nice workaround! |
@erquhart Your proposal for finding CSS by selector sounds reasonable. Another possibility would be to allow loading the preview iframe via a URL rather than trying to inject a react tree inside. I think it might be a more surefire solution than trying to identify the misplaced CSS and teleport it somewhere else. |
@whmountains we need some pretty tight, realtime control over that preview pane, so far it seems injecting the React tree is a requirement - the preview isn't served separately. How would you propose doing this with a URL? |
Hey, i'm wondering if there is any news on this, as i ran into the exact same issue as @whmountains. In addition i feel that using Maybe rendering the preview into a React portal instead of an iFrame would solve the issue? |
That's an interesting idea. I haven't looked into portals at all, but feel free to check it out and see if it's possible. |
@erquhart To answer your question I would create a subscriber interface, which a HoC within the preview pane can access. Similar to how react-redux works with You could also have another HoC which would wrap the entire preview pane and implement scroll sync by listening to scroll events. |
AFAIK netlify-cms uses redux under the hood. Could you just expose the store inside the iframe? Everything from EditorPreviewPane on down would be incorporated into the custom build running inside the iframe. Just throwing out ideas. I'm not very familiar with the codebase or all the caveats a system like this would introduce. It just seems that netlify-cms's preview interface is breaking core assumptions about how web pages are rendered and it would be nice to fix that somehow so everything "just works". |
For anyone using emotion, I solved this issue by using SSR on the fly to extract the css and then inject it into the nearest document (iframe) head. Very hacky but it works. import { renderToString } from "react-dom/server"
import { renderStylesToString } from "emotion-server"
class CSSInjector extends React.Component {
render() {
return (
<div
ref={ref => {
if (ref && !this.css) {
this.css = renderStylesToString(renderToString(this.props.children))
ref.ownerDocument.head.innerHTML += this.css
}
}}>
{React.Children.only(this.props.children)}
</div>
)
}
} It works by wrapping your preview template: CMS.registerPreviewTemplate("blog", props => (
<CSSInjector>
<BlogPreviewTemplate {...props} />
</CSSInjector>
)) |
On second thought that PR won't help for the emotion case at all. I'm also thinking that what you've done really isn't hacky, especially considering how you moved it into a component. This might even find it's way into the docs! 😂 |
styled-components has a way to target an iframe as it's injection point: import { StyleSheetManager } from 'styled-components'
<StyleSheetManager target={iframeHeadElem}>
<App />
</StyleSheetManager> Any styled component within |
I was just looking at @whmountains care to give it a go and let us know? |
Note that the target feature was only introduced in v3.2.0 and isn't documented just yet: https://www.styled-components.com/releases#v3.2.0_stylesheetmanager-target-prop |
I just tried this out and it works great! Just simply: const iframe = document.querySelector(".nc-previewPane-frame")
const iframeHeadElem = iframe.contentDocument.head;
return (
<StyleSheetManager target={iframeHeadElem}>
{/* styled elements */}
</StyleSheetManager>
) |
Leaving this open as I'd still like to discuss how our API might improve so that this isn't so manual. Perhaps we need some kind of preview plugin API that would allow a styled-components plugin to handle this behind the scenes. |
This comment has been minimized.
This comment has been minimized.
@firthir Can you elaborate what is the idea behind the localStorage Item? |
This comment has been minimized.
This comment has been minimized.
@firthir this issue is about CSS in JS solutions like Emotion, Styled Components, etc. |
This may merit staying open. What we have so far are instructions for individual libraries, most of which are on the tedious side. At a minimum we should document this stuff, at a maximum we should have a simpler support model. Sent with GitHawk |
I re-opened an added a What do you think? |
Thanks for the previous comments, which inspired me a lot! Short Term SolutionIn my opinion, the most natural solution is to create a higher-order component that adds support to a specific CSS-IN-JS library. For example, I would add support for styled-components and emotion with the following snippet today:
Long Term SolutionBut for the long term, in order to achieve best possible user experience, it would be best to hide these dirty details and try to detect whether the project is using
|
Should this work for nextjs implementations as well? I'm using css modules, and have failed to get any of the examples to work. Nextjs is embeded the styles into the admin page as <style> blocks. I would expect to see the iframe head element to have shows the styles in a similar way, but i'm not. I imagine its because nextjs works in a different way, but i'm not entirely sure. /////////// |
Ok, So i think this is particular to my issue with NextJS, running in dev with local cms, and very likely i'm just not doing things the right way (i'm not that clued into css.modules and styled components). But i simply copied the styles in my header into the iframe head element. I imagine that i'm using withEmotion wrong in some way.
|
None of these solutions seem to work particularly well for me. As soon as i register emotion preview templates, the UI goes to shit. This is enough to break it:
using the code from #793 (comment) Further more, in editing mode, the cms.css gets inserted into the iframe, instead of the main document, so the widgets go to shit as well. If i move that back out, the widgets look fine. I can't seem to figure out what to do to make the main UI work. I'm not entirely sure what's happening. EDIT: So actually this is enough to break it:
So it probably has nothing to do with emotion. I just need to register an arbitrary preview template. If i remove the last line, CMS looks fine. |
I'm in the boat as @miklschmidt - none of the above solutions worked. The CMS's styles look fine until I add this line:
Then they have the janky formatting mirrored in his screenshot. gatsby: 2.32.2 |
For those who are experiencing the issue in #793 (comment), can you try downgrading to |
I have this working with Emotion 10 but if I try with Emotion 11 the entire preview panel breaks with a React error. Any plan in the near future to update to Emotion 11? Thanks. |
Hi @javialon26, yes there are plans to upgrade but no timeline yet. This will need to be behind a major version bump as it is a breaking change for emotion 10 users. I'm not sure if we should couple this to #5111 (also a breaking change). |
Hi, we're using the (still beta) variable type lists and yaml aliases to pull off a page-builder model for a project. But we're using twin.macro which extends emotion with tailwind functionality. Is there any normal way to get these styles working in preview templates yet? Thanks |
@erezrokah I can put something together if it helps but I'm seeing that Netlify CMS currently doesn't support version 11 of Emotion (the way we use Twin it's basically just emotion with some extra support for tailwind, but I'm not sure we can downgrade to Emotion 10). Do you know if there's any timeline for getting Netlify CMS updated so it can work with the latest version of Emotion? We are actively using the CMS with client projects now so not having preview is becoming a serious problem for us... thanks! |
Thanks @erezrokah -- I ended up taking a different approach since extracting styles from Emotion 11 wasn't working, and also because we are using Gatsby, so even with styling working our components that include special Gatsby functionality like I realized that since we're using a page-builder model where all components for a single page are listed in a variable-type list, that the ideal way to do this would be to show the actual rendered web page, and then overlay the new data changes onto it. I was able to do it that way! I realize that it doesn't cover all use cases of this CMS but will post the details here in case it's helpful for anyone else. "I heard you liked iframes, so I put an iframe in your iframe" 😏The page is loaded into a second iframe inside the preview, like so: At first you're seeing the original page with unaltered content, but as soon as you type into a CMS field you see the content change without any glitches. Since it's precompiled, all the jss styles are included in the page already so no need to use CMS styling. It was cool to find that changing images in the CMS swaps the Gatsby images without any trouble, that's due to a special image component we wrote that will replace the GatsbyImage with a regular img tag if a string src is passed to it. Data is sent into the secondary iframe using the secure Because we're doing a page-builder model, it actually works to just pass the entire const PagePreview: FC<PreviewTemplateComponentProps> = ({ entry }) => {
const win = useRef<HTMLIFrameElement | null>(null);
// home page is a special case without a slug
let src = `${window.location.origin}/`;
if (props.slug !== 'home') {
src += props.slug;
}
// not in hook - refresh page data on render
win.current?.contentWindow?.postMessage(props, location.origin);
return (
<iframe
ref={win}
onLoad={handleLoad}
title={props.slug}
style={{ width: '100%', height: '100vh' }}
src={src}
/>
);
};
CMS.registerPreviewTemplate('pages', PagePreview); Then in the main page template, // this is subbed in for pageContext when set
const [cmsPreviewData, setCmsPreviewData] = useState<typeof pageContext>();
useEffect(() => {
if (typeof window !== 'undefined') {
const handleDataChange = (event: MessageEvent) => {
// Verify same-origin. Browsers won't encode active scripts in postMessage
// Also lightly validate data structure but keep it minimal to avoid false flags
if (
event instanceof MessageEvent &&
event.origin === window.origin &&
event.data?.slug === pageContext.slug &&
Array.isArray(event.data?.['components'])
) {
setCmsPreviewData(event.data);
} else {
console.error('invalid message received');
}
};
window.addEventListener('message', handleDataChange);
return () => window.removeEventListener('message', handleDataChange);
}
}, [pageContext]); I added a few other niceties, including a loading message and a I would hesitate to recommend that anyone use this on a site with live user sessions, but it seems like a great solution for a static site. We haven't made this live yet so please reply with feedback if you have any! Thanks |
Hello, |
I'll give it a shot 🙂 When using MUI 5 npm has a lot of issues with dependency conflicts and that's bugging me dearly Edit: WIP, one test suite failing I don't understand why, I'll try to solve it or create the PR anyway |
Okay so I tried out a couple of solutions proposed here and I can verify that emotion's cache provider with iframe as a container element solves the issue. One problem tho, I notices that in preview, the styles are being duplicated (
|
Turns out memoizing the cache helps Sorry about the any-ies
|
#6645 One test suite failing I don't understand why |
Background
I'm currently using Netlify CMS to build a website for a client. It's a completely static site, but it uses React as a template engine. I also use the React bundle to render a preview inside Netlify CMS.
The other important detail is that this website is styled with styled-components, which works by injecting css rules into
document.getElementsByTagName('head')[0]
.The Problem
Netlify CMS transplants the preview component inside an iframe, while the
<style>
elements generated bystyled-components
remain outside the iframe. This leaves me with an unstyled preview, which is most unappealing. ✨I haven't tested, but I expect this to affect other CSS-in-JS libraries like Glamorous, jsxstyle, or JSS as well as any other React library that injects extra elements, like react-helmet or react-portal.
The text was updated successfully, but these errors were encountered: