Skip to content

Commit

Permalink
migrate button
Browse files Browse the repository at this point in the history
  • Loading branch information
NgocNhi123 committed Jun 30, 2024
1 parent c8b4678 commit 265bbd6
Show file tree
Hide file tree
Showing 5 changed files with 272 additions and 0 deletions.
147 changes: 147 additions & 0 deletions new-docs/src/components/button.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { Meta } from "@storybook/react";
import { GoPlus } from "react-icons/go";
import { Button, Dialog } from "../../../core/src";
import { GalleryButton1, GalleryButton2 } from "../../../gallery/src";
import { Utils } from "../utils/utils";

const meta: Meta = {
title: "Components/Button",
component: Button,
argTypes: {
busy: Utils.arg("boolean", "Visual"),
disabled: Utils.arg("boolean", "Visual"),
fill: Utils.arg("boolean", "Visual"),
highlight: Utils.arg("boolean", "Visual"),
minWidth: Utils.arg("boolean", "Visual"),
selected: Utils.arg("boolean", "Visual"),
size: Utils.arg(Button.sizes, "Visual"),
style: Utils.arg(Button.styles, "Visual"),
color: Utils.arg(Button.colors, "Visual"),

children: Utils.arg(null, "Content"),
icon: Utils.arg(null, "Content"),
iconLabel: Utils.arg(null, "Content"),
iconRight: Utils.arg(null, "Content"),

type: Utils.arg(null, "Button"),
forwardedRef: Utils.arg(null, "Button"),
onClick: Utils.arg(null, "Button"),
onFocus: Utils.arg(null, "Button"),
onBlur: Utils.arg(null, "Button"),
autoFocus: Utils.arg(null, "Button"),
dangerouslySetTabIndex: Utils.arg(null, "Button"),

target: Utils.arg(null, "Link"),
href: Utils.arg(null, "Link"),
rel: Utils.arg(null, "Link"),
download: Utils.arg(null, "Link"),
},
};

Utils.page.component(meta, {
primary: "sticky",
shots: [<GalleryButton1 key="1" />, <GalleryButton2 key="2" />],
});

export default meta;

interface Props {
style?: string;
size?: string;
color?: string;
fill?: boolean;
highlight?: boolean;
selected?: boolean;
busy?: boolean;
iconRight?: boolean;
disabled?: boolean;
}

export const Primary = (props: Props): JSX.Element => (
<Button
onClick={() => Dialog.alert("Hello")}
children="Say Hi"
// eslint-disable-next-line
style={(Button.styles as any)[props.style!]}
// eslint-disable-next-line
size={(Button.sizes as any)[props.size!]}
// eslint-disable-next-line
color={(Button.colors as any)[props.color!]}
fill={props.fill}
highlight={props.highlight}
selected={props.selected}
busy={props.busy}
iconRight={props.iconRight}
disabled={props.disabled}
/>
);

/**
* Moai buttons closely follow the interface and behaviour of the [HTML
*\`button\`][1] element. To get started, you only need to provide a label via
* \`children\` and a handler via \`onClick\`:
*
* [1]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button
*/
export const Basic = (): JSX.Element => (
<Button onClick={() => alert("Hi")}>Say Hi</Button>
);

/**
* Icons can be used in buttons via the \`icon\` prop. This follows our [Icon
* standard][1], which supports any SVG icons. The icon is on the left side by
* default, with the \`iconRight\` prop to move it to the right.
*
* It's [intentional][3] that [screen readers][2] would skip the icon and only
* announce the label of a button (i.e. the text inside the \`children\` prop).
* If your button doesn't have a \`children\` defined (i.e. icon-only buttons),
* provide the \`iconLabel\` prop so screen readers can announce it.
*
* Out of the box, icon-only buttons look like rectangles due to the unequal
* paddings. To make them squares, set the \`size\` prop to one with an \`Icon\`
* suffix, like \`Button.sizes.mediumIcon\`.
*
* [1]: /docs/guides-icons--docs
* [2]: https://en.wikipedia.org/wiki/Screen_reader
* [3]: https://www.sarasoueidan.com/blog/accessible-icon-buttons/#icon-sitting-next-to-text
*/
export const Icon = (): JSX.Element => (
// Icons are imported from external libraries, like:
// import { GoPlus } from "react-icons/go";

<div style={{ display: "flex", gap: 8 }}>
{/* Basic usage with icon */}
<Button icon={GoPlus} children="Add" />

{/* Icon on the right side */}
<Button icon={GoPlus} children="Add" iconRight />

{/* Require "iconLabel" because there is no "children" */}
<Button icon={GoPlus} iconLabel="Add" />

{/* Square icon button */}
<Button icon={GoPlus} iconLabel="Add" size={Button.sizes.mediumIcon} />
</div>
);

/**
* Buttons with \`href\` prop are rendered as [HTML \`a\`][1] elements instead of
* the usual \`button\`. This helps you have links that look like buttons (e.g.
* with strong appearance to attract attention) but still preserve all [built-in
* behaviours][3] of links.
*
* For example, you can right click the below button to copy the URL or open it
* in a new tab:
*
* [1]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a
* [2]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button
* [3]: https://www.nngroup.com/articles/command-links
*/
export const Link = (): JSX.Element => (
<Button
highlight
href="https://moai.thien.do"
target="_blank"
children="Go to moai.thien.do"
/>
);
42 changes: 42 additions & 0 deletions new-docs/src/utils/arg.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { ControlType } from "@storybook/blocks";
import { ArgTypes, Args } from "@storybook/react/*";

const argOptions = (target: unknown): string[] | undefined => {
if (target === null || target === undefined) return;

if (Array.isArray(target)) {
return target;
}

if (typeof target === "object") {
return Object.keys(target as any);

Check failure on line 12 in new-docs/src/utils/arg.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
}
};

const argControl = (target: unknown): ControlType | false => {
if (target === null || target === undefined) return false;

if (Array.isArray(target)) {
const type = target.length > 4 ? "select" : "radio";
return type;
}

if (typeof target === "object") {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const options = Object.keys(target as any);
const type = options.length > 4 ? "select" : "radio";
return type;
}

return target as ControlType;
};

export const utilsArg = (
target: unknown,
category?: string,
): Partial<ArgTypes<Args>> => {
const table = category ? { category } : undefined;
const control = argControl(target) as any;

Check failure on line 39 in new-docs/src/utils/arg.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
const options = argOptions(target);
return { options, control, table };
};
14 changes: 14 additions & 0 deletions new-docs/src/utils/page/component.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.shots {
display: flex;
gap: 24px;
}

.sticky .primary {
position: sticky;
top: 0;
z-index: 3;
}

.sticky .table {
padding: 1px;
}
51 changes: 51 additions & 0 deletions new-docs/src/utils/page/component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {
Controls,
Description,
Primary,
Stories,
Title,
} from "@storybook/blocks";
import { Meta } from "@storybook/react";
import React from "react";
import { background } from "../../../../core/src";
import s from "./component.module.css";

interface Props {
shots: React.ReactNode[];
primary: "sticky" | "none" | "default";
}

const ComponentPage = (props: Props): JSX.Element => (

Check warning on line 18 in new-docs/src/utils/page/component.tsx

View workflow job for this annotation

GitHub Actions / lint

Fast refresh only works when a file only exports components. Move your component(s) to a separate file
<div className={props.primary === "sticky" ? s.sticky : ""}>
<Title />
<Description />
{props.shots.length > 0 && (
<>
<h3 id="props" className="sbdocs sbdocs-h3">
Gallery
</h3>
<div className={s.shots}>{props.shots}</div>
</>
)}
<Stories includePrimary={false} />
<div>
<h3 id="props" className="sbdocs sbdocs-h3">
All Props
</h3>
{props.primary !== "none" && (
<div className={[s.primary, background.strong].join(" ")}>
<Primary />
</div>
)}
<div className={s.table}>
<Controls />
</div>
</div>
</div>
);

export const utilsPageComponent = (meta: Meta, props: Props): void => {
meta.parameters ??= {};
meta.parameters.docs ??= {};
meta.parameters.docs.page = () => <ComponentPage {...props} />;
};
18 changes: 18 additions & 0 deletions new-docs/src/utils/utils.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { utilsArg } from "./arg";
import { utilsPageComponent } from "./page/component";

/**
* Utilities to work with Storybook
*/
export const Utils = {
/**
* Try to generate a suitable control for passed "target"
*/
arg: utilsArg,
page: {
/**
* layout Component pages
*/
component: utilsPageComponent,
},
};

0 comments on commit 265bbd6

Please sign in to comment.