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

Component preview #163

Merged
merged 7 commits into from
Aug 8, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ slug: /button
## Example

```tsx live
<PreviewBlock>
<PreviewBlock componentName="Button">
<Button variant="primary" size="md">
Test button
Button
</Button>
</PreviewBlock>
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@ slug: /link

## Example

```tsx live
<PreviewBlock>
<Link>Test link</Link>
</PreviewBlock>
```
TODO:

## Props

Expand Down
1 change: 1 addition & 0 deletions @stellar/design-system-website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"prism-react-renderer": "^1.3.5",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-element-to-jsx-string": "^15.0.0",
"rehype-parse": "^8.0.4",
"rehype-react": "^7.2.0",
"rehype-sanitize": "^5.0.1",
Expand Down
103 changes: 103 additions & 0 deletions @stellar/design-system-website/src/componentPreview/ButtonPreview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import React from "react";
import { ComponentPreview } from "@site/src/components/PreviewBlock";
import CheckIconSvg from "@site/static/img/check-icon.svg";

export const ButtonPreview: ComponentPreview = {
options: [
{
type: "select",
prop: "variant",
options: [
{
value: "primary",
label: "Primary",
},
{
value: "secondary",
label: "Secondary",
},
{
value: "tertiary",
label: "Tertiary",
},
{
value: "destructive",
label: "Destructive",
},
{
value: "error",
label: "Error",
},
{
value: "success",
label: "Success",
},
],
},
{
type: "select",
prop: "size",
options: [
{
value: "md",
label: "MD",
},
{
value: "sm",
label: "SM",
},
{
value: "xs",
label: "XS",
},
],
},
{
type: "checkbox",
prop: "isExtraPadding",
label: "Extra padding",
},
{
type: "checkbox",
prop: "isFullWidth",
label: "Full width",
},
{
type: "checkbox",
prop: "isLoading",
label: "Loading",
},
{
type: "checkbox",
prop: "isPill",
label: "Pill",
},
{
type: "checkbox",
prop: "isUppercase",
label: "Uppercase",
},
{
type: "checkbox",
prop: "icon",
label: "With icon",
customValue: <CheckIconSvg />,
clearProp: "iconPosition",
},
{
type: "select",
prop: "iconPosition",
enabledByProp: "icon",
options: [
{
value: "right",
label: "Right",
},
{
value: "left",
label: "Left",
},
],
},
],
};
173 changes: 169 additions & 4 deletions @stellar/design-system-website/src/components/PreviewBlock/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,171 @@
import React from "react";
import React, { cloneElement, useEffect, useState } from "react";
import reactElementToJSXString from "react-element-to-jsx-string";
import { PlaygroundEditor } from "@site/src/theme/Playground";
import "./styles.css";

export const PreviewBlock = ({ children }: { children: React.ReactNode }) => {
// TODO: theme switch
return <div className="sds-theme-light">{children}</div>;
// Previews
import { ButtonPreview } from "@site/src/componentPreview/ButtonPreview";

type Theme = "sds-theme-light" | "sds-theme-dark";

// Types
export type PreviewOptionType = "select" | "checkbox";

interface PreviewOptionBase {
type: PreviewOptionType;
prop: string;
enabledByProp?: string;
clearProp?: string;
}

interface PreviewOptionSelect extends PreviewOptionBase {
options: {
value: string;
label: string;
}[];
}

interface PreviewOptionCheckbox extends PreviewOptionBase {
label: string;
customValue?: any;
}

export type ComponentPreview = {
options: (PreviewOptionSelect | PreviewOptionCheckbox)[];
};

/**
* This file handles the layout and logic for the PreviewBlock to render
* components.
*
* Config file for every component is in /src/componentPreview/ directory. File
* names are [componentName]Preview.
*/
export const PreviewBlock = ({
componentName,
children,
}: {
componentName: string;
children: React.ReactElement;
}) => {
const [sds, setSds] = useState<any>({});
const { Checkbox, Select } = sds;

// Importing SDS here because we need it async for server-side-rendering
useEffect(() => {
const initSds = async () => {
setSds(await import("@stellar/design-system"));
};
initSds();
}, []);

const [theme, setTheme] = useState<Theme>("sds-theme-light");

// All component previews
const previews: { [key: string]: ComponentPreview } = {
Button: ButtonPreview,
};

const compPreview = previews[componentName];

if (!compPreview) {
throw Error(`There is no preview for "${componentName}" component.`);
}

const [props, setProps] = useState({});

const handleSelectChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
const { id, value } = event.target;
setProps({ ...props, [id]: value });
};

const handleCheckboxChange = (
event: React.ChangeEvent<HTMLInputElement>,
customValue: any,
clearProp: string,
) => {
const { id, checked } = event.target;
const _props = props;

if (!checked && clearProp && props[clearProp]) {
delete _props[clearProp];
}

setProps({
..._props,
[id]: checked ? customValue ?? true : false,
});
};

const renderOption = (option: any) => {
if (option.type === "select") {
return Select ? (
<Select
id={option.prop}
fieldSize="sm"
onChange={handleSelectChange}
disabled={option.enabledByProp && !props[option.enabledByProp]}
key={option.prop}
>
<>
{option.options.map((o) => (
<option value={o.value} key={o.value}>
{o.label}
</option>
))}
</>
</Select>
) : null;
} else if (option.type === "checkbox") {
return Checkbox ? (
<Checkbox
id={option.prop}
fieldSize="sm"
label={option.label}
onChange={(e) =>
handleCheckboxChange(e, option.customValue, option.clearProp)
}
disabled={option.enabledByProp && !props[option.enabledByProp]}
key={option.prop}
/>
) : null;
}

return null;
};

// Default component with props
const component = cloneElement(children, { ...props });

return (
<>
<div className={`PreviewBlock ${theme}`}>
<div className="PreviewBlock__controls">
{Select ? (
<Select
id="theme"
fieldSize="sm"
onChange={(e) => {
setTheme(
e.target.value === "light"
? "sds-theme-light"
: "sds-theme-dark",
);
}}
>
<option value="light">Light</option>
<option value="dark">Dark</option>
</Select>
) : null}

{/* Component options */}
<>{compPreview.options.map((o) => renderOption(o))}</>
</div>

<div className="PreviewBlock__component">{component}</div>
</div>

<PlaygroundEditor>{reactElementToJSXString(component)}</PlaygroundEditor>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
.PreviewBlock {
background-color: var(--color-gray-00);
background-image: radial-gradient(var(--color-gray-40) 10%, transparent 11%),
radial-gradient(var(--color-gray-40) 10%, transparent 11%);
background-size: 12px 12px;
background-position: 0 0, 12px 12px;
background-repeat: repeat;
padding: 1.5rem 1rem;
border-top-left-radius: 0.5rem;
border-top-right-radius: 0.5rem;
border: 1px solid var(--color-gray-40);
}

.PreviewBlock__controls {
display: flex;
align-items: center;
gap: 1rem 0.5rem;
flex-wrap: wrap;
}

.PreviewBlock__controls > * {
flex-shrink: 0;
}

.PreviewBlock__controls .Select {
width: 7.5rem;
}

.PreviewBlock__component {
min-height: 15rem;
display: flex;
align-items: center;
justify-content: center;
}
20 changes: 20 additions & 0 deletions @stellar/design-system-website/src/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@

/* --color-gray-70 */
--ifm-font-color-base: #6f6e77;

/* Playground */
/* --color-gray-00 */
--ifm-pg-background: #ffffff;
/* --color-gray-30 */
--ifm-pg-background-dot: #eeedef;
/* --color-gray-30 */
--ifm-pg-cta-background: #eeedef;
/* --color-gray-90 */
--ifm-pg-cta-color: #000000;
}

/* For readability concerns, you should choose a lighter palette in dark mode. */
Expand Down Expand Up @@ -81,6 +91,16 @@

/* --color-gray-70 */
--ifm-font-color-base: #a09fa6;

/* Playground */
/* --color-gray-00 */
--ifm-pg-background: #000000;
/* --color-gray-30 */
--ifm-pg-background-dot: #28282c;
/* --color-gray-30 */
--ifm-pg-cta-background: #28282c;
/* --color-gray-90 */
--ifm-pg-cta-color: #ffffff;
}

/* Typography */
Expand Down
2 changes: 2 additions & 0 deletions @stellar/design-system-website/src/theme/MDXComponents.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import MDXComponents from "@theme-original/MDXComponents";
import { ComponentDescription } from "@site/src/components/ComponentDescription";
import { ComponentProps } from "@site/src/components/ComponentProps";
import { PreviewBlock } from "@site/src/components/PreviewBlock";

export default {
...MDXComponents,
ComponentDescription,
ComponentProps,
PreviewBlock,
};
Loading
Loading