From 9ea155e4f48fbf11fc2ca26d84e51499776cefb7 Mon Sep 17 00:00:00 2001 From: MananTank Date: Wed, 13 Nov 2024 19:21:50 +0000 Subject: [PATCH] [Dashboard] Refactor: Migrate CodeSegment to shadcn/tailwind + Improved UI (#5407) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem solved Short description of the bug fixed or feature added --- ## PR-Codex overview This PR focuses on refactoring the code related to `CodeSegment` and `CodeEnvironment`, moving them to a new location and updating their usage across various components. It also introduces a new `CodeSegment` component with improved functionality. ### Detailed summary - Deleted `types.ts` and `CodeSegment.tsx` from `contract-tabs`. - Introduced `CodeSegment` and `CodeEnvironment` in `code-segment.client.tsx`. - Updated imports in multiple files to use the new `CodeSegment` and `CodeEnvironment`. - Enhanced `CodeSegment` to manage code snippets for different environments. - Added stories for `CodeSegment` in `code-segment.stories.tsx`. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` --- .../components/blocks/code-segment.client.tsx | 131 +++++++++++++ .../blocks/code-segment.stories.tsx | 174 ++++++++++++++++++ apps/dashboard/src/@/components/ui/tabs.tsx | 10 +- .../(team)/~/usage/storage/page.tsx | 6 +- .../src/components/connect/CodePlayground.tsx | 2 +- .../contract-functions/contract-function.tsx | 6 +- .../contract-tabs/code/CodeSegment.tsx | 163 ---------------- .../components/contract-tabs/code/types.ts | 15 -- .../tabs/code/components/code-overview.tsx | 6 +- 9 files changed, 326 insertions(+), 187 deletions(-) create mode 100644 apps/dashboard/src/@/components/blocks/code-segment.client.tsx create mode 100644 apps/dashboard/src/@/components/blocks/code-segment.stories.tsx delete mode 100644 apps/dashboard/src/components/contract-tabs/code/CodeSegment.tsx delete mode 100644 apps/dashboard/src/components/contract-tabs/code/types.ts diff --git a/apps/dashboard/src/@/components/blocks/code-segment.client.tsx b/apps/dashboard/src/@/components/blocks/code-segment.client.tsx new file mode 100644 index 00000000000..4789ca76888 --- /dev/null +++ b/apps/dashboard/src/@/components/blocks/code-segment.client.tsx @@ -0,0 +1,131 @@ +"use client"; +import { CodeClient } from "@/components/ui/code/code.client"; +import type React from "react"; +import { type Dispatch, type SetStateAction, useMemo } from "react"; +import { TabButtons } from "../ui/tabs"; + +export type CodeEnvironment = + | "javascript" + | "typescript" + | "react" + | "react-native" + | "unity"; + +type SupportedEnvironment = { + environment: CodeEnvironment; + title: string; +}; + +type CodeSnippet = Partial>; + +const Environments: SupportedEnvironment[] = [ + { + environment: "javascript", + title: "JavaScript", + }, + { + environment: "typescript", + title: "TypeScript", + }, + { + environment: "react", + title: "React", + }, + { + environment: "react-native", + title: "React Native", + }, + { + environment: "unity", + title: "Unity", + }, +]; + +interface CodeSegmentProps { + snippet: CodeSnippet; + environment: CodeEnvironment; + setEnvironment: + | Dispatch> + | ((language: CodeEnvironment) => void); + isInstallCommand?: boolean; + hideTabs?: boolean; + onlyTabs?: boolean; +} + +export const CodeSegment: React.FC = ({ + snippet, + environment, + setEnvironment, + isInstallCommand, + hideTabs, + onlyTabs, +}) => { + const activeEnvironment: CodeEnvironment = useMemo(() => { + return ( + snippet[environment] ? environment : Object.keys(snippet)[0] + ) as CodeEnvironment; + }, [environment, snippet]); + + const activeSnippet = useMemo(() => { + return snippet[activeEnvironment]; + }, [activeEnvironment, snippet]); + + const lines = useMemo( + () => (activeSnippet ? activeSnippet.split("\n") : []), + [activeSnippet], + ); + + const code = lines.join("\n").trim(); + + const environments = Environments.filter( + (env) => + Object.keys(snippet).includes(env.environment) && + snippet[env.environment], + ); + + return ( +
+ {!hideTabs && ( + ({ + label: env.title, + onClick: () => setEnvironment(env.environment), + isActive: activeEnvironment === env.environment, + name: env.title, + isEnabled: true, + }))} + tabClassName="text-sm gap-2 !text-sm" + tabIconClassName="size-4" + tabContainerClassName="px-3 pt-1.5 gap-0.5" + hideBottomLine={!!onlyTabs} + /> + )} + + {onlyTabs ? null : ( + <> + + + )} +
+ ); +}; diff --git a/apps/dashboard/src/@/components/blocks/code-segment.stories.tsx b/apps/dashboard/src/@/components/blocks/code-segment.stories.tsx new file mode 100644 index 00000000000..7b2c0999073 --- /dev/null +++ b/apps/dashboard/src/@/components/blocks/code-segment.stories.tsx @@ -0,0 +1,174 @@ +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import type { Meta, StoryObj } from "@storybook/react"; +import { useState } from "react"; +import { BadgeContainer, mobileViewport } from "../../../stories/utils"; +import { type CodeEnvironment, CodeSegment } from "./code-segment.client"; + +const meta = { + title: "blocks/CodeSegment", + component: Story, + parameters: { + nextjs: { + appDirectory: true, + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Desktop: Story = { + args: {}, +}; + +export const Mobile: Story = { + args: {}, + parameters: { + viewport: mobileViewport("iphone14"), + }, +}; + +type Mode = "default" | "no-tabs" | "only-tabs"; + +function Story() { + const [mode, setMode] = useState("default"); + const modes: Mode[] = ["default", "no-tabs", "only-tabs"]; + + return ( +
+ + + + + + + +
+ ); +} + +function Variant(props: { + envsToShow: CodeEnvironment[]; + storyLabel: string; + mode: Mode; +}) { + const [env, setEnv] = useState( + props.envsToShow[0] || "javascript", + ); + return ( + + + + ); +} + +const jsCode = `\ +import { getContract } from "thirdweb"; +import { sepolia } from "thirdweb/chains"; +import { getOwnedNFTs } from "thirdweb/extensions/erc1155"; + +const contract = getContract({ + client, + address: "0x1234...", + chain: sepolia, +}); +`; + +const jsxCode = `\ +import { ThirdwebProvider } from "thirdweb/react"; + +function Main() { + return ( + + + + ); +}`; + +const unityCode = `\ +using Thirdweb; + +// Reference the SDK +var sdk = ThirdwebManager.Instance.SDK; + +// Create data +NFTMetadata meta = new NFTMetadata() +{ + name = "Unity NFT", + description = "Minted From Unity", + image = "ipfs://QmbpciV7R5SSPb6aT9kEBAxoYoXBUsStJkMpxzymV4ZcVc", +}; +string metaJson = Newtonsoft.Json.JsonConvert.SerializeObject(meta); + +// Upload raw text or from a file path +var response = await ThirdwebManager.Instance.SDK.storage.UploadText(metaJson);`; + +const tsCode = `\ +type User = { + name: string; + age: number; +} + +function logUser(user: User) { + console.log(user) +} +`; diff --git a/apps/dashboard/src/@/components/ui/tabs.tsx b/apps/dashboard/src/@/components/ui/tabs.tsx index 686cc73a705..58cde11e601 100644 --- a/apps/dashboard/src/@/components/ui/tabs.tsx +++ b/apps/dashboard/src/@/components/ui/tabs.tsx @@ -87,6 +87,8 @@ export function TabButtons(props: { tabContainerClassName?: string; containerClassName?: string; shadowColor?: string; + tabIconClassName?: string; + hideBottomLine?: boolean; }) { const { containerRef, lineRef, activeTabRef } = useUnderline(); @@ -94,7 +96,9 @@ export function TabButtons(props: { return (
{/* Bottom line */} -
+ {!props.hideBottomLine && ( +
+ )} - {tab.icon && } + {tab.icon && ( + + )} {tab.name} ); diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/~/usage/storage/page.tsx b/apps/dashboard/src/app/team/[team_slug]/(team)/~/usage/storage/page.tsx index 5157370bc92..63ef2086fe5 100644 --- a/apps/dashboard/src/app/team/[team_slug]/(team)/~/usage/storage/page.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/(team)/~/usage/storage/page.tsx @@ -1,9 +1,11 @@ "use client"; import { ChakraProviderSetup } from "@/components/ChakraProviderSetup"; +import { + type CodeEnvironment, + CodeSegment, +} from "@/components/blocks/code-segment.client"; import { Divider, Flex, GridItem, SimpleGrid, Tooltip } from "@chakra-ui/react"; -import { CodeSegment } from "components/contract-tabs/code/CodeSegment"; -import type { CodeEnvironment } from "components/contract-tabs/code/types"; import { RelevantDataSection } from "components/dashboard/RelevantDataSection"; import { useState } from "react"; import { Card, Heading, Link, Text, TrackedCopyButton } from "tw-components"; diff --git a/apps/dashboard/src/components/connect/CodePlayground.tsx b/apps/dashboard/src/components/connect/CodePlayground.tsx index 30348291360..3174e39c617 100644 --- a/apps/dashboard/src/components/connect/CodePlayground.tsx +++ b/apps/dashboard/src/components/connect/CodePlayground.tsx @@ -1,3 +1,4 @@ +import type { CodeEnvironment } from "@/components/blocks/code-segment.client"; import { CodeClient } from "@/components/ui/code/code.client"; import { Flex, @@ -10,7 +11,6 @@ import { } from "@chakra-ui/react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { ChakraNextImage } from "components/Image"; -import type { CodeEnvironment } from "components/contract-tabs/code/types"; import { Aurora } from "components/homepage/Aurora"; import { connectPlaygroundData } from "components/product-pages/common/connect/data"; import { useState } from "react"; diff --git a/apps/dashboard/src/components/contract-functions/contract-function.tsx b/apps/dashboard/src/components/contract-functions/contract-function.tsx index 6e6e24dd00b..12c777b802f 100644 --- a/apps/dashboard/src/components/contract-functions/contract-function.tsx +++ b/apps/dashboard/src/components/contract-functions/contract-function.tsx @@ -1,5 +1,9 @@ "use client"; +import { + type CodeEnvironment, + CodeSegment, +} from "@/components/blocks/code-segment.client"; import { CopyTextButton } from "@/components/ui/CopyTextButton"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; @@ -49,8 +53,6 @@ import { COMMANDS, formatSnippet, } from "../../contract-ui/tabs/code/components/code-overview"; -import { CodeSegment } from "../contract-tabs/code/CodeSegment"; -import type { CodeEnvironment } from "../contract-tabs/code/types"; import { InteractiveAbiFunction } from "./interactive-abi-function"; const ContractFunctionComment = lazy( diff --git a/apps/dashboard/src/components/contract-tabs/code/CodeSegment.tsx b/apps/dashboard/src/components/contract-tabs/code/CodeSegment.tsx deleted file mode 100644 index 784b126cff6..00000000000 --- a/apps/dashboard/src/components/contract-tabs/code/CodeSegment.tsx +++ /dev/null @@ -1,163 +0,0 @@ -import { CodeClient } from "@/components/ui/code/code.client"; -import { ButtonGroup, Flex, Icon } from "@chakra-ui/react"; -import { JavaScriptIcon } from "components/icons/brand-icons/JavaScriptIcon"; -import { ReactIcon } from "components/icons/brand-icons/ReactIcon"; -import { TypeScriptIcon } from "components/icons/brand-icons/TypeScriptIcon"; -import { UnityIcon } from "components/icons/brand-icons/UnityIcon"; -import { type Dispatch, type JSX, type SetStateAction, useMemo } from "react"; -import { Button } from "tw-components"; -import type { ComponentWithChildren } from "types/component-with-children"; -import type { - CodeEnvironment, - CodeSnippet, - SupportedEnvironment, -} from "./types"; - -const Environments: SupportedEnvironment[] = [ - { - environment: "javascript", - title: "JavaScript", - icon: JavaScriptIcon, - colorScheme: "yellow", - }, - { - environment: "typescript", - title: "TypeScript", - icon: TypeScriptIcon, - colorScheme: "blue", - }, - { - environment: "react", - title: "React", - icon: ReactIcon, - colorScheme: "purple", - }, - { - environment: "react-native", - title: "React Native", - icon: ReactIcon, - colorScheme: "purple", - }, - { - environment: "unity", - title: "Unity", - icon: UnityIcon, - colorScheme: "purple", - }, -]; - -interface CodeSegmentProps { - snippet: CodeSnippet; - environment: CodeEnvironment; - setEnvironment: - | Dispatch> - | ((language: CodeEnvironment) => void); - isInstallCommand?: boolean; - hideTabs?: boolean; - onlyTabs?: boolean; -} - -export const CodeSegment: React.FC = ({ - snippet, - environment, - setEnvironment, - isInstallCommand, - hideTabs, - onlyTabs, -}) => { - const activeEnvironment: CodeEnvironment = useMemo(() => { - return ( - snippet[environment] ? environment : Object.keys(snippet)[0] - ) as CodeEnvironment; - }, [environment, snippet]); - - const activeSnippet = useMemo(() => { - return snippet[activeEnvironment]; - }, [activeEnvironment, snippet]); - - const lines = useMemo( - () => (activeSnippet ? activeSnippet.split("\n") : []), - [activeSnippet], - ); - - const code = lines.join("\n").trim(); - - const environments = Environments.filter( - (env) => - Object.keys(snippet).includes(env.environment) && - snippet[env.environment], - ); - - return ( -
- {!hideTabs && ( - - - - {environments.map((env) => ( - } - active={activeEnvironment === env.environment} - onClick={() => setEnvironment(env.environment)} - > - {env.title} - - ))} - - - - )} - - {onlyTabs ? null : ( - <> - - - )} -
- ); -}; - -interface SupportedEnvironmentButtonProps { - active: boolean; - icon?: JSX.Element; - isDisabled?: boolean; - onClick: () => void; -} - -const SupportedEnvironmentButton: ComponentWithChildren< - SupportedEnvironmentButtonProps -> = ({ active, icon, onClick, children, isDisabled }) => { - return ( - - ); -}; diff --git a/apps/dashboard/src/components/contract-tabs/code/types.ts b/apps/dashboard/src/components/contract-tabs/code/types.ts deleted file mode 100644 index e79d85d8c43..00000000000 --- a/apps/dashboard/src/components/contract-tabs/code/types.ts +++ /dev/null @@ -1,15 +0,0 @@ -export type CodeEnvironment = - | "javascript" - | "typescript" - | "react" - | "react-native" - | "unity"; - -export interface SupportedEnvironment { - environment: CodeEnvironment; - title: string; - icon: React.FC; - colorScheme: string; -} - -export type CodeSnippet = Partial>; diff --git a/apps/dashboard/src/contract-ui/tabs/code/components/code-overview.tsx b/apps/dashboard/src/contract-ui/tabs/code/components/code-overview.tsx index 7357ed6b29d..cb113e59227 100644 --- a/apps/dashboard/src/contract-ui/tabs/code/components/code-overview.tsx +++ b/apps/dashboard/src/contract-ui/tabs/code/components/code-overview.tsx @@ -1,4 +1,8 @@ "use client"; +import { + type CodeEnvironment, + CodeSegment, +} from "@/components/blocks/code-segment.client"; import { Alert, AlertDescription, @@ -24,8 +28,6 @@ import { } from "abitype"; import { getContractFunctionsFromAbi } from "components/contract-components/getContractFunctionsFromAbi"; import { useContractEvents } from "components/contract-components/hooks"; -import { CodeSegment } from "components/contract-tabs/code/CodeSegment"; -import type { CodeEnvironment } from "components/contract-tabs/code/types"; import { useSearchParams } from "next/navigation"; import { useMemo, useState } from "react"; import * as ERC20Ext from "thirdweb/extensions/erc20";