diff --git a/.changeset/shy-parents-sparkle.md b/.changeset/shy-parents-sparkle.md new file mode 100644 index 00000000..3a7de3e5 --- /dev/null +++ b/.changeset/shy-parents-sparkle.md @@ -0,0 +1,5 @@ +--- +'@status-im/components': minor +--- + +add Step and Tabs with Step diff --git a/packages/components/src/index.tsx b/packages/components/src/index.tsx index 701ab3ff..27a89501 100644 --- a/packages/components/src/index.tsx +++ b/packages/components/src/index.tsx @@ -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' diff --git a/packages/components/src/messages/components/reactions-dialog.tsx b/packages/components/src/messages/components/reactions-dialog.tsx index 80678ae5..dfc76805 100644 --- a/packages/components/src/messages/components/reactions-dialog.tsx +++ b/packages/components/src/messages/components/reactions-dialog.tsx @@ -68,6 +68,7 @@ export const ReactionsDialog = (props: Props) => { const Icon = REACTIONS_ICONS[reaction as keyof ReactionsType] return ( } diff --git a/packages/components/src/step/index.tsx b/packages/components/src/step/index.tsx new file mode 100644 index 00000000..fdbeb14f --- /dev/null +++ b/packages/components/src/step/index.tsx @@ -0,0 +1 @@ +export { Step, type StepProps } from './step' diff --git a/packages/components/src/step/step.stories.tsx b/packages/components/src/step/step.stories.tsx new file mode 100644 index 00000000..63c8e429 --- /dev/null +++ b/packages/components/src/step/step.stories.tsx @@ -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 = { + 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 + +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: () => ( + + + + + + + + + + + + + + + + + + ), +} + +export default meta diff --git a/packages/components/src/step/step.tsx b/packages/components/src/step/step.tsx new file mode 100644 index 00000000..c777a26c --- /dev/null +++ b/packages/components/src/step/step.tsx @@ -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 ( + + + + {value} + + + + ) +} + +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, ColorTokens> = { + neutral: '$neutral-100', + complete: '$white-100', + active: '$neutral-100', +} diff --git a/packages/components/src/tabs/tabs.stories.tsx b/packages/components/src/tabs/tabs.stories.tsx index 8c7e274b..de12a3ff 100644 --- a/packages/components/src/tabs/tabs.stories.tsx +++ b/packages/components/src/tabs/tabs.stories.tsx @@ -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 = { component: Tabs, @@ -18,40 +18,129 @@ const meta: Meta = { }, } -type Story = StoryObj<{ size: 24 | 32; icon: boolean; count: boolean }> - -export const Default: Story = { - name: 'Default', +export const Default: StoryObj = { args: { - size: 24, - icon: false, - }, - argTypes: { - size: { - control: 'select', - options: [24, 32] satisfies ComponentProps['size'][], - }, - icon: { - control: 'boolean', - }, - count: { - control: 'boolean', - }, + defaultValue: '1', }, render(args) { - const icon = args.icon ? : undefined - const count = args.count ? 8 : undefined - return ( - - - + + + Tab 1 - + Tab 2 - + + Tab 3 + + + + Content 1 + + + Content 2 + + + Content 3 + + + ) + }, +} + +export const Icon: StoryObj = { + args: { + defaultValue: '1', + }, + render(args) { + return ( + + + } + > + Tab 1 + + } + > + Tab 2 + + } + > + Tab 3 + + + + Content 1 + + + Content 2 + + + Content 3 + + + ) + }, +} + +export const Counter: StoryObj = { + args: { + defaultValue: '1', + }, + render(args) { + return ( + + + + Tab 1 + + + Tab 2 + + + Tab 3 + + + + Content 1 + + + Content 2 + + + Content 3 + + + ) + }, +} + +export const Step: StoryObj = { + args: { + defaultValue: '1', + }, + render(args) { + return ( + + + + Tab 1 + + + Tab 2 + + Tab 3 diff --git a/packages/components/src/tabs/tabs.tsx b/packages/components/src/tabs/tabs.tsx index a470c5bf..b4c817a6 100644 --- a/packages/components/src/tabs/tabs.tsx +++ b/packages/components/src/tabs/tabs.tsx @@ -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 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) => { - 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) => { size={size} active={selected} > - {icon && cloneElement(icon, { size: iconSizes[size] })} + {props.type === 'icon' && + cloneElement(props.icon, { size: iconSizes[size] })} + + {props.type === 'step' && } + {children} - {count && ( + + {props.type === 'counter' && ( - + )}