Skip to content

Commit

Permalink
Merge pull request #655 from curvefi/feat/mui-chip
Browse files Browse the repository at this point in the history
feat: chips and badges
  • Loading branch information
DanielSchiavini authored Feb 4, 2025
2 parents 2bcc257 + d120d50 commit 2954796
Show file tree
Hide file tree
Showing 8 changed files with 272 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,29 +39,28 @@ type RateCellProps = {
export const LineGraphCell = ({ market, type }: RateCellProps) => {
const { snapshots, snapshotKey, isLoading, rate } = useSnapshots(market, type)
const { design } = useTheme()
if (rate == null) {
return '-'
}
return (
<Box data-testid={`line-graph-${type}`}>
{snapshots?.length ? (
<LineChart data={snapshots} {...graphSize} compact>
<YAxis hide type="number" domain={calculateDomain(snapshots[0][snapshotKey] as number)} />
<Line
type="monotone"
dataKey={snapshotKey}
stroke={getColor(design, snapshots, snapshotKey)}
strokeWidth={1}
dot={<></>}
/>
</LineChart>
) : isLoading ? (
<Skeleton {...graphSize} />
) : (
<Typography sx={{ ...graphSize, alignContent: 'center', textAlign: 'left' }} variant="bodyXsBold">
{t`No historical data`}
</Typography>
)}
</Box>
rate != null && (
<Box data-testid={`line-graph-${type}`}>
{snapshots?.length ? (
<LineChart data={snapshots} {...graphSize} compact>
<YAxis hide type="number" domain={calculateDomain(snapshots[0][snapshotKey] as number)} />
<Line
type="monotone"
dataKey={snapshotKey}
stroke={getColor(design, snapshots, snapshotKey)}
strokeWidth={1}
dot={<></>}
/>
</LineChart>
) : isLoading ? (
<Skeleton {...graphSize} />
) : (
<Typography sx={{ ...graphSize, alignContent: 'center', textAlign: 'left' }} variant="bodyXsBold">
{t`No historical data`}
</Typography>
)}
</Box>
)
)
}
21 changes: 21 additions & 0 deletions packages/curve-ui-kit/src/themes/chip/mui-chip-overrides.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import '@mui/material/Chip'

export type ChipColors = 'active' | 'alert' | 'default' | 'highlight' | 'warning' | 'accent' | 'selected' | 'unselected'
type DisabledChipColors = 'default' | 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning'

type NewChipColorOverrides = { [key in ChipColors]: true }
type DisabledChipColorsOverrides = { [key in DisabledChipColors]: false }

type ChipSizes = 'extraSmall' | 'small' | 'medium' | 'large' | 'extraLarge'
type ChipSizeOverrides = { [key in ChipSizes]: true }

declare module '@mui/material/Chip' {
export interface ChipPropsColorOverrides extends NewChipColorOverrides, DisabledChipColorsOverrides {}

export interface ChipPropsSizeOverrides extends ChipSizeOverrides {}

export interface ChipPropsVariantOverrides {
filled: false
outlined: false
}
}
131 changes: 131 additions & 0 deletions packages/curve-ui-kit/src/themes/chip/mui-chip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import type { Components } from '@mui/material'
import type { ChipProps } from '@mui/material/Chip/Chip'
import { DesignSystem } from '@ui-kit/themes/design'
import { Grays } from '@ui-kit/themes/design/0_primitives'
import { TypographyVariantKey } from '@ui-kit/themes/typography'
import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces'
import { handleBreakpoints, Responsive } from '@ui-kit/themes/basic-theme'
import type { TypographyOptions } from '@mui/material/styles/createTypography'

// note: the design system is using inverted themes for this color, there is no semantic colors for the clickable chips.
const invertPrimary = (color: DesignSystem['Color']) => color.Neutral[50]

const { Sizing, Spacing, IconSize } = SizesAndSpaces

type ChipSizeDefinition = { font: TypographyVariantKey; height: Responsive }

type ChipSizes = NonNullable<ChipProps['size']>

const chipSizes: Record<ChipSizes, ChipSizeDefinition> = {
extraSmall: { font: 'bodyXsBold', height: IconSize.md },
small: { font: 'buttonXs', height: IconSize.md },
medium: { font: 'buttonXs', height: Sizing.md },
large: { font: 'buttonM', height: Sizing.md },
extraLarge: { font: 'headingSBold', height: Sizing.xl },
}

// overrides for clickable chips
const chipSizeClickable: Record<ChipSizes, Partial<ChipSizeDefinition>> = {
extraSmall: {},
small: { height: Sizing.md },
medium: { font: 'buttonS' },
large: { height: Sizing.lg, font: 'buttonS' },
extraLarge: { height: Sizing.xl },
}

/**
* Defines the MuiChip component.
* In Figma we have two different components "Badge" and "Chip" that are implemented here.
* - Figma's Badge component is the non-clickable MuiChip. MuiBadge is attached to another component, so it cannot be used.
* - As we share colors between components, Figma's Chip active color is implemented as "selected" color. inactive is "unselected"
* - We do not use the "variant" prop (at the time of writing).
*/
export const defineMuiChip = (
{ Chips, Color, Text: { TextColors }, Layer }: DesignSystem,
typography: TypographyOptions,
): Components['MuiChip'] => ({
styleOverrides: {
root: {
borderStyle: 'solid',
borderWidth: 1,
borderColor: 'transparent',
borderRadius: Chips.BorderRadius.NonClickable,
color: TextColors.Primary,
backgroundColor: 'transparent',
...handleBreakpoints({ paddingInline: Spacing.xs }),
},
},
variants: [
// 'badge' colors not in design system but defined directly in components
{
props: { color: 'alert' },
style: { backgroundColor: Layer.Feedback.Error, color: Grays[50] },
},
{
props: { color: 'default' },
style: { borderColor: Layer[1].Outline },
},

{ props: { color: 'active' }, style: { backgroundColor: Color.Secondary[400], color: invertPrimary(Color) } },
{ props: { color: 'warning' }, style: { backgroundColor: Color.Tertiary[200] } },
{ props: { color: 'accent' }, style: { backgroundColor: Layer.Highlight.Fill, color: invertPrimary(Color) } },
{
props: { color: 'highlight' },
style: {
borderColor: Layer.Highlight.Outline,
color: TextColors.Highlight,
backgroundColor: invertPrimary(Color),
},
},

// chip colors taken from design system variables
{
props: { color: 'selected' },
style: {
backgroundColor: Chips.Current.Fill,
color: Chips.Current.Label,
borderColor: Chips.Current.Outline,
'& .MuiChip-deleteIcon': {
fill: Chips.Current.Label,
},
},
},
{
props: { color: 'unselected' },
style: {
color: Chips.Default.Label,
backgroundColor: Chips.Default.Fill,
borderColor: Chips.Default.Stroke,
'& .MuiChip-deleteIcon': {
fill: Chips.Default.Label,
},
},
},

// 'clickable' is the "Chip" in the design system
{
props: { clickable: true },
style: {
borderRadius: Chips.BorderRadius.Clickable,
cursor: 'pointer',
'&:has(.MuiChip-icon)': { ...handleBreakpoints({ paddingInline: Spacing.sm }) },
'&:hover': {
backgroundColor: Chips.Hover.Fill,
color: Chips.Hover.Label,
'& .MuiChip-deleteIcon': {
fill: Chips.Hover.Label,
},
},
},
},

...Object.entries(chipSizes).map(([size, { font, ...rest }]) => ({
props: { size: size as ChipSizes },
style: handleBreakpoints({ ...typography[font], ...rest }),
})),
...Object.entries(chipSizeClickable).map(([size, { font, ...rest }]) => ({
props: { size: size as ChipSizes, clickable: true },
style: handleBreakpoints({ ...(font && typography[font]), ...rest }),
})),
],
})
2 changes: 2 additions & 0 deletions packages/curve-ui-kit/src/themes/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { defineMuiMenuItem } from '@ui-kit/themes/mui-menu-item'
import { defineMuiAlert, defineMuiAlertTitle } from '@ui-kit/themes/mui-alert'
import type { TypographyOptions } from '@mui/material/styles/createTypography'
import { TransitionFunction } from '@ui-kit/themes/design/0_primitives'
import { defineMuiChip } from '@ui-kit/themes/chip/mui-chip'

export const DEFAULT_BAR_SIZE = SizesAndSpaces.ButtonSize.sm
export const MOBILE_SIDEBAR_WIDTH = { width: '100%', minWidth: 320 } as const
Expand Down Expand Up @@ -44,6 +45,7 @@ export const createComponents = (design: DesignSystem, typography: TypographyOpt
},
},
},
MuiChip: defineMuiChip(design, typography),
MuiContainer: {
styleOverrides: { root: { display: 'flex', maxWidth: 'var(--width)' } },
},
Expand Down
12 changes: 12 additions & 0 deletions packages/curve-ui-kit/src/themes/design/2_theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@ export const createLightDesign = (Light: typeof plain.Light | typeof inverted.Li
} as const

const Chips = {
BorderRadius: {
Clickable: 0,
NonClickable: '100px',
},
Default: {
Label: Text.TextColors.Secondary,
Fill: Layer[2].Fill,
Expand Down Expand Up @@ -532,6 +536,10 @@ export const createDarkDesign = (Dark: typeof plain.Dark | typeof inverted.Dark)
} as const

const Chips = {
BorderRadius: {
Clickable: 0,
NonClickable: '100px',
},
Default: {
Label: Text.TextColors.Secondary,
Fill: Layer[2].Fill,
Expand Down Expand Up @@ -812,6 +820,10 @@ export const createChadDesign = (Chad: typeof plain.Chad | typeof inverted.Chad)
} as const

const Chips = {
BorderRadius: {
Clickable: 0,
NonClickable: 0,
},
Default: {
Label: Text.TextColors.Secondary,
Fill: Layer[1].Fill,
Expand Down
2 changes: 1 addition & 1 deletion packages/curve-ui-kit/src/themes/generate-themes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { basicMuiTheme, type ThemeKey } from './basic-theme'
import { createPalette } from './palette'
import { createTypography } from './typography'
import { createComponents } from './components'
import { DesignSystem, DesignOptions } from './design'
import { DesignOptions, DesignSystem } from './design'

const paletteMode = (theme: ThemeKey, options: DesignOptions) =>
options.inverted ? (theme == 'dark' ? 'light' : 'dark') : theme == 'chad' ? 'light' : theme
Expand Down
2 changes: 1 addition & 1 deletion packages/curve-ui-kit/src/themes/mui-alert.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Components } from '@mui/material/styles'
import type { Components } from '@mui/material'
import { DesignSystem } from './design'
import { CheckIcon } from '@ui-kit/shared/icons/CheckIcon'
import { InfoCircledIcon } from '@ui-kit/shared/icons/InfoCircledIcon'
Expand Down
82 changes: 82 additions & 0 deletions packages/curve-ui-kit/src/themes/stories/Chip.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import CheckIcon from '@mui/icons-material/Check'
import type { Meta, StoryObj } from '@storybook/react'
import Chip from '@mui/material/Chip'
import Stack from '@mui/material/Stack'
import Typography from '@mui/material/Typography'
import Box from '@mui/material/Box'
import { fn } from '@storybook/test'
import type { ChipProps } from '@mui/material/Chip'

type ChipStoryProps = {
color: ChipProps['color']
clickable: boolean
variant?: ChipProps['variant']
}

const sizes = ['extraSmall', 'small', 'medium', 'large', 'extraLarge'] satisfies ChipProps['size'][]

const ChipStories = ({ clickable, color, variant }: ChipStoryProps) => (
<Stack spacing={7} flexGrow={0} marginBlock={9}>
<Typography variant="headingXxl">
{clickable ? 'Chips' : 'Badges'}: {color} color
</Typography>
{sizes.map((size) => (
<Box key={size} display="flex" flexDirection="row" gap={5} justifyContent="space-evenly">
{/* simple one */}
<Chip label={size} size={size} color={color} clickable={clickable} variant={variant} />

{/* with icon */}
<Chip label={size} size={size} color={color} icon={<CheckIcon />} clickable={clickable} variant={variant} />

{/* with icon and delete icon only for clickable chips */}
{clickable && (
<Chip
label={size}
size={size}
color={color}
icon={<CheckIcon />}
clickable
onDelete={fn()}
variant={variant}
/>
)}
</Box>
))}
</Stack>
)

const meta: Meta<typeof ChipStories> = {
title: 'UI Kit/Primitives/Chips and loose badges',
component: ChipStories,
argTypes: {
clickable: {
control: 'boolean',
description: 'Whether the chip is clickable',
},
color: {
control: 'select',
options: ['alert', 'default', 'active', 'highlight', 'warning', 'accent', 'selected', 'unselected'],
description: 'The color of the component',
},
variant: {
control: 'select',
options: ['filled', 'outlined'],
description: 'The variant of the component (not used)',
},
},
args: {
clickable: false,
},
}

export const AlertBadge: StoryObj<typeof ChipStories> = { args: { color: 'alert' } }
export const DefaultBadge: StoryObj<typeof ChipStories> = { args: { color: 'default' } }
export const ActiveBadge: StoryObj<typeof ChipStories> = { args: { color: 'active' } }
export const HighlightBadge: StoryObj<typeof ChipStories> = { args: { color: 'highlight' } }
export const WarningBadge: StoryObj<typeof ChipStories> = { args: { color: 'warning' } }
export const AccentBadge: StoryObj<typeof ChipStories> = { args: { color: 'accent' } }

export const UnselectedChip: StoryObj<typeof ChipStories> = { args: { clickable: true, color: 'unselected' } }
export const SelectedChip: StoryObj<typeof ChipStories> = { args: { clickable: true, color: 'selected' } }

export default meta

0 comments on commit 2954796

Please sign in to comment.