* u tabs

* u stories

* a steps

* f tabs

* a changeset

* u tabs

* e step
This commit is contained in:
Felicio Mununga 2023-08-25 14:22:44 +02:00 committed by GitHub
parent 6c24833a91
commit 762a698be2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 306 additions and 38 deletions

View File

@ -0,0 +1,5 @@
---
'@status-im/components': minor
---
add Step and Tabs with Step

View File

@ -22,6 +22,7 @@ export * from './provider'
export * from './shadow'
export * from './shortcut'
export * from './skeleton'
export * from './step'
export * from './tabs'
export * from './tag'
export * from './text'

View File

@ -68,6 +68,7 @@ export const ReactionsDialog = (props: Props) => {
const Icon = REACTIONS_ICONS[reaction as keyof ReactionsType]
return (
<Tabs.Trigger
type="icon"
key={reaction}
value={reaction}
icon={<Icon size={20} />}

View File

@ -0,0 +1 @@
export { Step, type StepProps } from './step'

View File

@ -0,0 +1,78 @@
import { Stack } from '@tamagui/core'
import { Step } from './step'
import type { StepProps } from './step'
import type { Meta, StoryObj } from '@storybook/react'
const meta: Meta<typeof Step> = {
title: 'Components/Step',
component: Step,
argTypes: {
value: {
control: {
type: 'number',
min: 0,
max: 1000,
},
},
type: {
control: 'select',
options: ['neutral', 'complete', 'active'],
},
},
parameters: {
design: {
type: 'figma',
url: 'https://www.figma.com/file/IBmFKgGL1B4GzqD8LQTw6n/Design-System-for-Desktop%2FWeb?type=design&node-id=18126-5278&mode=design&t=QNu79iGJYnhdNqOn-4',
},
},
}
type Story = StoryObj<StepProps>
export const Neutral: Story = {
args: {
value: 1,
type: 'neutral',
},
}
export const Complete: Story = {
args: {
value: 1,
type: 'complete',
},
}
export const Active: Story = {
args: {
value: 1,
type: 'active',
},
}
export const AllVariants: Story = {
args: {},
render: () => (
<Stack space flexDirection="row">
<Stack space>
<Step type="neutral" value={1} />
<Step type="neutral" value={10} />
<Step type="neutral" value={999} />
</Stack>
<Stack space>
<Step type="complete" value={1} />
<Step type="complete" value={10} />
<Step type="complete" value={999} />
</Stack>
<Stack space>
<Step type="active" value={1} />
<Step type="active" value={10} />
<Step type="active" value={999} />
</Stack>
</Stack>
),
}
export default meta

View File

@ -0,0 +1,74 @@
import { Stack, styled } from '@tamagui/core'
import { Text } from '../text'
import type { ColorTokens } from '@tamagui/core'
// todo?: rename netural to default
export type StepVariants = 'neutral' | 'complete' | 'active'
type Props = {
value: number
type?: StepVariants
}
const Step = (props: Props) => {
const { value, type = 'neutral' } = props
return (
<Base>
<Content type={type}>
<Text size={11} weight="medium" color={textColors[type]}>
{value}
</Text>
</Content>
</Base>
)
}
export { Step }
export type { Props as StepProps }
const Base = styled(Stack, {
padding: 2,
display: 'inline-flex',
justifyContent: 'center',
alignItems: 'center',
flexBasis: 'fit-content',
})
const Content = styled(Stack, {
backgroundColor: '$white-100',
paddingHorizontal: 3,
paddingVertical: 0,
borderRadius: '$6',
minHeight: 18,
maxHeight: 18,
minWidth: 18,
maxWidth: 28,
justifyContent: 'center',
alignItems: 'center',
borderWidth: 1,
borderColor: 'transparent',
variants: {
type: {
neutral: {
backgroundColor: '$transparent',
borderColor: '$neutral-20',
},
complete: {
backgroundColor: '$blue-50',
},
active: {
backgroundColor: '$blue-50-opa-10',
},
},
},
})
const textColors: Record<NonNullable<Props['type']>, ColorTokens> = {
neutral: '$neutral-100',
complete: '$white-100',
active: '$neutral-100',
}

View File

@ -3,8 +3,8 @@ import { PlaceholderIcon } from '@status-im/icons'
import { Text } from '../text'
import { Tabs } from './tabs'
import type { TabsProps } from './tabs'
import type { Meta, StoryObj } from '@storybook/react'
import type { ComponentProps } from 'react'
const meta: Meta<typeof Tabs> = {
component: Tabs,
@ -18,40 +18,129 @@ const meta: Meta<typeof Tabs> = {
},
}
type Story = StoryObj<{ size: 24 | 32; icon: boolean; count: boolean }>
export const Default: Story = {
name: 'Default',
export const Default: StoryObj<TabsProps> = {
args: {
size: 24,
icon: false,
},
argTypes: {
size: {
control: 'select',
options: [24, 32] satisfies ComponentProps<typeof Tabs.List>['size'][],
},
icon: {
control: 'boolean',
},
count: {
control: 'boolean',
},
defaultValue: '1',
},
render(args) {
const icon = args.icon ? <PlaceholderIcon size={20} /> : undefined
const count = args.count ? 8 : undefined
return (
<Tabs defaultValue="1">
<Tabs.List size={args.size}>
<Tabs.Trigger value="1" icon={icon} count={count}>
<Tabs {...args}>
<Tabs.List size={32}>
<Tabs.Trigger type="default" value="1">
Tab 1
</Tabs.Trigger>
<Tabs.Trigger value="2" icon={icon}>
<Tabs.Trigger type="default" value="2">
Tab 2
</Tabs.Trigger>
<Tabs.Trigger value="3" icon={icon}>
<Tabs.Trigger type="default" value="3">
Tab 3
</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="1">
<Text size={15}>Content 1</Text>
</Tabs.Content>
<Tabs.Content value="2">
<Text size={15}>Content 2</Text>
</Tabs.Content>
<Tabs.Content value="3">
<Text size={15}>Content 3</Text>
</Tabs.Content>
</Tabs>
)
},
}
export const Icon: StoryObj<TabsProps> = {
args: {
defaultValue: '1',
},
render(args) {
return (
<Tabs {...args}>
<Tabs.List size={32}>
<Tabs.Trigger
type="icon"
value="1"
icon={<PlaceholderIcon size={16} />}
>
Tab 1
</Tabs.Trigger>
<Tabs.Trigger
type="icon"
value="2"
icon={<PlaceholderIcon size={16} />}
>
Tab 2
</Tabs.Trigger>
<Tabs.Trigger
type="icon"
value="3"
icon={<PlaceholderIcon size={16} />}
>
Tab 3
</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="1">
<Text size={15}>Content 1</Text>
</Tabs.Content>
<Tabs.Content value="2">
<Text size={15}>Content 2</Text>
</Tabs.Content>
<Tabs.Content value="3">
<Text size={15}>Content 3</Text>
</Tabs.Content>
</Tabs>
)
},
}
export const Counter: StoryObj<TabsProps> = {
args: {
defaultValue: '1',
},
render(args) {
return (
<Tabs {...args}>
<Tabs.List size={32}>
<Tabs.Trigger type="counter" value="1" count={5}>
Tab 1
</Tabs.Trigger>
<Tabs.Trigger type="counter" value="2" count={10}>
Tab 2
</Tabs.Trigger>
<Tabs.Trigger type="counter" value="3" count={100}>
Tab 3
</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="1">
<Text size={15}>Content 1</Text>
</Tabs.Content>
<Tabs.Content value="2">
<Text size={15}>Content 2</Text>
</Tabs.Content>
<Tabs.Content value="3">
<Text size={15}>Content 3</Text>
</Tabs.Content>
</Tabs>
)
},
}
export const Step: StoryObj<TabsProps> = {
args: {
defaultValue: '1',
},
render(args) {
return (
<Tabs {...args}>
<Tabs.List size={32}>
<Tabs.Trigger type="step" value="1" step={1}>
Tab 1
</Tabs.Trigger>
<Tabs.Trigger type="step" value="2" step={10}>
Tab 2
</Tabs.Trigger>
<Tabs.Trigger type="step" value="3" step={999}>
Tab 3
</Tabs.Trigger>
</Tabs.List>

View File

@ -7,6 +7,7 @@ import { styled } from 'tamagui'
import { Counter } from '../counter'
import { usePressableColors } from '../hooks/use-pressable-colors'
import { Step } from '../step'
import { Text } from '../text'
import type { TextProps } from '../text'
@ -16,6 +17,7 @@ import type { Ref } from 'react'
type Variants = GetVariants<typeof TriggerBase>
type Props = {
// type: TriggerProps['type']
children: React.ReactNode[]
defaultValue: string
value?: string
@ -57,16 +59,28 @@ const TabsList = (props: ListProps) => {
)
}
type TriggerProps = {
value: string
children: string
icon?: React.ReactElement
count?: number
}
type TriggerProps =
| {
type: 'default'
value: string
children: string
}
| {
type: 'icon'
value: string
children: string
icon: React.ReactElement
}
| { type: 'counter'; value: string; children: string; count: number }
| {
type: 'step'
value: string
children: string
step: number
}
// TODO: Add counter
const TabsTrigger = (props: TriggerProps, ref: Ref<View>) => {
const { icon = null, children, count, ...triggerProps } = props
const { children, ...triggerProps } = props
// props coming from parent List and Trigger, not passed by the user (line 52)
const providedProps = props as TriggerProps & {
@ -96,13 +110,18 @@ const TabsTrigger = (props: TriggerProps, ref: Ref<View>) => {
size={size}
active={selected}
>
{icon && cloneElement(icon, { size: iconSizes[size] })}
{props.type === 'icon' &&
cloneElement(props.icon, { size: iconSizes[size] })}
{props.type === 'step' && <Step type="complete" value={props.step} />}
<Text size={textSize} weight="medium" color={color}>
{children}
</Text>
{count && (
{props.type === 'counter' && (
<Stack marginRight={-4}>
<Counter type="secondary" value={count} />
<Counter type="secondary" value={props.count} />
</Stack>
)}
</TriggerBase>