Skip to content

Commit

Permalink
[PAY-3720][PAY-3718][PAY-3709] Add Harmony Components: NavItem, Balan…
Browse files Browse the repository at this point in the history
…cePill, and NotificationCount (#10793)
  • Loading branch information
faridsalau authored Dec 19, 2024
1 parent 513a06b commit 82d9259
Show file tree
Hide file tree
Showing 16 changed files with 669 additions and 0 deletions.
95 changes: 95 additions & 0 deletions packages/harmony/src/components/NavItem/NavItem.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import {
Meta,
Title,
Description,
Controls,
Canvas,
Unstyled
} from '@storybook/blocks'

import { RelatedFoundations } from 'storybook/components'

import { ThemeProvider } from '../../foundations/theme'
import { Flex } from '../layout/Flex'

import { NavItem } from './NavItem'
import * as NavItemStories from './NavItem.stories'

<Meta of={NavItemStories} />

<Title />

<Description of={NavItem} />

A navigation item component, typically used in sidebars or navigation menus. Supports default, hover, and selected states, as well as optional left and right icons.

## Usage

The `NavItem` component displays a navigation link with options for left and right icons and different states (default, hover, selected).

<Canvas of={NavItemStories.Default} />

## Props

<Controls />

## States

### Default

<Canvas of={NavItemStories.Default} />

### Selected

<Canvas of={NavItemStories.Selected} />

## Variations

### With Left Icon

<Canvas of={NavItemStories.WithLeftIcon} />

### With Right Icon

<Canvas of={NavItemStories.WithRightIcon} />

### With Both Icons

<Canvas of={NavItemStories.WithBothIcons} />

## Styling

The `NavItem` component uses the following foundational styles:

- **Color:** Text and icon colors change based on the state (default, hover, selected).
- **Spacing:** Uses `s` and `m` spacing values for padding and gaps.
- **Corner Radius:** Uses `m` corner radius for the rounded corners.
- **Background Color:** Changes based on the state:
- Default: transparent
- Hover: `surface2` (unless selected)
- Selected: `s400`
- **Border:** A border appears on hover with `border.default` color.
- **Typography:** Uses `title` variant with `l` size and `weak` strength.
- **Transitions:** Smooth background-color transition with `0.18s ease-in-out`.

## Themes

<Unstyled>
<Flex gap='m' wrap='wrap'>
<ThemeProvider theme='day'>
<NavItem leftIcon='UserFeed'>Label</NavItem>
</ThemeProvider>
<ThemeProvider theme='dark'>
<NavItem leftIcon='UserFeed'>Label</NavItem>
</ThemeProvider>
<ThemeProvider theme='matrix'>
<NavItem leftIcon='UserFeed'>Label</NavItem>
</ThemeProvider>
</Flex>
</Unstyled>

## Related Foundations

<RelatedFoundations
foundationNames={['Color', 'Spacing', 'CornerRadius', 'Typography']}
/>
56 changes: 56 additions & 0 deletions packages/harmony/src/components/NavItem/NavItem.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import type { Meta, StoryObj } from '@storybook/react'

import { IconFeed, IconVolumeLevel3 } from '../../icons'

import { NavItem } from './NavItem'

const meta: Meta<typeof NavItem> = {
title: 'Components/NavItem',
component: NavItem,
parameters: {
design: {
type: 'figma',
url: '' // Add your Figma URL here
}
},
tags: ['autodocs']
}

export default meta

type Story = StoryObj<typeof NavItem>

export const Default: Story = {
args: {
children: 'Label'
}
}

export const Selected: Story = {
args: {
children: 'Label',
isSelected: true
}
}

export const WithLeftIcon: Story = {
args: {
children: 'Label',
leftIcon: IconFeed
}
}

export const WithRightIcon: Story = {
args: {
children: 'Label',
rightIcon: IconVolumeLevel3
}
}

export const WithBothIcons: Story = {
args: {
children: 'Label',
leftIcon: IconFeed,
rightIcon: IconVolumeLevel3
}
}
86 changes: 86 additions & 0 deletions packages/harmony/src/components/NavItem/NavItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { useTheme } from '@emotion/react'

import { motion } from '../../foundations/motion'
import { Flex } from '../layout/Flex'
import { Text } from '../text/Text'

import type { NavItemProps } from './types'

/**
* A navigation item component, typically used in sidebars or navigation menus.
* Supports default, hover, and selected states, as well as optional left and right icons.
*/
export const NavItem = ({
children,
leftIcon: LeftIcon,
rightIcon: RightIcon,
isSelected = false,
onClick,
...props
}: NavItemProps) => {
const { color } = useTheme()

const hasLeftIcon = !!LeftIcon
const hasRightIcon = !!RightIcon

const backgroundColor = isSelected ? color.secondary.s400 : undefined

const textColor = isSelected ? 'staticWhite' : 'default'

const iconColor = isSelected ? 'staticStaticWhite' : 'default'

return (
<Flex
alignItems='center'
gap='s'
pl='s'
pr='s'
css={{
width: '240px',
cursor: 'pointer',
transition: `background-color ${motion.hover}`
}}
{...props}
onClick={onClick}
>
<Flex
alignItems='center'
flex={1}
gap='m'
p='s'
borderRadius='m'
css={{
backgroundColor,
borderWidth: '1px',

'&:hover': {
backgroundColor: isSelected ? undefined : color.background.surface2,
borderColor: color.border.default
}
}}
>
<Flex
alignItems='center'
gap='m'
flex={1}
css={{
maxWidth: '240px'
}}
>
{hasLeftIcon ? <LeftIcon size='l' color={iconColor} /> : null}
<Text
variant='title'
size='l'
strength='weak'
lineHeight='single'
color={textColor}
ellipses
>
{children}
</Text>
</Flex>
{hasRightIcon ? <RightIcon size='m' color={iconColor} /> : null}
</Flex>
</Flex>
)
}
2 changes: 2 additions & 0 deletions packages/harmony/src/components/NavItem/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './NavItem'
export * from './types'
16 changes: 16 additions & 0 deletions packages/harmony/src/components/NavItem/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { IconComponent } from 'components/icon'

import type { WithCSS } from '../../foundations/theme'

export type NavItemProps = WithCSS<{
/** The label text of the navigation item. */
children: React.ReactNode
/** The name of the icon to display on the left side of the label. */
leftIcon?: IconComponent
/** The name of the icon to display on the right side of the label. */
rightIcon?: IconComponent
/** Whether the navigation item is currently selected. */
isSelected?: boolean
/** The callback function to be called when the navigation item is clicked. */
onClick?: () => void
}>
59 changes: 59 additions & 0 deletions packages/harmony/src/components/balance-pill/BalancePill.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
Meta,
Title,
Description,
Controls,
Canvas,
Unstyled
} from '@storybook/blocks'

import { Heading, RelatedFoundations } from 'storybook/components'

import { ThemeProvider, themes } from '../../foundations/theme'
import { Flex } from '../layout/Flex'
import { Text } from '../text/Text'

import { BalancePill } from './BalancePill'
import * as BalancePillStories from './BalancePill.stories'

<Meta of={BalancePillStories} />

<Title />

<Description of={BalancePill} />

## Usage

The `BalancePill` component is designed to display a balance amount along with an optional icon or child component. It utilizes several foundational styles from the Harmony design system, including colors, spacing, and border-radius.

<Canvas of={BalancePillStories.Default} />

## Themes

<Unstyled>
<Flex gap='m' wrap='wrap'>
<ThemeProvider theme='day'>
<BalancePill balance='1,000'>
<Text>💰</Text>
</BalancePill>
</ThemeProvider>
<ThemeProvider theme='dark'>
<BalancePill balance='1,000'>
<Text>💰</Text>
</BalancePill>
</ThemeProvider>
<ThemeProvider theme='matrix'>
<BalancePill balance='1,000'>
<Text>💰</Text>
</BalancePill>
</ThemeProvider>
</Flex>
</Unstyled>

## Props

<Controls />

## Related Foundations

<RelatedFoundations foundationNames={['Color', 'Spacing', 'CornerRadius']} />
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { Meta, StoryObj } from '@storybook/react'

import { BalancePill } from './BalancePill'

const meta: Meta<typeof BalancePill> = {
title: 'Components/BalancePill',
component: BalancePill,
parameters: {
design: {
type: 'figma',
url: '' // Add your Figma URL here
}
},
tags: ['autodocs']
}

export default meta

type Story = StoryObj<typeof BalancePill>

export const Default: Story = {
args: {
balance: 125
}
}
52 changes: 52 additions & 0 deletions packages/harmony/src/components/balance-pill/BalancePill.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useTheme, CSSObject } from '@emotion/react'

import { Flex } from '../layout/Flex'
import { Text } from '../text/Text'

import type { BalancePillProps } from './types'

/**
* A pill-shaped component that displays a balance, typically used to show a user's token balance.
*/
export const BalancePill = ({
balance,
children,
...props
}: BalancePillProps) => {
const { color } = useTheme()

const textStyles: CSSObject = {
color: color.neutral.n950
}

return (
<Flex
pl='s'
alignItems='center'
gap='xs'
borderRadius='circle'
border='default'
backgroundColor='surface1'
{...props}
>
<Text
variant='label'
size='s'
strength='strong'
textAlign='center'
css={textStyles}
>
{balance}
</Text>
<Flex
w='unit6'
h='unit6'
p='unitHalf'
justifyContent='center'
alignItems='center'
>
{children}
</Flex>
</Flex>
)
}
Loading

0 comments on commit 82d9259

Please sign in to comment.