From b6d68f21364a8f14436b1212b1dda895a9cfa5fc Mon Sep 17 00:00:00 2001 From: Jesse Pinho Date: Fri, 16 Aug 2024 19:21:23 -0700 Subject: [PATCH] Fix a number of issues that came up in final review --- packages/ui/.storybook/preview.jsx | 5 +++ packages/ui/README.md | 6 +++ .../AccountSelectorAddress.tsx | 37 ++++++++++--------- packages/ui/src/Button/index.tsx | 2 + packages/ui/src/Dialog/index.tsx | 22 +++++++++++ packages/ui/src/FormField/index.tsx | 4 +- packages/ui/src/Pill/index.tsx | 1 + packages/ui/src/Text/index.stories.tsx | 2 + packages/ui/src/TextInput/index.stories.tsx | 12 ++++-- 9 files changed, 68 insertions(+), 23 deletions(-) diff --git a/packages/ui/.storybook/preview.jsx b/packages/ui/.storybook/preview.jsx index bd210b1012..b17745781c 100644 --- a/packages/ui/.storybook/preview.jsx +++ b/packages/ui/.storybook/preview.jsx @@ -68,6 +68,11 @@ const preview = { ); }, ], + argTypes: { + // The `motion` prop is used throughout many Penumbra UI components for + // framer-motion settings, and shouldn't be controlled in Storybook. + motion: { control: false }, + }, parameters: { actions: { argTypesRegex: '^on[A-Z].*' }, controls: { diff --git a/packages/ui/README.md b/packages/ui/README.md index 9312427278..0cef87da66 100644 --- a/packages/ui/README.md +++ b/packages/ui/README.md @@ -2,6 +2,12 @@ The Penumbra UI library is a set of UI components purpose-built for the Penumbra ecosystem. Use these components to get rendering of various Penumbra data types out of the box, and to create a UI that is consistent with other Penumbra UIs' look and feel. +## Storybook + +All Penumbra UI components (except some deprecated ones) in the latest tagged release can be found at the Penumbra UI Storybook site: https://ui.penumbra.zone/ + +To view the latest components merged to `main` (even if they are not yet in a tagged release), check out the Storybook Preview site: https://preview.ui.penumbra.zone/ + ## Set up First, install the library: diff --git a/packages/ui/src/AccountSelector/AccountSelectorAddress.tsx b/packages/ui/src/AccountSelector/AccountSelectorAddress.tsx index 7d59b7e02b..5cc34bf4c7 100644 --- a/packages/ui/src/AccountSelector/AccountSelectorAddress.tsx +++ b/packages/ui/src/AccountSelector/AccountSelectorAddress.tsx @@ -1,37 +1,34 @@ import { Address } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/keys/v1/keys_pb'; import { bech32mAddress } from '@penumbra-zone/bech32m/penumbra'; -import styled from 'styled-components'; +import styled, { DefaultTheme } from 'styled-components'; import { useDensity } from '../hooks/useDensity'; import { Density } from '../types/Density'; import { CopyToClipboardButton } from '../CopyToClipboardButton'; import { Shrink0 } from '../utils/Shrink0'; -import { technical, truncate } from '../utils/typography'; import { useAnimationDeferredValue } from '../hooks/useAnimationDeferredValue'; +import { Text } from '../Text'; -const Root = styled.div<{ $ephemeral: boolean; $loading: boolean; $density: Density }>` +const Root = styled.div<{ $density: Density }>` border: 1px solid ${props => props.theme.color.other.tonalStroke}; padding: ${props => props.theme.spacing(2)} ${props => props.theme.spacing(3)}; display: flex; gap: ${props => props.theme.spacing(2)}; - color: ${props => - props.$loading - ? props.theme.color.text.muted - : props.$ephemeral - ? props.theme.color.text.special - : props.theme.color.text.primary}; - ${props => props.$density === 'sparse' && 'word-break: break-all;'} `; -const TextWrapper = styled.div<{ $density: Density }>` +const TextWrapper = styled.div` flex-grow: 1; - - ${props => props.$density === 'compact' && truncate} - ${technical} `; +const getAddressColor = (loading: boolean, ephemeral: boolean) => (theme: DefaultTheme) => + loading + ? theme.color.text.muted + : ephemeral + ? theme.color.text.special + : theme.color.text.primary; + export interface AccountSelectorAddressProps { address?: Address; ephemeral: boolean; @@ -47,9 +44,15 @@ export const AccountSelectorAddress = ({ const deferredAddress = useAnimationDeferredValue(address); return ( - - - {deferredAddress ? bech32mAddress(deferredAddress) : `penumbra1...`} + + + + {deferredAddress ? bech32mAddress(deferredAddress) : `penumbra1...`} + diff --git a/packages/ui/src/Button/index.tsx b/packages/ui/src/Button/index.tsx index 246cbae2c0..ca295e57db 100644 --- a/packages/ui/src/Button/index.tsx +++ b/packages/ui/src/Button/index.tsx @@ -14,6 +14,7 @@ import { motion } from 'framer-motion'; const iconOnlyAdornment = css` border-radius: ${props => props.theme.borderRadius.full}; padding: ${props => props.theme.spacing(1)}; + width: max-content; `; const sparse = css` @@ -30,6 +31,7 @@ const compact = css` padding-right: ${props => props.theme.spacing(props.$iconOnly ? 2 : 4)}; height: 32px; min-width: 32px; + width: max-content; `; const outlineColorByActionType: Record = { diff --git a/packages/ui/src/Dialog/index.tsx b/packages/ui/src/Dialog/index.tsx index b3243ada50..26e01f70ed 100644 --- a/packages/ui/src/Dialog/index.tsx +++ b/packages/ui/src/Dialog/index.tsx @@ -172,6 +172,28 @@ export type DialogProps = { * Dialog content here * * ``` + * + * ## Animating a dialog out of its trigger + * + * You can use the `motion` prop with a layout ID to make a dialog appear to + * animate out of the trigger button: + * + * ```tsx + * const layoutId = useId(); + * + * return ( + * + * + * + * + * + * ... + * + * + * ); + * ``` */ export const Dialog = ({ children, onClose, isOpen }: DialogProps) => { const isControlledComponent = isOpen !== undefined; diff --git a/packages/ui/src/FormField/index.tsx b/packages/ui/src/FormField/index.tsx index a2cce50854..0f444b9f85 100644 --- a/packages/ui/src/FormField/index.tsx +++ b/packages/ui/src/FormField/index.tsx @@ -11,10 +11,10 @@ const Root = styled.label` `; const HelperText = styled.div<{ $disabled: boolean }>` + ${small} + color: ${props => props.$disabled ? props.theme.color.text.muted : props.theme.color.text.secondary}; - - ${small} `; const LabelText = styled.div<{ $disabled: boolean }>` diff --git a/packages/ui/src/Pill/index.tsx b/packages/ui/src/Pill/index.tsx index cc1dcc6e2e..ef4873f4ee 100644 --- a/packages/ui/src/Pill/index.tsx +++ b/packages/ui/src/Pill/index.tsx @@ -20,6 +20,7 @@ const Root = styled.span<{ $density: Density; $priority: Priority }>` display: inline-block; max-width: 100%; + width: max-content; padding-top: ${props => props.theme.spacing(props.$density === 'sparse' ? 2 : 1)}; padding-bottom: ${props => props.theme.spacing(props.$density === 'sparse' ? 2 : 1)}; diff --git a/packages/ui/src/Text/index.stories.tsx b/packages/ui/src/Text/index.stories.tsx index 8ccb34c644..c830404c43 100644 --- a/packages/ui/src/Text/index.stories.tsx +++ b/packages/ui/src/Text/index.stories.tsx @@ -20,6 +20,7 @@ const meta: Meta = { detail: { control: false }, small: { control: false }, technical: { control: false }, + detailTechnical: { control: false }, as: { options: ['span', 'div', 'h1', 'h2', 'h3', 'h4', 'p', 'main', 'section'], @@ -47,6 +48,7 @@ const OPTIONS = [ 'detail', 'small', 'technical', + 'detailTechnical', ] as const; const Option = ({ diff --git a/packages/ui/src/TextInput/index.stories.tsx b/packages/ui/src/TextInput/index.stories.tsx index 25acad88df..7934890896 100644 --- a/packages/ui/src/TextInput/index.stories.tsx +++ b/packages/ui/src/TextInput/index.stories.tsx @@ -15,6 +15,10 @@ const SampleButton = () => ( ); +const addressBookIcon = ( + theme.color.text.primary} /> +); + const meta: Meta = { component: TextInput, tags: ['autodocs', '!dev'], @@ -22,7 +26,7 @@ const meta: Meta = { startAdornment: { options: ['Address book icon', 'None'], mapping: { - 'Address book icon': , + 'Address book icon': addressBookIcon, None: undefined, }, }, @@ -33,6 +37,8 @@ const meta: Meta = { None: undefined, }, }, + max: { control: false }, + min: { control: false }, }, }; export default meta; @@ -46,9 +52,7 @@ export const Basic: Story = { value: '', disabled: false, type: 'text', - startAdornment: ( - theme.color.text.primary} /> - ), + startAdornment: addressBookIcon, endAdornment: , },