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

Improve ability to customise theme #236

Open
chris48s opened this issue Jan 3, 2023 · 7 comments
Open

Improve ability to customise theme #236

chris48s opened this issue Jan 3, 2023 · 7 comments

Comments

@chris48s
Copy link
Contributor

chris48s commented Jan 3, 2023

Hello.

I recently put together a proof-of-concept to evaluate moving a site to using docusaurus-openapi. I was able to apply some customisations by creating a custom theme and using the apiItemComponent setting to tell docusaurus-openapi to use my custom theme instead of docusaurus-theme-openapi but I found that I had to copy a fairly large chunk of code from docusaurus-theme-openapi to apply some relatively minor customisations and I fear it could be tricky to keep in sync over time. I'd like to see if I can make some contributions that would allow us to adopt docusaurus-openapi while maintaining a smaller surface area of forked code. There were three things I really wanted to do.


1. Customise the Response component

Our API is a bit esoteric. It mostly returns SVG images and we wanted to render the image as the response instead of showing text output. To achieve this, I defined a custom Response component with the following change:

chris48s/shields-docusaurus-poc@b9a7fa8

but in order to apply that change, I also needed to completely override the ApiItem and ApiDemoPanel components despite the fact that I didn't need to make any changes to them.

I think APIs that return images is a sufficiently non-standard requirement that I don't necessarily think this needs to be directly addressed as a use-case in the default theme (although I do note there is another issue related to rendering binary images #234 ), but it would be nice if there were a way to inject a custom Response component without having to fork so much of the theme code to allow users to handle more unusual response types like this. Assuming you don't want this in core, I think my suggestion in this case would be to allow injecting a custom Response component, or possibly define a subset of components (including Response) which can be injected in a similar way to apiItemComponent to make customisation easier. The subset of components which can be injected in this way then essentially becomes a theme plugin interface.


2. Additional language examples

Currently, docusaurus-openapi gives you 2 options for code examples: You can either use https://github.com/postmanlabs/postman-code-generators which gives you a wide variety of presets and generates the code example based on a postmanRequest object with the values from the form. Alternatively, you can define x-code-samples in your OpenAPI definition, but the code examples are just rendered as static content. They aren't dynamic based on the values from the form.

Because this API returns images, I wanted my "code" examples to show how to embed the images in HTML, markdown, reStructuredText, etc. I also wanted the examples to be dynamic based on the form values. These don't exist as presets in postman-code-generators. To achieve this, I defined my code examples in x-code-samples with a 'placeholder' value and edited my component to replace the placeholder value with one from postmanRequest.

chris48s/shields-docusaurus-poc@67a873b

(I renamed my component from Curl to Example, but its basically the same code from https://github.com/cloud-annotations/docusaurus-openapi/blob/main/packages/docusaurus-theme-openapi/src/theme/ApiDemoPanel/Curl/index.tsx#L160-L190 )

This is a crude and incomplete implementation (it ignores headers, POST body, etc), but you get the idea.

I think I am slightly less clear on my suggested implementation here. The objective is it would be useful to be able to define custom code examples which don't exist as presets in https://github.com/postmanlabs/postman-code-generators but which are dynamic based on the form. I guess the options would include:

  • Formalise the ability to supply some kind of placeholder values in x-code-samples
  • Add 'a third way' for users to define additional dynamic code examples
  • Allow users to inject a custom Curl component without having to fully override ApiItem and ApiDemoPanel

3. Disable DocPaginator

This one is the easiest. I wanted to disable the DocPaginator so I just deleted it.

chris48s/shields-docusaurus-poc@a6f5d04

This could be a simple boolean flag. This is also the least important customisation I made.


As I say, I'm happy to try and make some contributions to help with these features, but it feels like the first step is to discuss the way forward and define what the preferred implementations would look like. I can also split this out into 3 smaller issues if it helps.

@bourdakos1
Copy link
Member

2 and 3 sound good to me :)

for 1 have you tried swizzling? https://docusaurus.io/docs/swizzling I’ve never done it before, but this sounds like it might be a good use case? If it works we can better document it so that others can easily create their own response panels

@chris48s
Copy link
Contributor Author

chris48s commented Jan 8, 2023

Thanks for the response. I must admit, I am a first-time swizzler 😄 . Conceptually this seems a lot like the solution I was looking for. It also seems like marking certain components as 'safe' means there is already a mechanism to provide a stable plugin interface.

Having had a bit of an experiment, here's some findings:

I was not able to swizzle the Response component in isolation. When run npm run swizzle, it is listed as a component I can swizzle and does spit out a file I can modify, but I still have to swizzle the whole ApiDemoPanel component to get it to pick up the local version of the component, so that didn't really help. Same thing for the Curl component.

My theory is that this is either to do with

  • The nested structure (Response is a subdir under ApiDemoPanel) or
  • The import statements are relative paths (i.e: import Response from "./Response"; not import Response from "@theme/ApiDemoPanel/Response";, or whatever)

or possibly a combination of both, but I haven't really dug into it.

One thing I was able to do using this approach was I removed the pagination by swizzling the core DocPaginator component and replacing it with

import React from 'react';

export default function DocPaginator(props) {
  return '';
}

so that worked at least.

@bourdakos1
Copy link
Member

Yea, I’m guessing it’s the relative imports. Looking at the standard Docusaurus theme, they have a nested structure, but use “@theme/…” for all imports. We should update all of our imports to match.

@brittonhayes
Copy link

@chris48s @bourdakos1 have there been any developments on the additional language examples functionality you mentioned in number 2? At the very least an update to the docs to indicate to users that they can set something like the following in their theme config:

// working theme config edit to add more languages
  themeConfig:
    /** @type {import('docusaurus-preset-openapi').ThemeConfig} */
    ({
      languageTabs: [
        {
          tabName: "cURL",
          highlight: "bash",
          language: "curl",
          variant: "curl",
          options: {
            longFormat: false,
            followRedirect: true,
            trimRequestBody: true,
          },
        },
        {
          tabName: "Javascript",
          highlight: "javascript",
          language: "javascript",
          variant: "fetch",
          options: {
            ES6_enabled: true,
            trimRequestBody: true,
          },
        },
        ...

I've noticed that while this does generate the code for all supported languages from the postman-code-generator, it doesn't support highlighting for all the languages via prism-react-renderer. For example python, javascript (axios and fetch), and go all highlight perfectly. However when I try the same pattern set for the other languages but specifying something like Dart, Swift, Ruby, or Rust - the highlighting no longer works.

So, TLDR: I think this open issue sounds like a lot of great ideas and I think even just adding more clear support for other language examples as part of these customization enhancements would be quite useful as an end user!

// This generates the code but highlighting does not work
{
    tabName: "Rust",
    highlight: "rust",
    language: "rust",
    variant: "reqwest",
    options: {
      followRedirect: true,
      trimRequestBody: true,
    },
},

image

image

@chris48s
Copy link
Contributor Author

The way we solved this was:

docusaurus-theme-openapi >=0.6.4 allows the Curl component to be swizzled in isolation

We made a custom Curl component which substitutes the placeholder string $url in examples from x-code-samples with postmanRequest.url.toString(): https://github.com/badges/shields/blob/master/frontend/src/theme/ApiDemoPanel/Curl/index.js#L188-L191

We define our code examples in x-code-samples. So our x-code-samples block might look like

"x-code-samples": [
  {
    "lang": "URL",
    "label": "URL",
    "source": "$url"
  },
  {
    "lang": "Markdown",
    "label": "Markdown",
    "source": "![PyPI - License]($url)"
  },
  {
    "lang": "reStructuredText",
    "label": "rSt",
    "source": ".. image:: $url\n:   alt: PyPI - License"
  },
  {
    "lang": "AsciiDoc",
    "label": "AsciiDoc",
    "source": "image:$url[PyPI - License]"
  },
  {
    "lang": "HTML",
    "label": "HTML",
    "source": "<img alt=\"PyPI - License\" src=\"$url\">"
  }
]

..and then the page that generates is https://shields.io/badges/py-pi-license

Not sure how much that helps. This works for us, but it might not be the right solution for everyone. The way we are using this is a bit esoteric in some ways, but quite simple in others.

@brittonhayes
Copy link

Thanks so much for the reply! I'll take a look at this and see how you all managed to get the functionality implemented.

@cocowalla
Copy link

@brittonhayes did you ever figure out how to get syntax highlighting working with Rust, Java, C# etc?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants