Skip to content

Commit

Permalink
Added display of examples in doc (#300)
Browse files Browse the repository at this point in the history
  • Loading branch information
franckgaudin committed Jun 19, 2024
2 parents d55a969 + c854550 commit a120ea3
Show file tree
Hide file tree
Showing 55 changed files with 854 additions and 276 deletions.
3 changes: 2 additions & 1 deletion apps/docs/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"PreviewComponent": true,
"MigrateGuide": true,
"PackageInstallation": true,
"PropTable": true
"PropTable": true,
"Example": true
},
"overrides": [
{
Expand Down
8 changes: 3 additions & 5 deletions apps/docs/.storybook/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@
width: 100%;
min-height: 100vh;
padding: 2rem;
background: linear-gradient(90deg, var(--stb-container-background) 10px, transparent 1%) 50%,
linear-gradient(var(--stb-container-background) 10px, transparent 1%) 50%,
var(--stb-container-color);
background: var(--stb-container-background);
background-size: 12px 12px;
}

[data-theme="dark"] {
--sbt-container-background: var(--hd-neutral-900);
--sbt-container-color: var(--hd-neutral-800);
--stb-container-background: var(--hd-neutral-800);
--stb-container-color: var(--hd-neutral-800);
}
14 changes: 14 additions & 0 deletions apps/docs/.storybook/main.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import type { StorybookConfig } from "@storybook/nextjs";
import TsconfigPathsPlugin from "tsconfig-paths-webpack-plugin";
import path from "path";

export default {
stories: ["../components/**/*.stories.@(ts|tsx)", "../app/ui/**/*.stories.@(ts|tsx)"],
env: config => ({
...config,
STORYBOOK_MODE: "active"
}),
addons: [
"@storybook/addon-a11y",
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions"
Expand All @@ -15,6 +21,9 @@ export default {
docs: {
autodocs: "tag"
},
features: {
experimentalRSC: true
},
webpackFinal: async config => {
// Configure aliases
if (config.resolve) {
Expand All @@ -23,7 +32,12 @@ export default {
new TsconfigPathsPlugin({
extensions: config.resolve.extensions
})

];
config.resolve.alias = {
...config.resolve.alias,
"@": path.resolve(__dirname, "app")
};
}

// Configure SVGR
Expand Down
12 changes: 8 additions & 4 deletions apps/docs/.storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Preview } from "@storybook/react";
import React from "react";

import "../app/globals.css";

// Storybook styles
Expand All @@ -22,12 +23,15 @@ const preview: Preview = {
},
globalTypes: {
scheme: {
name: "Color scheme",
description: "Global color scheme for components",
description: "Global theme for components",
defaultValue: "light",
toolbar: {
icon: "mirror",
items: ["light", "dark"],
title: "Theme",
icon: "circlehollow",
items: [
{ value: "light", title: "Light" },
{ value: "dark", title: "Dark" }
],
dynamicTitle: true
}
}
Expand Down
4 changes: 2 additions & 2 deletions apps/docs/app/components/[...slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ export default async function ComponentPage({ params }: PageProps) {
},
{
name: "npm",
src: links.npm,
src: "https://www.npmjs.com/package/@hopper-ui/components",
label: "View on npm"
},
{
name: "issue",
src: links.issue,
src: "https://github.com/gsoft-inc/wl-hopper/issues/new",
label: "Report an issue"
}
];
Expand Down
8 changes: 8 additions & 0 deletions apps/docs/app/lib/getComponentCode.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { highlightCode } from "@/components/highlightCode";
import { getFormattedCode } from "@/app/lib/getComponentCode.ts";

export async function mockGetComponentCode(code: string) {
const mockCode = await getFormattedCode(code);

return await highlightCode(mockCode);
}
1 change: 1 addition & 0 deletions apps/docs/app/rehype.css
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ code[data-theme*=' '] span {
content: counter(line);
display: inline-block;
margin-right: var(--hd-space-2);
min-width: var(--hd-space-2);
text-align: right;
color: var(--hd-codeblock-line-line-number-color);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { getComponentCode } from "@/app/lib/getComponentCode.ts";
import HighlightCode from "../../../../components/highlightCode/HighlightCode.tsx";

interface ComponentCodeWrapperProps {
src: string;
}

export const ComponentCodeWrapper = async ({ src }: ComponentCodeWrapperProps) => {
if (!src) {
return null;
}

const code = await getComponentCode(src);

return <HighlightCode code={code} />;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { Meta, StoryObj } from "@storybook/react";

import { mockGetComponentCode } from "@/app/lib/getComponentCode.mock.ts";
import ComponentExample from "./ComponentExample.tsx";
import HighlightCode from "../../../../components/highlightCode/HighlightCode.tsx";
import ComponentPreview from "@/app/ui/components/componentExample/ComponentPreview.tsx";

const meta = {
title: "components/ComponentExample",
component: ComponentExample,
args: {
src: "buttons/docs/button"
}
} satisfies Meta<typeof ComponentExample>;

export default meta;
type Story = StoryObj<typeof meta>;

const mockCode = await mockGetComponentCode("import { Button } from \"@headless/button\"");
const preview = <ComponentPreview src="buttons/docs/button/preview" />;
const MockComponentCodeWrapper = ({ code }: { code: string }) => <HighlightCode code={code} />;

export const Default: Story = {
args: {
type: "both",
preview,
code: <MockComponentCodeWrapper code={mockCode} />
}
};

export const Code: Story = {
args: {
type: "code",
code: <MockComponentCodeWrapper code={mockCode} />
}
};

export const Preview: Story = {
args: {
type: "preview",
preview
}
};

export const Open: Story = {
args: {
type: "code",
code: <MockComponentCodeWrapper code={mockCode} />,
isOpen: true
}
};
106 changes: 106 additions & 0 deletions apps/docs/app/ui/components/componentExample/ComponentExample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
"use client";

import { memo, type ReactNode } from "react";
import clsx from "clsx";

import { CodeIcon } from "@/components/icon";
import { ToggleButton } from "@/components/toggleButton/ToggleButton.tsx";
import { useToggle } from "@/hooks/useToggle.ts";

import ComponentPreviewWrapper from "./ComponentPreviewWrapper.tsx";

import "./componentExample.css";

interface CommonProps {
src: string;
className?: string;
isOpen?: boolean;
}

interface CodeProps extends CommonProps {
type: "code";
code: ReactNode;
}

interface PreviewProps extends CommonProps {
type: "preview";
preview: ReactNode;
}

interface BothProps extends CommonProps {
type: "both";
code: ReactNode;
preview: ReactNode;
}

export type ComponentExampleProps = CodeProps | PreviewProps | BothProps;

const ComponentExample = memo(({
src,
type = "both",
className,
isOpen = false,
...props
}: ComponentExampleProps) => {
const [showCode, toggleShowCode] = useToggle(isOpen);

const showBothComponent = type === "both";
const showPreviewComponent = type === "preview" || showBothComponent;
const showCodeComponent = (showBothComponent && showCode) || type === "code";

if (!src) {
return null;
}

const renderToggleButton = () => {
if (!showBothComponent) {
return null;
}

return (
<ToggleButton isSelected={showCode}
className="hd-component-preview-wrapper__action"
onPress={toggleShowCode}
>
<CodeIcon />
<span>Show code</span>
</ToggleButton>
);
};

const renderPreviewComponent = () => {
if (!showPreviewComponent) {
return null;
}

return (
<ComponentPreviewWrapper
preview={(type === "preview" || type === "both") ? (props as PreviewProps | BothProps).preview : undefined}
toggleButton={renderToggleButton()}
/>
);
};

const renderCodeComponent = () => {
if (!showCodeComponent) {
return null;
}

return (
<div className={clsx("hd-component-code", showCodeComponent && "hd-component-code--expanded")}>
{(type === "code" || type === "both") ? (props as CodeProps | BothProps).code : undefined}
</div>
);
};

return (
<div data-usage={type}
className={clsx("hd-component-example", showCodeComponent && "hd-component-example--expanded", className)}
>
{renderPreviewComponent()}
{renderCodeComponent()}
</div>
);
});

export default ComponentExample;
23 changes: 23 additions & 0 deletions apps/docs/app/ui/components/componentExample/ComponentPreview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"use client";

import { useMemo } from "react";
import dynamic from "next/dynamic";

import ComponentSkeleton from "./ComponentSkeleton.tsx";

import "./componentSkeleton.css";

interface ComponentPreviewProps {
src: string;
}

const ComponentPreview = ({ src }: ComponentPreviewProps) => {
const DynamicComponent = useMemo(() => dynamic(() => import(`../../../../../../packages/components/src/${src}.tsx`), {
ssr: false,
loading: () => <ComponentSkeleton overlay />
}), [src]);

return <DynamicComponent />;
};

export default ComponentPreview;
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"use client";

import { useState, memo, type ReactNode, useCallback } from "react";
import Card from "@/components/card/Card.tsx";
import ThemeSwitch from "@/components/themeSwitch/ThemeSwitch.tsx";
import type { ColorScheme } from "@/context/theme/ThemeProvider.tsx";
import { StyledSystemProvider } from "@hopper-ui/styled-system";

import "./componentPreviewWrapper.css";

interface ComponentPreviewWrapperProps {
preview?: ReactNode;
toggleButton?: ReactNode;
height?: string;
}

const ComponentPreviewWrapper = memo(({ preview, toggleButton, height = "13rem" }: ComponentPreviewWrapperProps) => {
const [colorScheme, setColorScheme] = useState<"light" | "dark">("light");

const toggleTheme = useCallback(() => {
const theme: ColorScheme = colorScheme === "dark"
? "light"
: "dark";

setColorScheme(theme);
}, [colorScheme]);

return (
<div
className="hd-component-preview-wrapper"
data-schema={colorScheme}
style={{ height: height }}
>
<div className="hd-component-preview-wrapper__actions">
{toggleButton}
<ThemeSwitch className="hd-component-preview-wrapper__action"
onChange={toggleTheme}
colorMode={colorScheme}
/>
</div>
<Card className="hd-component-preview-wrapper__card" size="sm">
<StyledSystemProvider colorScheme={colorScheme}>
{preview}
</StyledSystemProvider>
</Card>

</div>
);
});

export default ComponentPreviewWrapper;
13 changes: 13 additions & 0 deletions apps/docs/app/ui/components/componentExample/ComponentSkeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import clsx from "clsx";

interface ComponentSkeletonProps {
overlay?: boolean;
}

const PreviewSkeleton = ({ overlay = false }: ComponentSkeletonProps) => <div
className={clsx("hd-component-example-skeleton", {
"hd-component-example-skeleton__overlay": overlay
})}
/>;

export default PreviewSkeleton;
Loading

0 comments on commit a120ea3

Please sign in to comment.