measuredco / puck

The visual editor for React
https://puckeditor.com
MIT License
5.31k stars 326 forks source link

Improve resiliency of iframe CSS loading #543

Closed chrisvxd closed 3 months ago

chrisvxd commented 3 months ago

Test Puck's iframe behaviour across multiple CSS frameworks and improve resiliency:

Related: #409

xaviemirmon commented 3 months ago

I've been investigating this issue using the Mantine library and have found two problems that prevent it from rendering all of the styles correctly.

  1. The AutoFrame component only mirrors the styles from the document head. Mantine is also injecting styles into the body, and these styles aren't getting mirrored.
  2. Mantine is also relying on data attributes in the html tag for theme/color mode selection, but this isn't getting copied across.
xaviemirmon commented 3 months ago

PR #544 is ready for review.

chrisvxd commented 3 months ago

Mantine now working. @xaviemirmon to review further Tailwind setups before closing.

henning-f commented 3 months ago

Hello @chrisvxd,

we really appreciate your work and love the puck editor. As we would like to work with serverless infrastructure we started a new project with the remix recipe and added Chakra UI and eventually realized that stylings from the Iframe are not generated and rendered in the editor route. The published site will be rendered as expected.

We tried to enforce client rendering and some other hacks, but did not manage to solve it from the outside. We think this issue is connected with the iframe.

I created a minimal sample for this: https://github.com/henning-f/puck-remix-chakra-issue I followed the instructions of the Chakra UI documentation for Remix.

Are these issues connected?

I am looking forward to your response.

MiniMarPaz commented 3 months ago

Hello.

In our project we are using styled-components library for React to style our components. With iframe enabled , those styles are not being picked up by editor. However inline stylings works as expected. Sharing some code example so maybe You can try to reproduce that issue.

Editor Config :

import BlockQuote from './BlockQuote
const config = {
  components: {
    Quote:{
      fields: {
        quote: {
          type: 'text',
        },
      },
      defaultProps: {
        quote: 'Hello, quote',
      },
      render: ({ quote }) => {
        return <BlockQuote quote={quote} />;
      },
    }
  },
};

Component Code

import styled from 'styled-components';

export default function BlockQuote(props) {
  const { quote } = props;
  return (
    <QuoteWrapper>
      <LineDiv />
      <div className='quote-text'>
        <span>{quote}</span>
      </div>
    </QuoteWrapper>
  );
}

const LineDiv = styled.div`
  width: 0.25rem;
  background-color: ${({ theme }) => theme.colors.blue[60]};
  align-self: stretch;
  margin-right: 1rem;
`;

const QuoteWrapper = styled.div`
  padding-bottom: 3rem;
  padding-top: 2rem;
  display: flex;
  align-items: center;

  .quote-text {
    font-size: 1.75rem;
    line-height: 2.25rem;
    color: ${({ theme }) => theme.colors.blue[60]};
  }
`;
henning-f commented 3 months ago

Hey @chrisvxd, Just wanted to confirm that disabling iframes (iframe={{enabled:false}}) fixed the issue with the remix recipe, too. I thought it wouldn't work, but I guess I had a configuration error last time.

xaviemirmon commented 3 months ago

@MiniMarPaz which version of Puck are you using? Have you tried the canary which has some iFrame enhancements?

MiniMarPaz commented 3 months ago

@xaviemirmon I'm using latest available version of Puck. 0.15.0 . I did not tried canary version but I will try and let You know if canary version fix issue

xaviemirmon commented 3 months ago

@henning-f I have been looking at your issue and have narrowed it down to how Emotion Cache is interacting with the iFrame. My current theory is that Emotion Cache isn't able to interact with the style tags in the iFrame.

Related to: https://github.com/emotion-js/emotion/issues/3071 and https://github.com/chakra-ui/chakra-ui/issues/8241

xaviemirmon commented 3 months ago

@MiniMarPaz amazing thanks!

MiniMarPaz commented 3 months ago

@xaviemirmon I've tried canary version of Puck but unfortunately those improvements that You've mentioned do not fix issue. While iframe is enabled, styled-components styles are not picked up.

chrisvxd commented 3 months ago

@MiniMarPaz I tried replicating your issue in the demo app using your code, and was unable to

image

Can you provide a codesandbox that replicates the issue?

chrisvxd commented 3 months ago

Okay, I was able to reproduce the Chakra/Remix issue and have released some new APIs and tools to help in (available in 0.16.0-canary.607a585).

Here's an example chakra + remix example using the new APIs: https://github.com/chrisvxd/puck-chakra-remix-example/commit/baf0863fe0fa4ea1a20c4011320c37a8d489ca32

New APIs

iframe override API

Receives the iframe document as a prop. This enables you to inject your styles and manipulate the iframe document however you need. This example injects a custom emotion cache with the cha key for Chakra:

    <Puck overrides={{
      iframe: ({ children, document }) => {
        const [cache, setCache] = useState<EmotionCache | null>(null);

        useEffect(() => {
          if (document) {
            setCache(
              createCache({
                key: "cha",
                container: document.head,
              })
            );
          }
        }, [document]);

        if (cache) {
          return <CacheProvider value={cache}>{children}</CacheProvider>;
        }

        return <>{children}</>;
      },
    }} />

emotion-cache plugin

This captures the above as a plugin, since it's likely a common use-case. We could consider adding one for style-components, if necessary.

import { Puck } from "@measured/puck";
import createEmotionCache from "@measured/puck-plugin-emotion-cache";

// Create your emotion cache plugin. This example configures it for Chakra.
const chakraEmotionCache = createEmotionCache("cha");

// Render Puck
export function Page() {
  return <Puck config={config} data={data} plugins={[chakraEmotionCache]} />;
}

I'm going to close this out as I'm pretty sure this will cover any remaining use-cases, but shout if there are still issues and I'll