From 19823191f32801ca71cff6ee0db2eec8369b8eb0 Mon Sep 17 00:00:00 2001 From: Sergo Date: Wed, 17 Apr 2024 23:47:28 +0200 Subject: [PATCH] Improve stories --- .../src/__stories__/shared/button.stories.tsx | 5 +- .../__stories__/shared/warning.stories.tsx | 36 ++++++ .../widgets/token-upgrade-amount.stories.tsx | 2 +- ...token-upgrade-token-extensions.stories.tsx | 81 +++++++++++++ .../token-upgrade-token-info.stories.tsx | 105 +++++++++++------ packages/ui/src/shared/warning.tsx | 6 +- packages/ui/src/widgets/token-upgrade.tsx | 2 +- .../ui/src/widgets/token-upgrade/badge.tsx | 3 +- .../token-upgrade/token-extensions.tsx | 67 +++++------ .../src/widgets/token-upgrade/token-info.tsx | 107 +++++------------- 10 files changed, 264 insertions(+), 150 deletions(-) create mode 100644 packages/ui/src/__stories__/shared/warning.stories.tsx create mode 100644 packages/ui/src/__stories__/widgets/token-upgrade-token-extensions.stories.tsx diff --git a/packages/ui/src/__stories__/shared/button.stories.tsx b/packages/ui/src/__stories__/shared/button.stories.tsx index 5d5e0d6..5fd9ad1 100644 --- a/packages/ui/src/__stories__/shared/button.stories.tsx +++ b/packages/ui/src/__stories__/shared/button.stories.tsx @@ -14,14 +14,13 @@ const headerStory = { // More on argTypes: https://storybook.js.org/docs/api/argtypes argTypes: {}, args: { - "data-testid": "button", onClick: fn(), }, } export default headerStory -export const Default: StoryObj> = { +export const Default: StoryObj = { args: { children: "Button Text", }, @@ -29,7 +28,7 @@ export const Default: StoryObj> = { const ctx = within(canvasElement) await step("should be clickable", async () => { - const btn = ctx.getByTestId("button") + const btn = ctx.getByRole("button") await expect(btn).not.toBeDisabled() await userEvent.click(btn) }) diff --git a/packages/ui/src/__stories__/shared/warning.stories.tsx b/packages/ui/src/__stories__/shared/warning.stories.tsx new file mode 100644 index 0000000..22ab651 --- /dev/null +++ b/packages/ui/src/__stories__/shared/warning.stories.tsx @@ -0,0 +1,36 @@ +import { Warning } from "../../shared/warning" +import type { StoryObj } from "@storybook/react" +import { expect, fn, within } from "@storybook/test" + +const headerStory = { + title: "UI/Shared/Warning", + component: Warning, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout + layout: "padded", + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs + tags: ["autodocs"], + // More on argTypes: https://storybook.js.org/docs/api/argtypes + argTypes: {}, + args: { + "data-testid": "warning", + onClick: fn(), + }, +} + +export default headerStory + +export const Default: StoryObj = { + args: { + message: "Warning Text", + }, + async play({ canvasElement, step }: any) { + const ctx = within(canvasElement) + + await step("should be rendered", async () => { + const el = ctx.getByRole("alert", { busy: false }) + await expect(el).toBeVisible() + }) + }, +} diff --git a/packages/ui/src/__stories__/widgets/token-upgrade-amount.stories.tsx b/packages/ui/src/__stories__/widgets/token-upgrade-amount.stories.tsx index 9771011..e3a5f4e 100644 --- a/packages/ui/src/__stories__/widgets/token-upgrade-amount.stories.tsx +++ b/packages/ui/src/__stories__/widgets/token-upgrade-amount.stories.tsx @@ -1,5 +1,5 @@ import * as Form from "@radix-ui/react-form" -import Amount from "../../widgets/token-upgrade/amount" +import { Amount } from "../../widgets/token-upgrade/amount" import type { StoryFn, StoryObj } from "@storybook/react" import { expect, fn, userEvent, within } from "@storybook/test" diff --git a/packages/ui/src/__stories__/widgets/token-upgrade-token-extensions.stories.tsx b/packages/ui/src/__stories__/widgets/token-upgrade-token-extensions.stories.tsx new file mode 100644 index 0000000..fcb1114 --- /dev/null +++ b/packages/ui/src/__stories__/widgets/token-upgrade-token-extensions.stories.tsx @@ -0,0 +1,81 @@ +import * as web3 from "@solana/web3.js" +import type { StoryFn, StoryObj } from "@storybook/react" +import { create } from "superstruct" +import { expect, within } from "@storybook/test" +import { + DefaultAccountState, + PermanentDelegate, + TokenExtension, + TransferFeeAmount, + TransferHookAccount, +} from "../../entities/token/validators" +import { TokenExtensions } from "../../widgets/token-upgrade/token-extensions" + +const meta = { + title: "UI/Token Upgrade/TokenExtensions", + component: TokenExtensions, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout + layout: "padded", + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs + tags: ["autodocs"], + // More on argTypes: https://storybook.js.org/docs/api/argtypes + args: {}, + argTypes: {}, + decorators: [ + (Story: StoryFn) => { + return + }, + ], +} + +export default meta +type Story = StoryObj + +const defaultAccountState = create( + { accountState: "initialized" }, + DefaultAccountState, +) + +const transferHookAccount = create({ transferring: true }, TransferHookAccount) + +const permanentDelegate = create( + { delegate: web3.Keypair.generate().publicKey }, + PermanentDelegate, +) +const transferFeeAmount = create({ withheldAmount: 0 }, TransferFeeAmount) + +export const Default: Story = { + args: { + address: "Bk9ErfcwzZ8MgSPdEgfmkreNHad9ik8jYP8um", + extensions: [ + create( + { extension: "defaultAccountState", state: defaultAccountState }, + TokenExtension, + ), + create( + { extension: "transferHookAccount", state: transferHookAccount }, + TokenExtension, + ), + create( + { extension: "permanentDelegate", state: permanentDelegate }, + TokenExtension, + ), + create( + { extension: "transferFeeAmount", state: transferFeeAmount }, + TokenExtension, + ), + ], + }, + async play({ canvasElement, step }: any) { + const ctx = within(canvasElement) + + await step("should render extensions", async () => { + await expect(ctx.getByText("defaultAccountState")).toBeVisible() + await expect(ctx.getByText("transferHookAccount")).toBeVisible() + await expect(ctx.getByText("permanentDelegate")).toBeVisible() + await expect(ctx.getByText("transferFeeAmount")).toBeVisible() + }) + }, +} diff --git a/packages/ui/src/__stories__/widgets/token-upgrade-token-info.stories.tsx b/packages/ui/src/__stories__/widgets/token-upgrade-token-info.stories.tsx index e0c276f..eaa1902 100644 --- a/packages/ui/src/__stories__/widgets/token-upgrade-token-info.stories.tsx +++ b/packages/ui/src/__stories__/widgets/token-upgrade-token-info.stories.tsx @@ -1,11 +1,10 @@ -import * as Form from "@radix-ui/react-form" -import Amount from "../../widgets/token-upgrade/amount" +import { TokenInfoBase as TokenInfo } from "../../widgets/token-upgrade/token-info" import type { StoryFn, StoryObj } from "@storybook/react" -import { expect, fn, userEvent, within } from "@storybook/test" +import { expect, userEvent, within } from "@storybook/test" const meta = { - title: "UI/Token Upgrade/Token Info", - component: Amount, + title: "UI/Token Upgrade/TokenInfo", + component: TokenInfo, parameters: { // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout layout: "padded", @@ -13,46 +12,86 @@ const meta = { // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs tags: ["autodocs"], // More on argTypes: https://storybook.js.org/docs/api/argtypes - args: { - onAmountChange: fn(), - onAmountMaxChange: fn(), - }, - argTypes: { - address: { control: "text" }, - balance: { control: "text" }, - label: { control: "text" }, - name: { control: "text" }, - step: { control: "number" }, - symbol: { control: "text" }, - }, + args: {}, + argTypes: {}, decorators: [ (Story: StoryFn) => { return ( - - - - - +
+ +
) }, ], } export default meta -type Story = StoryObj +type Story = StoryObj export const Default: Story = { + args: { + address: "Bk9ErfcwzZ8MgSPdEgfmkreNHad9ik8jYP8um", + extensions: [ + { + extension: "permanentDelegate", + state: { delegate: "DELEGATE_ADDRESS" }, + }, + ], + }, + async play({ canvasElement, step }: any) { + const ctx = within(canvasElement) + + await step("should render extensions", async () => { + const pdExtension = ctx.getByText("permanentDelegate") + await expect(pdExtension).toBeVisible() + await expect(pdExtension).not.toBeDisabled() + await userEvent.click(pdExtension) + await userEvent.click(canvasElement) + }) + }, +} + +export const NoExtensions: Story = { + args: { + address: "Bk9ErfcwzZ8MgSPdEgfmkreNHad9ik8jYP8um", + extensions: [], + }, + async play({ canvasElement, step }: any) { + const ctx = within(canvasElement) + + await step("should render text message", async () => { + await expect( + ctx.getByText("There are no token extensions enabled."), + ).toBeVisible() + await expect(ctx.getByRole("alert", { busy: false })).toBeVisible() + }) + }, +} + +export const Failure: Story = { + args: { + address: "Bk9ErfcwzZ8MgSPdEgfmkreNHad9ik8jYP8um", + error: new Error("Can not fetch token info"), + }, + async play({ canvasElement, step }: any) { + const ctx = within(canvasElement) + + await step("should render warning", async () => { + await expect(ctx.getByRole("alert", { busy: false })).toBeVisible() + }) + }, +} + +export const Empty: Story = { args: { address: undefined, }, - /* - * async play({ canvasElement, step }: any) { - * const ctx = within(canvasElement) - * - * await step("should be rendered", async () => { - * const el = ctx.getByRole("spinbutton", { description: "Amount" }) - * await expect(el).not.toBeDisabled() - * }) - * }, - */ + async play({ canvasElement, step }: any) { + const ctx = within(canvasElement) + + await step("should render nothing", async () => { + const el = ctx.getByTestId("story-inner-wrapper") + await expect(el).toBeEmptyDOMElement() + }) + }, } diff --git a/packages/ui/src/shared/warning.tsx b/packages/ui/src/shared/warning.tsx index 38f254a..fe1364a 100644 --- a/packages/ui/src/shared/warning.tsx +++ b/packages/ui/src/shared/warning.tsx @@ -3,7 +3,11 @@ import { ExclamationTriangleIcon } from "@heroicons/react/20/solid" export function Warning(props: { message: string }) { return ( -
+
( return ( diff --git a/packages/ui/src/widgets/token-upgrade/token-extensions.tsx b/packages/ui/src/widgets/token-upgrade/token-extensions.tsx index d0ea5cf..266d7ab 100644 --- a/packages/ui/src/widgets/token-upgrade/token-extensions.tsx +++ b/packages/ui/src/widgets/token-upgrade/token-extensions.tsx @@ -28,44 +28,47 @@ export function TokenExtensions({ address, extensions }: TokenExtensionProps) { {(extensions?.length ?? 0) > 0 ? (
- {extensions?.map(({ extension, state }, i) => { - return ( - - - - + {extensions?.map(({ extension, state }, i) => { + return ( + + + + + {extension} + + + - {extension} - - - -
- {JSON.stringify(state)} -
-
-
-
- ) - })} +
+ {JSON.stringify(state)} +
+ + + + ) + })} +
) : (
There are no token extensions enabled. diff --git a/packages/ui/src/widgets/token-upgrade/token-info.tsx b/packages/ui/src/widgets/token-upgrade/token-info.tsx index 8ba1711..b46b62f 100644 --- a/packages/ui/src/widgets/token-upgrade/token-info.tsx +++ b/packages/ui/src/widgets/token-upgrade/token-info.tsx @@ -1,95 +1,46 @@ import React, { useEffect, useState } from "react" -import { cva, VariantProps } from "class-variance-authority" import { TokenExtensions } from "./token-extensions" -import { useTokenExtension } from "../../entities/token/use-token-extension" +import { + useTokenExtension, + TokenExtensionList, +} from "../../entities/token/use-token-extension" import { Warning } from "../../shared/warning" -const inputVariants = cva( - "block w-full rounded border-0 py-1.5 sm:text-sm sm:leading-6", - { - variants: { - data: { - absent: "pl-10 pr-1.5", - present: "pl-10 pr-20", - }, - variant: { - disabled: "pointer-events-none rounded-md", - regular: "rounded-md", - active: "rounded-none rounded-r-md", - }, - alert: { - no: "text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600", - error: - "text-red-900 ring-1 ring-red-300 placeholder:text-red-300 focus:ring-2 focus:ring-inset focus:ring-red-500", - }, - }, - compoundVariants: [ - { - variant: "disabled", - alert: "error", - class: "pr-20", - }, - { - variant: "active", - data: "present", - class: "!pl-1", - }, - ], - defaultVariants: { - data: "absent", - variant: "regular", - alert: "no", - }, - }, -) - -interface AmountProps - extends VariantProps, - React.ComponentPropsWithoutRef<"input"> { +interface TokenInfoProps extends React.ComponentPropsWithoutRef<"div"> { address?: string - balance?: string - error?: Error - label?: string - name?: string - onAmountChange?: (a: { amount: number }) => void - onAmountMaxChange?: (a: { amount: number }) => void - symbol?: string } -export default function Amount({ - address, - balance = "0", - disabled, - label = "Amount", - name = "amount", - onAmountChange, - onAmountMaxChange, - min = 0, - placeholder = "0", - step = 1, - symbol, - value, -}: AmountProps) { +export function TokenInfo({ address }: TokenInfoProps) { const { extensions, error, isLoading } = useTokenExtension(address) - const [err, setError] = useState() + const [err, setError] = useState(null) useEffect(() => { if (!isLoading) setError(error) // eslint-disable-next-line react-hooks/exhaustive-deps }, [isLoading]) - return ( + return +} + +interface TokenInfoBaseProps extends TokenInfoProps { + address?: string + error: Error | null + extensions: TokenExtensionList +} + +export function TokenInfoBase({ + address, + error, + extensions, +}: TokenInfoBaseProps) { + return error ? ( + + ) : extensions && address ? ( <> - {err ? ( - - ) : extensions ? ( - <> -
- -
- - - ) : null} +
+ +
+ - ) + ) : null }