Skip to content

Commit

Permalink
forward refs and props for UI components
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasheyenbrock committed Aug 7, 2022
1 parent 1c184c1 commit 2d49a47
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 102 deletions.
11 changes: 8 additions & 3 deletions packages/graphiql-react/src/toolbar/button.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { useState } from 'react';
import { forwardRef, useState } from 'react';

import { UnStyledButton } from '../ui';

import './button.css';

export function ToolbarButton(props: JSX.IntrinsicElements['button']) {
export const ToolbarButton = forwardRef<
HTMLButtonElement,
JSX.IntrinsicElements['button']
>((props, ref) => {
const [error, setError] = useState<Error | null>(null);
return (
<UnStyledButton
{...props}
ref={ref}
className={
'graphiql-toolbar-button' +
(error ? ' error' : '') +
Expand All @@ -30,4 +34,5 @@ export function ToolbarButton(props: JSX.IntrinsicElements['button']) {
aria-invalid={error ? 'true' : props['aria-invalid']}
/>
);
}
});
ToolbarButton.displayName = 'ToolbarButton';
21 changes: 13 additions & 8 deletions packages/graphiql-react/src/ui/button-group.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { forwardRef } from 'react';

import './button-group.css';

export function ButtonGroup(props: JSX.IntrinsicElements['div']) {
return (
<div
{...props}
className={`graphiql-button-group ${props.className || ''}`.trim()}
/>
);
}
export const ButtonGroup = forwardRef<
HTMLDivElement,
JSX.IntrinsicElements['div']
>((props, ref) => (
<div
{...props}
ref={ref}
className={`graphiql-button-group ${props.className || ''}`.trim()}
/>
));
ButtonGroup.displayName = 'ButtonGroup';
55 changes: 31 additions & 24 deletions packages/graphiql-react/src/ui/button.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,38 @@
import { forwardRef } from 'react';
import { compose } from '../utility/compose';

import './button.css';

export function UnStyledButton(props: JSX.IntrinsicElements['button']) {
return (
<button
{...props}
className={compose('graphiql-un-styled', props.className)}
/>
);
}
export const UnStyledButton = forwardRef<
HTMLButtonElement,
JSX.IntrinsicElements['button']
>((props, ref) => (
<button
{...props}
ref={ref}
className={compose('graphiql-un-styled', props.className)}
/>
));
UnStyledButton.displayName = 'UnStyledButton';

type ButtonProps = { state?: 'success' | 'error' };

export function Button(props: ButtonProps & JSX.IntrinsicElements['button']) {
return (
<button
{...props}
className={compose(
'graphiql-button',
props.state === 'success'
? 'graphiql-button-success'
: props.state === 'error'
? 'graphiql-button-error'
: '',
props.className,
)}
/>
);
}
export const Button = forwardRef<
HTMLButtonElement,
ButtonProps & JSX.IntrinsicElements['button']
>((props, ref) => (
<button
{...props}
ref={ref}
className={compose(
'graphiql-button',
props.state === 'success'
? 'graphiql-button-success'
: props.state === 'error'
? 'graphiql-button-error'
: '',
props.className,
)}
/>
));
Button.displayName = 'Button';
36 changes: 21 additions & 15 deletions packages/graphiql-react/src/ui/dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
import { Dialog as ReachDialog } from '@reach/dialog';
import { VisuallyHidden } from '@reach/visually-hidden';
import { ComponentProps } from 'react';
import { ComponentProps, forwardRef } from 'react';
import { CloseIcon } from '../icons';
import { createComponentGroup } from '../utility/component-group';
import { compose } from '../utility/compose';
import { UnStyledButton } from './button';

import './dialog.css';

export function Dialog(props: ComponentProps<typeof ReachDialog>) {
return <ReachDialog {...props} />;
}
const DialogRoot = forwardRef<
HTMLDivElement,
ComponentProps<typeof ReachDialog>
>((props, ref) => <ReachDialog {...props} ref={ref} />);
DialogRoot.displayName = 'Dialog';

function DialogClose(props: JSX.IntrinsicElements['button']) {
return (
<UnStyledButton
{...props}
className={compose('graphiql-dialog-close', props.className)}>
<VisuallyHidden>Close dialog</VisuallyHidden>
<CloseIcon />
</UnStyledButton>
);
}
const DialogClose = forwardRef<
HTMLButtonElement,
JSX.IntrinsicElements['button']
>((props, ref) => (
<UnStyledButton
{...props}
ref={ref}
className={compose('graphiql-dialog-close', props.className)}>
<VisuallyHidden>Close dialog</VisuallyHidden>
<CloseIcon />
</UnStyledButton>
));
DialogClose.displayName = 'Dialog.Close';

Dialog.Close = DialogClose;
export const Dialog = createComponentGroup(DialogRoot, { Close: DialogClose });
28 changes: 18 additions & 10 deletions packages/graphiql-react/src/ui/markdown.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { forwardRef } from 'react';
import { markdown } from '../markdown';
import { compose } from '../utility/compose';

import './markdown.css';

Expand All @@ -8,13 +10,19 @@ type MarkdownContentProps = {
type: 'description' | 'deprecation';
};

export function MarkdownContent(props: MarkdownContentProps) {
return (
<div
className={`graphiql-markdown-${props.type}${
props.onlyShowFirstChild ? ' graphiql-markdown-preview' : ''
}`}
dangerouslySetInnerHTML={{ __html: markdown.render(props.children) }}
/>
);
}
export const MarkdownContent = forwardRef<
HTMLDivElement,
MarkdownContentProps & Omit<JSX.IntrinsicElements['div'], 'children'>
>(({ children, onlyShowFirstChild, type, ...props }, ref) => (
<div
{...props}
ref={ref}
className={compose(
`graphiql-markdown-${type}`,
onlyShowFirstChild ? ' graphiql-markdown-preview' : '',
props.className,
)}
dangerouslySetInnerHTML={{ __html: markdown.render(children) }}
/>
));
MarkdownContent.displayName = 'MarkdownContent';
16 changes: 13 additions & 3 deletions packages/graphiql-react/src/ui/spinner.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { forwardRef } from 'react';
import { compose } from '../utility/compose';

import './spinner.css';

export function Spinner() {
return <div className="graphiql-spinner" />;
}
export const Spinner = forwardRef<HTMLDivElement, JSX.IntrinsicElements['div']>(
(props, ref) => (
<div
{...props}
ref={ref}
className={compose('graphiql-spinner', props.className)}
/>
),
);
Spinner.displayName = 'Spinner';
88 changes: 49 additions & 39 deletions packages/graphiql-react/src/ui/tabs.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { forwardRef } from 'react';
import { CloseIcon } from '../icons';
import { createComponentGroup } from '../utility/component-group';
import { compose } from '../utility/compose';
import { UnStyledButton } from './button';

Expand All @@ -8,60 +10,68 @@ type TabProps = {
isActive?: boolean;
};

export function Tab({
isActive,
...props
}: TabProps & JSX.IntrinsicElements['div']) {
return (
<div
{...props}
role="tab"
aria-selected={isActive}
className={compose(
'graphiql-tab',
isActive ? 'graphiql-tab-active' : '',
props.className,
)}>
{props.children}
</div>
);
}

function TabButton(props: JSX.IntrinsicElements['button']) {
return (
<UnStyledButton
{...props}
type="button"
className={compose('graphiql-tab-button', props.className)}>
{props.children}
</UnStyledButton>
);
}
const TabRoot = forwardRef<
HTMLDivElement,
TabProps & JSX.IntrinsicElements['div']
>(({ isActive, ...props }, ref) => (
<div
{...props}
ref={ref}
role="tab"
aria-selected={isActive}
className={compose(
'graphiql-tab',
isActive ? 'graphiql-tab-active' : '',
props.className,
)}>
{props.children}
</div>
));
TabRoot.displayName = 'Tab';

Tab.Button = TabButton;
const TabButton = forwardRef<
HTMLButtonElement,
JSX.IntrinsicElements['button']
>((props, ref) => (
<UnStyledButton
{...props}
ref={ref}
type="button"
className={compose('graphiql-tab-button', props.className)}>
{props.children}
</UnStyledButton>
));
TabButton.displayName = 'Tab.Button';

function TabClose(props: JSX.IntrinsicElements['button']) {
return (
const TabClose = forwardRef<HTMLButtonElement, JSX.IntrinsicElements['button']>(
(props, ref) => (
<UnStyledButton
aria-label="Close Tab"
title="Close Tab"
{...props}
ref={ref}
type="button"
className={compose('graphiql-tab-close', props.className)}>
<CloseIcon />
</UnStyledButton>
);
}
),
);
TabClose.displayName = 'Tab.Close';

Tab.Close = TabClose;
export const Tab = createComponentGroup(TabRoot, {
Button: TabButton,
Close: TabClose,
});

export function Tabs(props: JSX.IntrinsicElements['div']) {
return (
export const Tabs = forwardRef<HTMLDivElement, JSX.IntrinsicElements['div']>(
(props, ref) => (
<div
{...props}
ref={ref}
role="tablist"
className={compose('graphiql-tabs', props.className)}>
{props.children}
</div>
);
}
),
);
Tabs.displayName = 'Tabs';

0 comments on commit 2d49a47

Please sign in to comment.