Skip to content

Commit

Permalink
Refactor Code components (#770)
Browse files Browse the repository at this point in the history
* Refactor Code components

* Update breezy-kiwis-fold.md

* Explicit label in example

---------

Co-authored-by: Ryan Ling <[email protected]>
  • Loading branch information
AaronMoat and 72636c authored Jan 9, 2025
1 parent 9596fcb commit b2761b6
Show file tree
Hide file tree
Showing 15 changed files with 224 additions and 210 deletions.
13 changes: 13 additions & 0 deletions .changeset/breezy-kiwis-fold.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
'scoobie': major
---

CodeBlock: change props

- `label` and `language` are now required

- `copy` is no longer accepted; the copy button is always shown

To have just the code block without the top row with the copy button, use `CodeContainer`

- `size` is no longer accepted; it is now hardcoded to `standard`
5 changes: 5 additions & 0 deletions .changeset/twelve-bags-grin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'scoobie': minor
---

CodeContainer: add new component
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 21 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,33 @@ export const MyFirstBlockquote = () => (

### CodeBlock

Render lines of code with [Prism] syntax highlighting.

[prism]: https://github.com/PrismJS/prism
Render a rich [`CodeContainer`](#codecontainer) with interactive copy & GraphQL playground link buttons.

```tsx
import React from 'react';
import { CodeBlock } from 'scoobie';

export const MyFirstCodeBlock = () => (
<CodeBlock language="javascript">console.log('hello, world');</CodeBlock>
<CodeBlock language="javascript" label="Here is the code">
console.log('hello, world');
</CodeBlock>
);
```

### CodeContainer

Render code with [Prism] syntax highlighting, with optional `lineNumbers`.

[prism]: https://github.com/PrismJS/prism

```tsx
import React from 'react';
import { CodeContainer } from 'scoobie';

export const MyFirstCodeContainer = () => (
<CodeContainer language="javascript">
console.log('hello, world');
</CodeContainer>
);
```

Expand Down
21 changes: 0 additions & 21 deletions src/components/CodeBlock.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,8 @@ export default {
graphqlPlayground: 'https://manage.developer.seek.com/graphql-explorer',
initialIndex: 0,
language: 'graphql',
size: 'standard',
trim: true,
},
argTypes: {
size: {
control: { type: 'radio' },
options: ['standard', 'large'],
},
},
} satisfies Meta<typeof Component>;

type Story = StoryObj<typeof Component>;
Expand Down Expand Up @@ -69,17 +62,3 @@ export const Multi: Story = {
},
},
};

export const Minimal: Story = {
args: {
children: JSON.stringify(
{ stuff: 'things', otherStuff: [{ id: 17 }] },
null,
2,
),
label: '',
language: 'json',
copy: false,
lineNumbers: false,
},
};
138 changes: 49 additions & 89 deletions src/components/CodeBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,22 @@
import { Box, Stack, Text, TextLinkButton } from 'braid-design-system';
import { parse } from 'jsonc-parser';
import { Highlight } from 'prism-react-renderer';
import React, { useEffect, useState } from 'react';
import { useEffect, useState } from 'react';

import { Prism, themes } from '../private/Prism';
import { ScrollableInline } from '../private/ScrollableInline';
import {
DEFAULT_SIZE,
SIZE_TO_SMALLER,
SIZE_TO_TABLE_PADDING,
type Size,
} from '../private/size';

import { type CodeChildProps, normaliseChildren } from './CodeBlock/CodeChild';
import { GraphQLPlaygroundAction } from './CodeBlock/GraphQLPlaygroundAction';
import { LineNumbers } from './CodeBlock/LineNumbers';
import { Lines } from './CodeBlock/Lines';
import { CodeContainer } from './CodeContainer';
import { CopyableText } from './CopyableText';

import * as styles from './CodeBlock.css';

interface Props {
children: readonly CodeChildProps[] | string;
graphqlPlayground?: string;
initialIndex?: number;
label?: string;
language?: string;
size?: Size;
label: string;
language: string;
trim?: boolean;
lineNumbers?: boolean;
copy?: boolean;
}

export const CodeBlock = ({
Expand All @@ -38,10 +25,8 @@ export const CodeBlock = ({
initialIndex = 0,
label: rawLabel,
language: rawLanguage,
size = DEFAULT_SIZE,
trim = true,
lineNumbers = true,
copy = true,
}: Props) => {
const children = normaliseChildren(
typeof rawChildren === 'string'
Expand All @@ -56,9 +41,6 @@ export const CodeBlock = ({
trim,
);

const smallerSize = SIZE_TO_SMALLER[size];
const tablePadding = SIZE_TO_TABLE_PADDING[size];

const [index, setIndex] = useState({ dirty: false, value: initialIndex });

useEffect(
Expand All @@ -84,87 +66,65 @@ export const CodeBlock = ({

const graphqlPlaygroundButton =
children[0].language === 'graphql' && graphqlPlayground ? (
<Box component="span" paddingLeft={tablePadding}>
<Box component="span" paddingLeft="small">
<GraphQLPlaygroundAction
graphqlPlayground={graphqlPlayground}
size={size}
size="standard"
query={children[0].code}
variables={variables}
/>
</Box>
) : undefined;

const topRow =
children.some(({ label }) => label) || copy || graphqlPlaygroundButton;

return (
<Stack space={tablePadding}>
{topRow ? (
<ScrollableInline whiteSpace="nowrap">
<Box display="flex" justifyContent="spaceBetween">
<Box display="flex">
{children.map(({ label }, labelIndex) =>
label ? (
<Box
component="span"
key={label}
paddingLeft={labelIndex === 0 ? undefined : tablePadding}
<Stack space="small">
<ScrollableInline whiteSpace="nowrap">
<Box display="flex" justifyContent="spaceBetween">
<Box display="flex">
{children.map(({ label }, labelIndex) =>
label ? (
<Box
component="span"
key={label}
paddingLeft={labelIndex === 0 ? undefined : 'small'}
>
<Text
size="small"
tone={index.value === labelIndex ? 'secondary' : undefined}
weight="medium"
>
<Text
size={smallerSize}
tone={
index.value === labelIndex ? 'secondary' : undefined
}
weight="medium"
>
{children.length === 1 || index.value === labelIndex ? (
label
) : (
<TextLinkButton
onClick={() =>
setIndex({ dirty: true, value: labelIndex })
}
>
{label}
</TextLinkButton>
)}
</Text>
</Box>
) : null,
)}
</Box>

<Box display="flex">
{copy ? (
<Box component="span" paddingLeft={tablePadding}>
<CopyableText size={smallerSize}>{child.code}</CopyableText>
{children.length === 1 || index.value === labelIndex ? (
label
) : (
<TextLinkButton
onClick={() =>
setIndex({ dirty: true, value: labelIndex })
}
>
{label}
</TextLinkButton>
)}
</Text>
</Box>
) : null}

{graphqlPlaygroundButton}
</Box>
) : null,
)}
</Box>
</ScrollableInline>
) : null}

<Box borderRadius="large" className={styles.codeContainer}>
<Highlight
prism={Prism}
code={child.code}
language={child.language}
theme={themes.github}
>
{({ getTokenProps, tokens }) => (
<Box display="flex">
{lineNumbers ? (
<LineNumbers count={tokens.length} size={size} />
) : null}

<Lines getTokenProps={getTokenProps} lines={tokens} size={size} />
<Box display="flex">
<Box component="span" paddingLeft="small">
<CopyableText size="small">{child.code}</CopyableText>
</Box>
)}
</Highlight>
</Box>

{graphqlPlaygroundButton}
</Box>
</Box>
</ScrollableInline>

<CodeContainer
code={child.code}
language={child.language}
lineNumbers={lineNumbers}
/>
</Stack>
);
};
8 changes: 2 additions & 6 deletions src/components/CodeBlock/GraphQLPlaygroundAction.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { IconVideo, Text, TextLink } from 'braid-design-system';
import React from 'react';

import { SIZE_TO_SMALLER, type Size } from '../../private/size';
import type { Size } from '../../private/size';

interface Props {
query: string;
Expand All @@ -14,17 +13,14 @@ export const GraphQLPlaygroundAction = ({
query,
variables,
graphqlPlayground,
size,
}: Props) => {
const playgroundUrl = new URL(graphqlPlayground);
playgroundUrl.searchParams.set('query', query);
playgroundUrl.searchParams.set('variables', variables ?? '{}');
const href = playgroundUrl.toString();

const smallerSize = SIZE_TO_SMALLER[size];

return (
<Text size={smallerSize} weight="medium">
<Text size="small" weight="medium">
<TextLink
href={href}
icon={<IconVideo alignY="lowercase" />}
Expand Down
28 changes: 0 additions & 28 deletions src/components/CodeBlock/LineNumbers.tsx

This file was deleted.

35 changes: 0 additions & 35 deletions src/components/CodeBlock/Lines.tsx

This file was deleted.

Loading

0 comments on commit b2761b6

Please sign in to comment.