Skip to content
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

Allow a single docs folder to be consumed by 2 docs plugin instances (use-case: normal vs embed/iframe docs) #7480

Open
6 of 7 tasks
alexandernst opened this issue May 24, 2022 · 25 comments
Labels
apprentice Issues that are good candidates to be handled by a Docusaurus apprentice / trainee proposal This issue is a proposal, usually non-trivial change

Comments

@alexandernst
Copy link
Contributor

Have you read the Contributing Guidelines on issues?

Prerequisites

  • I'm using the latest version of Docusaurus.
  • I have tried the npm run clear or yarn clear command.
  • I have tried rm -rf node_modules yarn.lock package-lock.json and re-installing packages.
  • I have tried creating a repro with https://new.docusaurus.io.
  • I have read the console error message carefully (if applicable).

Description

If I add the docs plugin to the list of plugins (multi-instance) and I point it to the same folder that the preset is already pointing to (docs by default), the build process stops working and I see the following errors:

SyntaxError: /code/src/......../index.mdx: Identifier 'React' has already been declared. (46:184)

I believe the bug might be caused by the way each mdx file is being processed. Because a mdx file is (basically) a JSX file, import React from 'react'; is being prepended at the top of each file. But since I have two instances of the docs plugin pointing to the same folder, the import statement is being prepended twice (hence the error).

I made a minimal repro codesandbox.

Reproducible demo

https://codesandbox.io/s/trusting-rosalind-sc4s15

Steps to reproduce

  1. Open the link
  2. Open a new tty and type npm run build
  3. The build will fail.
  4. Comment the plugins section of the docusaurus.config.js
  5. Run npm run build again.
  6. The build will succeed.

Expected behavior

I should be able to point multiple instances of the docs plugin to the same folder.

Actual behavior

The build crashes.

Your environment

  • Public source code: N/A
  • Public site URL: N/A
  • Docusaurus version used: 2.0.0-beta20
  • Environment name and version (e.g. Chrome 89, Node.js 16.4): N/A
  • Operating system and version (e.g. Ubuntu 20.04.2 LTS): N/A

Self-service

  • I'd be willing to fix this bug myself.
@alexandernst alexandernst added bug An error in the Docusaurus core causing instability or issues with its execution status: needs triage This issue has not been triaged by maintainers labels May 24, 2022
@Josh-Cena
Copy link
Collaborator

Interesting. I get why it fails, but I don't understand what you are trying to achieve—what's the use-case? We do expect plugins to work in isolation and their folder paths do not overlap.

@Josh-Cena Josh-Cena added domain: content plugin Related to content plugin emitting metadata for theme consumption and removed status: needs triage This issue has not been triaged by maintainers domain: content plugin Related to content plugin emitting metadata for theme consumption labels May 24, 2022
@alexandernst
Copy link
Contributor Author

I'd like to be able to render the same documentation with different layouts on different URLs. Eg. foo.com/docs/intro should render the navbar, the sidebar, the docs itself, etc..., while foo.com/embedded/intro should render just the intro document, without any other elements (no navbar, no sidebar, no header, no footer, etc...). My use-case is being able to embed a particular document inside an iframe.

@Josh-Cena
Copy link
Collaborator

Ah, I see. I don't really think you need multiple plugins to do this, but I don't have a better solution on top of my head right now. Because you have the same set of data and metadata, it should be doable within the same plugin, just passing it to two themes.

@Josh-Cena Josh-Cena changed the title Multi-inistance of docs plugin pointing to same folder causes errors Allow same set of docs plugin content to be rendered by multiple themes May 24, 2022
@Josh-Cena Josh-Cena added proposal This issue is a proposal, usually non-trivial change and removed bug An error in the Docusaurus core causing instability or issues with its execution labels May 24, 2022
@alexandernst
Copy link
Contributor Author

How could I do that?

@Josh-Cena
Copy link
Collaborator

Josh-Cena commented May 24, 2022

I don't really know. I don't think it's possible with today's setup, which is why I'm still leaving it open :) But it's definitely legitimate and we'll figure out a way to do it. Sorry if you think it's going to be a quick little bug fix—it's probably more complicated than we have thought.

@slorber will be back tomorrow, and maybe he can have some opinions to offer as well.

@alexandernst
Copy link
Contributor Author

@slorber Hi! Did you have a chance to look into this proposal?

@slorber
Copy link
Collaborator

slorber commented May 27, 2022

No, I am reviewing existing PRs for now

@slorber slorber changed the title Allow same set of docs plugin content to be rendered by multiple themes Allow a single docs folder to be consumed by 2 docs plugin instances (use-case: normal vs embed/iframe docs) Jun 3, 2022
@slorber
Copy link
Collaborator

slorber commented Jun 3, 2022

@alexandernst this definitively makes sense to me, and is something I actually suggested in related discussions:

I'm surprised that it doesn't work actually, it's worth investigating.

We could allow you to do what you initially want: using 2 docs plugin instances and customize the layout accordingly.


Let's also focus on the use case instead of the actual technical details, and see if we can come up with a better technical proposal:

  • why do you want to embed docs?
  • how do you want to embed the docs? (popup, iframe, WebView...)
  • do you need to embed the whole docs folder, or just a few docs?
  • can you provide a production URL of a real doc that you would like to embed?
  • do you care much about the embedded doc URL? (ie should it start with /docs/ vs /embed)
  • what should happen if the user clicks a link on that page?

I'm not 100% convinced using 2 docs plugin instances is the ideal solution, because:

  • it forces you to create a duplicate for each existing doc, which may be overkill for many use-cases
  • it creates a different canonical URL for a doc that has exactly the same content, which may be bad for SEO (although google may understand this, I'm not sure it does. Note that for docs version, we may be able to add additional metadata in pages in the future to mitigate this)

Some possible technical solutions:

  • have you tried injecting custom CSS in the iframe/webview? You may try to manually include it in a hacky way as a post-build step for example, and only add the class if the iframe is loaded with a given querystring ?embed=true)
  • have you tried creating a custom page for the doc you want to embed, and importing the MDX file you want there? You wouldn't need to apply a layout on this page.

https://codesandbox.io/s/white-monad-b55vuh?file=/src/pages/testEmbed.js

import React from "react";
import Foo from "@site/docs/foo.mdx";

export default function FooEmbed() {
  return (
    <main>
      <h1>Embedded doc without layout</h1>
      <Foo />
    </main>
  );
}
  • Another weird workaround would be to try using 2 distinct Docusaurus sites 😅

@alexandernst
Copy link
Contributor Author

  • why do you want to embed docs?

I have a product with a lot of "elements" (think about it as if the product was a 3D viewer of a car and each element a piece of that car). I want to point users to the documentation site and let them read the entire doc, but I'd also like to be able to show the particular documentation page for each piece.

  • how do you want to embed the docs? (popup, iframe, WebView...)

When each element is clicked a modal window (containing several config options) is shown. Embedding the docs inside an iframe in a tab in that modal window sounds like a reasonably good idea to me.

  • do you need to embed the whole docs folder, or just a few docs?

Each "piece" will have it's own document page, so I'd show only that particular doc page.

  • can you provide a production URL of a real doc that you would like to embed?

No, none of this is publicly available :(

  • do you care much about the embedded doc URL? (ie should it start with /docs/ vs /embed)

Not at all. That would be an iframe, so the url doesn't really matter.

  • what should happen if the user clicks a link on that page?

Hmmm 🤔 haven't really thought about that. Maybe go to whatever link the user clicked, but the "embedded" version?

  • it creates a different canonical URL for a doc that has exactly the same content, which may be bad for SEO (although google may understand this, I'm not sure it does. Note that for docs version, we may be able to add additional metadata in pages in the future to mitigate this)

Indeed. Maybe all of this could be avoided by implementing #5046

  • have you tried injecting custom CSS in the iframe/webview? You may try to manually include it in a hacky way as a post-build step for example, and only add the class if the iframe is loaded with a given querystring ?embed=true)

That really sounds like a hacky solution, not my style I'm afraid.

  • have you tried creating a custom page for the doc you want to embed, and importing the MDX file you want there? You wouldn't need to apply a layout on this page.

That is theoretically possible, but I'll have hundreds of "pieces", which means hundreds of "doc pages". I'm afraid that will grow up to "unmanageable" really fast.

  • Another weird workaround would be to try using 2 distinct Docusaurus sites 😅

I'd rather just copy (on build time) the entire "docs" folder and do it with 2 instances of the docs plugin.

@alexandernst
Copy link
Contributor Author

I'm very interested in this feature, so I might as well try to do a PR. Have you got any thoughts about how this should be implemented?

@slorber
Copy link
Collaborator

slorber commented Jun 16, 2022

First I wonder if it should be a docs feature or a core (blog, pages too) feature? I think it could make sense in core.

The way I see it is that we should have a way to create "variants" of a given route.

Ideally, I'd like this to be flexible:

  • Is it possible to be granular (ie doc1 + blogPost2 instead of all docs, or all pages of a site)
  • it should be possible to create 0-n variants for each page to suit different use-cases
  • the render of a variant should be customizable with a swizzle
  • the path of a variant should rather be flexible? (but we can make it easy to default to /docs/myDoc-variant ?)

It would render the same entry component but would put some "variant = xyz" in some React route context (we have a "routeContext" hidden feature that we could use for that), and the component may tweak some things depending on the rendered variant.

Our theme components should be able to render variants undefined | "embed" by default, but you could implement your own variants the way you want or change defaults.

The variants would have the same props and thus the same permalink/canonical URL by default => better for SEO

We should also handle SPA links differently?

  • Maybe we should create a provider to define a default link target = "_blank" for anything opened with an embed variant?
  • Maybe we should link from one embedded doc to another (but do they always exist? do you have an embed version of your homepage for example? because user can click on home breadcrumb 🤪 )

I guess it should be possible to create a core API to support all that (or a plugin lifecycle 🤔 )

@alexandernst
Copy link
Contributor Author

Hi @slorber ! My project's priorities have changed and I must drop this task, I'm so sorry :(

@slorber
Copy link
Collaborator

slorber commented Jul 13, 2022

No problem, we'll continue investigating.

I also have the use-case for a single folder being consumed by 2 plugins myself, as I'd like to render MDX blog posts in 2 variants:

@dustinlacewell
Copy link

This is driving me nuts. :( Sorry.

@slorber
Copy link
Collaborator

slorber commented Aug 16, 2022

This is driving me nuts. :( Sorry.

not sure what you mean here 🤷‍♂️ if you are blocked by this issue please describe what's your use-case and what prevents you to achieve it. Having multiple users reporting their pain helps us choose how we want to solve it

@slorber
Copy link
Collaborator

slorber commented Mar 2, 2023

For the embedding use-case, would it be useful if we allowed applying a global CSS class directly from the iframe query string?

For example:

<iframe src="https://docusaurus.io/docs/intro?docusaurus-html-class=my-custom-class"></iframe>

This could allow you to easily plug custom CSS rules depending on the class you inject. For example, you could decide to hide the Docusaurus navbar if the class is present:

html.my-custom-class nav.navbar {
  display: none;
}

The class would be applied to <html> as soon as possible, with static inlined JavaScript (similar to what we do with light/dark theme), so there wouldn't be any flash of unstyled content.

The idea was initially discussed here: #8672 (comment) where we also allow define the Docusaurus theme from a querystring variable (PR: #8708)

@alexandernst
Copy link
Contributor Author

I think that this is a good idea! Maybe docusaurus could append that custom docusaurus-html-class query tag to the links that are rendered, so if the user clicks on a link, the frame will load the content with the same styling?

@slorber
Copy link
Collaborator

slorber commented Mar 2, 2023

Thanks @alexandernst , that makes sense that the class stays present as the user navigates the embedded document, so that the layout of that element does not switch to the regular layout.

Actually, I'm not sure it would be that easy to add a class name to <html> because of the way react-helmet works: it tends to override classes that are added imperatively afaik. We'll have to test this a bit.

It's possible that allowing users to provide a data-attribute is a safer choice. Once data attributes are set, they should never be overridden. Even if the URL does not contain it anymore (on link click), if we set <html data-embedded="true"/> it should stay there forever (as long as you navigate with SPA/Docusaurus <Link> component).

<iframe src="https://docusaurus.io/docs/intro?docusaurus-data-embedded=true"></iframe>
[data-embedded='true'] nav.navbar {
  display: none;
}

Let's collect a bit more feedback to see what others think.

@jbguerraz
Copy link

Hello mates 👋 Hello @slorber

We would also love embedding the documentation within a (react) app.

Let's say we have a (react) low-code app and some documentation for full platform documentation available on a static website docs.platform.domain
When we're (using the low-code app) for instance editing some entity, we'd love to have an help button that would then display the documentation for the current entity type in the current window, without loading an iframe but rendering a component like so <Docusaurus route="/docs/entity/someentityttye">

That would really be cool! Maybe then we could add some properties to, for instance, show or hide the frame (header, footer, nav, ...)

We'd be ready to invest time and contribute such feature if somehow it makes sens and if it somehow align with the current project architecture :-)

@slorber
Copy link
Collaborator

slorber commented Apr 6, 2023

@jbguerraz

without loading an iframe but rendering a component like so

I don't understand wha you mean here. If it's not an iframe, how can you reference a pathname. You are either rendering a local React component or inside an iframe, I don't see how to do otherwise


That would really be cool! Maybe then we could add some properties to, for instance, show or hide the frame (header, footer, nav, ...)

In #8672 I suggested that we could inject extra classes from querystring params, so that you can decide to show/hide things conditionally.

<iframe src="https://docs.platform.domain/docs/entity?docusaurus-html-class=embedded-iframe`>

Would this help?

Note we don't implement the behavior for .embedded-iframe, but we allow you to implement it without duplicating the page for embedding.

@dvdokkum
Copy link

Just want to chime in and say injectable classes via querystring param would be a big help for us. We maintain a full docs site, but also want to embed certain docs directly in our main application. Being able to drop the navbar element would make the execution much cleaner.

@slorber
Copy link
Collaborator

slorber commented May 31, 2023

@dvdokkum I implemented it, it should be available in canary today or in v3 alpha/beta/rc/stable a bit later

I only implemented the support for data-attribute injection because with classes it was causing issues on React hydration: React Helmet is erasing the classes that we add inline when it takes over. I think it should be good enough for styling purpose and data-attribute CSS selectors.

#9028

image

In the future it's possible that we design our own official "iframe/embedded mode" but for now we give you the flexibility to implement it yourself. Having your early adopter feedback on this can actually help design the official iframe mode, so please report if it works fine for you.

@dvdokkum
Copy link

Awesome, thank you! I'll give it a whirl later today.

@dvdokkum
Copy link

dvdokkum commented Jun 1, 2023

Yeah, works great, thanks! One thing I am noticing though (probably unrelated?) is that in the Canary release the copy button is no longer appearing on code blocks. Confirmed it wasn't anything I added related this feature, when I pop my version back to @latest the copy buttons come back.

@slorber
Copy link
Collaborator

slorber commented Jun 8, 2023

in the Canary release the copy button is no longer appearing on code blocks.

🤷‍♂️ I still see the copy button. Are you sure?

@slorber slorber added the apprentice Issues that are good candidates to be handled by a Docusaurus apprentice / trainee label Sep 25, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
apprentice Issues that are good candidates to be handled by a Docusaurus apprentice / trainee proposal This issue is a proposal, usually non-trivial change
Projects
None yet
Development

No branches or pull requests

6 participants