From 06a0b5ae8caafa33767327fb207e552e57dced59 Mon Sep 17 00:00:00 2001 From: Jerel Miller Date: Mon, 6 May 2024 10:23:13 -0600 Subject: [PATCH] Update some design elements based on latest orbit designs (#1349) --- .changeset/moody-grapes-play.md | 5 + package-lock.json | 49 ++++++ package.json | 2 + src/application/App.tsx | 21 +-- src/application/components/Button.tsx | 145 ++++++++++++++---- .../components/Cache/sidebar/EntityList.tsx | 2 +- src/application/components/CopyButton.tsx | 17 +- .../components/GitHubIssueLink.tsx | 2 +- .../components/Layouts/SettingsModal.tsx | 6 + src/application/components/List.tsx | 12 +- src/application/components/ListItem.tsx | 23 ++- src/application/components/Modal/Body.tsx | 7 +- src/application/components/Modal/Footer.tsx | 2 +- src/application/components/Modal/Header.tsx | 6 +- src/application/components/Modal/Modal.tsx | 2 +- src/application/components/Modal/Title.tsx | 2 +- .../components/Queries/Queries.tsx | 2 +- .../Queries/RunInExplorerButton.tsx | 6 +- src/application/components/Tabs/Trigger.tsx | 2 +- .../components/Tooltip/Tooltip.tsx | 4 +- src/application/types/utils.ts | 5 + tailwind.config.ts | 2 +- 22 files changed, 249 insertions(+), 75 deletions(-) create mode 100644 .changeset/moody-grapes-play.md create mode 100644 src/application/types/utils.ts diff --git a/.changeset/moody-grapes-play.md b/.changeset/moody-grapes-play.md new file mode 100644 index 000000000..3d86cb2d7 --- /dev/null +++ b/.changeset/moody-grapes-play.md @@ -0,0 +1,5 @@ +--- +"apollo-client-devtools": patch +--- + +Fix some design issues to match latest design implementation diff --git a/package-lock.json b/package-lock.json index 2bf4da655..0e6ecca41 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "@radix-ui/react-tabs": "^1.0.2", "@radix-ui/react-tooltip": "^1.0.7", "@xstate/fsm": "^2.1.0", + "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", "framer-motion": "^11.0.3", "graphql": "^16.0.0", @@ -31,6 +32,7 @@ "react-json-tree": "^0.18.0", "react-resizable-panels": "^1.0.0", "react-syntax-highlighter": "^15.5.0", + "tailwind-merge": "^2.3.0", "zen-observable": "^0.10.0" }, "devDependencies": { @@ -11924,6 +11926,25 @@ "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, + "node_modules/class-variance-authority": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.0.tgz", + "integrity": "sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==", + "dependencies": { + "clsx": "2.0.0" + }, + "funding": { + "url": "https://joebell.co.uk" + } + }, + "node_modules/class-variance-authority/node_modules/clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -24404,6 +24425,34 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "node_modules/tailwind-merge": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.3.0.tgz", + "integrity": "sha512-vkYrLpIP+lgR0tQCG6AP7zZXCTLc1Lnv/CCRT3BqJ9CZ3ui2++GPaGb1x/ILsINIMSYqqvrpqjUFsMNLlW99EA==", + "dependencies": { + "@babel/runtime": "^7.24.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwind-merge/node_modules/@babel/runtime": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", + "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tailwind-merge/node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, "node_modules/tailwindcss": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz", diff --git a/package.json b/package.json index 25e568f2f..9c328c63e 100755 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "@radix-ui/react-tabs": "^1.0.2", "@radix-ui/react-tooltip": "^1.0.7", "@xstate/fsm": "^2.1.0", + "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", "framer-motion": "^11.0.3", "graphql": "^16.0.0", @@ -58,6 +59,7 @@ "react-json-tree": "^0.18.0", "react-resizable-panels": "^1.0.0", "react-syntax-highlighter": "^15.5.0", + "tailwind-merge": "^2.3.0", "zen-observable": "^0.10.0" }, "devDependencies": { diff --git a/src/application/App.tsx b/src/application/App.tsx index 55fceccc1..37bc1f505 100644 --- a/src/application/App.tsx +++ b/src/application/App.tsx @@ -65,7 +65,7 @@ const ALERT_CONFIGS = { + icon={} + /> diff --git a/src/application/components/Button.tsx b/src/application/components/Button.tsx index d19b944f8..dfbe5c803 100644 --- a/src/application/components/Button.tsx +++ b/src/application/components/Button.tsx @@ -1,29 +1,118 @@ -import type { ComponentPropsWithoutRef, ElementType } from "react"; -import { forwardRef } from "react"; -import { clsx } from "clsx"; +import type { ComponentPropsWithoutRef, ReactElement } from "react"; +import { cloneElement, forwardRef, isValidElement } from "react"; import { Slot, Slottable } from "@radix-ui/react-slot"; import type { AsChildProps } from "../types/props"; +import { cva, type VariantProps } from "class-variance-authority"; +import { twMerge } from "tailwind-merge"; +import type { OmitNull } from "../types/utils"; type NativeButtonProps = ComponentPropsWithoutRef<"button">; -type ButtonSize = "md" | "sm" | "xs"; +type ButtonProps = AsChildProps & + Variants & { + className?: string; + icon?: ReactElement<{ "aria-hidden": boolean; className?: string }>; + }; -type ButtonProps = AsChildProps & { - className?: string; - icon?: ElementType; - variant: "primary" | "secondary" | "hidden"; - size: ButtonSize; -}; +type Variants = OmitNull>>; -const ICON_SIZES = { - xs: "w-3", - sm: "w-4", - md: "w-4", -} satisfies Record; +const button = cva( + [ + "flex", + "items-center", + "flex", + "gap-2", + "outline-none", + "focus:ring-3", + "focus:ring-offset-3", + "focus:ring-offset-primary", + "focus:dark:ring-offset-primary-dark", + "focus:ring-focused", + "focus:dark:ring-focused-dark", + "disabled:bg-button-disabled", + "disabled:dark:bg-button-disabled-dark", + "disabled:text-disabled", + "disabled:dark:text-disabled-dark", + "disabled:cursor-not-allowed", + "transition-colors", + "duration-200", + ], + { + variants: { + variant: { + hidden: [ + "text-primary", + "dark:text-primary-dark", + "hover:bg-button-secondaryHover", + "hover:dark:bg-button-secondaryHover-dark", + "active:bg-button-secondarySelected", + "active:dark:bg-button-secondarySelected-dark", + ], + primary: [ + "text-white", + "bg-button-primary", + "dark:bg-button-primary-dark", + "hover:bg-button-primaryHover", + "hover:dark:bg-button-primaryHover-dark", + "active:bg-selected", + "active:dark:bg-selected-dark", + ], + secondary: [ + "text-primary", + "dark:text-primary-dark", + "border", + "bg-button-secondary", + "dark:bg-button-secondary-dark", + "border-primary", + "dark:border-primary-dark", + "hover:bg-button-secondaryHover", + "hover:dark:bg-button-secondaryHover-dark", + "active:bg-selected", + "active:dark:bg-selected-dark", + ], + }, + size: { + xs: [ + "p-2", + "rounded", + "text-sm", + "font-semibold", + "has-[>svg:only-child]:p-1.5", + ], + sm: [ + "py-2", + "px-3", + "rounded", + "text-sm", + "font-semibold", + "has-[>svg:only-child]:p-2", + ], + md: [ + "py-2", + "px-3", + "rounded-lg", + "text-md", + "font-semibold", + "has-[>svg:only-child]:p-3", + ], + }, + }, + } +); + +const iconSize = cva([], { + variants: { + size: { + xs: ["w-3"], + sm: ["w-4"], + md: ["w-4"], + }, + }, +}); export const Button = forwardRef( function Button( - { asChild, className, children, variant, size, icon: Icon, ...props }, + { asChild, className, children, variant, size, icon, ...props }, ref ) { const Component = asChild ? Slot : "button"; @@ -32,25 +121,13 @@ export const Button = forwardRef( - {Icon && } + {isValidElement(icon) && + cloneElement(icon, { + "aria-hidden": true, + className: twMerge(iconSize({ size }), icon.props.className), + })} {children} ); diff --git a/src/application/components/Cache/sidebar/EntityList.tsx b/src/application/components/Cache/sidebar/EntityList.tsx index 644b36695..14378cd02 100644 --- a/src/application/components/Cache/sidebar/EntityList.tsx +++ b/src/application/components/Cache/sidebar/EntityList.tsx @@ -28,7 +28,7 @@ export function EntityList({ key={cacheId} onClick={() => setCacheId(cacheId)} selected={cacheId === selectedCacheId} - className="font-code h-8 text-sm" + className="font-code" > {searchTerm ? ( diff --git a/src/application/components/CopyButton.tsx b/src/application/components/CopyButton.tsx index f2f0425b2..6079b7fa3 100644 --- a/src/application/components/CopyButton.tsx +++ b/src/application/components/CopyButton.tsx @@ -1,5 +1,4 @@ import type { ComponentPropsWithoutRef } from "react"; -import clsx from "clsx"; import CopyToClipboard from "react-copy-to-clipboard"; import { Button } from "./Button"; import IconCopy from "@apollo/icons/default/IconCopy.svg"; @@ -20,14 +19,14 @@ export function CopyButton({ }: CopyButtonProps) { return ( - + + ); } diff --git a/src/application/components/List.tsx b/src/application/components/List.tsx index b75ccc0c2..db8d0e15b 100644 --- a/src/application/components/List.tsx +++ b/src/application/components/List.tsx @@ -1,8 +1,16 @@ import type { ComponentPropsWithoutRef } from "react"; import clsx from "clsx"; -type ListProps = ComponentPropsWithoutRef<"div">; +type ListProps = ComponentPropsWithoutRef<"ul">; export function List({ className, ...props }: ListProps) { - return
; + return ( +
    + ); } diff --git a/src/application/components/ListItem.tsx b/src/application/components/ListItem.tsx index 5282acd00..947af83e7 100644 --- a/src/application/components/ListItem.tsx +++ b/src/application/components/ListItem.tsx @@ -1,11 +1,11 @@ import type { ReactNode, ComponentPropsWithoutRef } from "react"; -import { clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; interface ListItemProps { className?: string; children?: ReactNode; selected?: boolean; - onClick?: ComponentPropsWithoutRef<"div">["onClick"]; + onClick?: ComponentPropsWithoutRef<"li">["onClick"]; } export function ListItem({ @@ -15,20 +15,29 @@ export function ListItem({ onClick, }: ListItemProps) { return ( -
    { + if (e.key === "Enter" || e.key === " ") { + e.currentTarget.click(); + } + }} >
    {children}
    -
    + ); } diff --git a/src/application/components/Modal/Body.tsx b/src/application/components/Modal/Body.tsx index c20dfe99e..94fdc2576 100644 --- a/src/application/components/Modal/Body.tsx +++ b/src/application/components/Modal/Body.tsx @@ -8,7 +8,12 @@ interface BodyProps { export function Body({ className, children }: BodyProps) { return ( -
    +
    {children}
    ); diff --git a/src/application/components/Modal/Footer.tsx b/src/application/components/Modal/Footer.tsx index 9adfd6190..550640406 100644 --- a/src/application/components/Modal/Footer.tsx +++ b/src/application/components/Modal/Footer.tsx @@ -11,7 +11,7 @@ export function Footer({ className, children }: FooterProps) {