sanity-io / ui

UI building blocks for Sanity.
https://sanity.io/ui
MIT License
129 stars 19 forks source link

[Code] Allow registration of other languages #1304

Open maxijonson opened 4 months ago

maxijonson commented 4 months ago

Is your feature request related to a problem? Please describe. Right now, there seem to be code highlighting for the <Code /> component only for javascript, json, jsx and typescript. I'm using it as a preview component for the Studio, and I pass in the selected language in parameter (for example, "tsx" or "sh") but I can't get highlighting to work, even if I try registerLanguage myself.

Describe the solution you'd like It would be nice to be able to support other languages

Describe alternatives you've considered This is a shortened version of what I'm trying, just to illustrate the problem, but it doesn't work:

import { Stack, Code } from "@sanity/ui";
import { registerLanguage } from "react-refractor";
import tsx from "refractor/lang/tsx";

registerLanguage(tsx);

const Preview = (props) => {
  return (
    <Stack>
      {props.renderDefault(props)}
      <Code language={props.language}>{props.code}</Code>
    </Stack>
  );
};

const schema = defineType({
  // ...
  preview: {
    select: {
      code: "code",
      language: "language",
    },
  },
  components: {
    preview: Preview,
  },
});

Additional context This is what the editor looks like. I've logged the props and the "language" is correctly set to "tsx", but there's no highlight. image

It might be worth noting that I have both refractor and react-refractor listed as dependencies because I use it differently in my frontend.

maxijonson commented 4 months ago

In case you want the actual, specific use-case I'm trying to work with, here it is:

// code-group.ts
import { CodeIcon } from "@sanity/icons";
import { defineArrayMember, defineField, defineType } from "sanity";
import CodeGroupPreview from "../../desk/components/code-group-preview/code-group-preview";

export default defineType({
  type: "object",
  name: "codeGroup",
  title: "Code Group",
  icon: CodeIcon,
  fields: [
    defineField({
      type: "array",
      name: "snippets",
      of: [
        defineArrayMember({
          type: "code",
          options: {
            language: "text",
            withFilename: true,
            languageAlternatives: [
              /**
               * If you add more languages, you'll need to:
               * 1. Add the language below
               * 2. Update the post-body-code.tsx to register the language
               */
              { title: "TypeScript", value: "typescript" },
              { title: "TSX", value: "tsx" },
              { title: "Shell", value: "sh" },
              { title: "JSON", value: "json" },
              { title: "Text", value: "text" },
            ],
          },
        }),
      ],
    }),
  ],
  preview: {
    select: {
      snippets: "snippets",
    },
  },
  components: {
    preview: CodeGroupPreview,
  },
});
// code-group-preview.tsx
"use client";
import {
  Button,
  Code,
  Container,
  Menu,
  MenuButton,
  MenuItem,
  Stack,
} from "@sanity/ui";
import { useEffect, useState } from "react";
import { registerLanguage } from "react-refractor";
import tsx from "refractor/lang/tsx";
import type { PreviewProps } from "sanity";
import type { CodeGroup } from "../../../sanity.types";

registerLanguage(tsx);

export type CodeGroupPreviewProps = PreviewProps & {
  snippets?: CodeGroup["snippets"];
};

const CodeGroupPreview = ({
  snippets = [],
  ...props
}: CodeGroupPreviewProps) => {
  const [activeTab, setActiveTab] = useState(0);
  const activeSnippet = snippets[activeTab];

  useEffect(() => {
    if (activeSnippet) return;
    setActiveTab(0);
  }, [activeSnippet]);

  if (!activeSnippet) return null;
  return (
    <Stack space={3}>
      {props.renderDefault({
        ...props,
        title: `Code Group | ${snippets.length} ${snippets.length === 1 ? "Snippet" : "Snippets"}`,
        layout: "block",
      })}
      <MenuButton
        button={
          <Button text={activeSnippet.filename || `Snippet ${activeTab}`} />
        }
        id="code-group-preview-menu"
        menu={
          <Menu>
            {snippets.map((snippet, index) => (
              <MenuItem
                key={index}
                onClick={() => setActiveTab(index)}
                padding={3}
                text={snippet.filename || `Snippet ${index}`}
              />
            ))}
          </Menu>
        }
        popover={{ portal: true }}
      />
      <Container padding={3} overflow="auto">
        <Code language="tsx">{activeSnippet.code}</Code>
      </Container>
    </Stack>
  );
};

export default CodeGroupPreview;

I'm using an array of the @sanity/code-input type. Weirdly enough, when opening my preview, which lists all code snippets from the code type, Syntax highlighting works there, but never on the preview component