Skip to content

Commit

Permalink
Merge pull request #35 from igor-tech/QUIZ-14
Browse files Browse the repository at this point in the history
add Radio group
  • Loading branch information
igor-tech authored May 10, 2024
2 parents 7146acb + 51e12db commit d614713
Show file tree
Hide file tree
Showing 7 changed files with 317 additions and 0 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@fontsource/roboto": "^5.0.13",
"@hookform/resolvers": "^3.3.4",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-radio-group": "^1.1.3",
"clsx": "^2.1.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
115 changes: 115 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/components/ui/radio-group/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './radio-group'
83 changes: 83 additions & 0 deletions src/components/ui/radio-group/radio-group.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
@import 'src/styles/mixins';

.root {
display: inline-flex;
flex-direction: column;
row-gap: 12px;

.label {
cursor: pointer;

position: relative;

display: inline-flex;
column-gap: 12px;
align-items: center;

&.disabled {
cursor: auto;
user-select: none;
color: var(--color-light-900);
}

.item {
@include interactive-circle(--color-accent-900, --color-dark-500);

all: unset;

cursor: pointer;

position: relative;

display: inline-flex;
align-items: center;
justify-content: center;

box-sizing: border-box;

.frame {
z-index: 2;

width: 16px;
height: 16px;

background-color: transparent;
border: 2px solid var(--color-accent-500);
border-radius: 50%;
}

.indicator {
position: absolute;
z-index: 2;

width: 8px;
height: 8px;

background-color: var(--color-accent-500);
border-radius: 50%;
}

&:disabled {
cursor: auto;

&::before,
&::after {
width: 0;
height: 0;
}

.frame {
border-color: var(--color-accent-900);
}

.indicator {
background-color: var(--color-accent-900);
}
}
}
}

.error {
color: var(--color-danger-300);
}
}
57 changes: 57 additions & 0 deletions src/components/ui/radio-group/radio-group.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import type { Meta, StoryObj } from '@storybook/react'

import { useState } from 'react'

import { OptionType, RadioGroup } from '@/components/ui/radio-group/radio-group'

const meta = {
argTypes: {},
component: RadioGroup,
tags: ['autodocs'],
title: 'Components/UI/Radio Group',
} satisfies Meta<typeof RadioGroup>

export default meta
type Story = StoryObj<typeof meta>

const DATA: OptionType[] = [
{ label: 'Option 1', value: '1' },
{ label: 'Option 2', value: '2' },
{ label: 'Option 3', value: '3' },
]

export const Controlled: Story = {
args: {
options: DATA,
},

render: args => {
const [value, setValue] = useState('1')

return (
<>
<RadioGroup
{...args}
defaultValue={value}
onValueChange={value => setValue(value)}
value={value}
/>
</>
)
},
}

export const WithError: Story = {
args: {
errorMessage: 'some error message',
options: DATA,
value: '1',
},
}

export const Disabled: Story = {
args: {
disabled: true,
options: DATA,
},
}
54 changes: 54 additions & 0 deletions src/components/ui/radio-group/radio-group.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { ComponentPropsWithoutRef, ElementRef, FC, forwardRef } from 'react'

import { Typography } from '@/components/ui/typography'
import * as RadioGroupRadix from '@radix-ui/react-radio-group'
import { clsx } from 'clsx'

import styles from './radio-group.module.scss'

export type OptionType = {
label: string
value: string
}

export type RadioGroupProps = {
className?: string
errorMessage?: string
options: OptionType[]
} & ComponentPropsWithoutRef<typeof RadioGroupRadix.Root>

export const RadioGroup: FC<RadioGroupProps> = forwardRef<
ElementRef<typeof RadioGroupRadix.Root>,
RadioGroupProps
>((props, ref) => {
const { className, errorMessage, options, ...rest } = props

const classNames = {
indicator: styles.indicator,
item: styles.item,
label: clsx(styles.label, rest.disabled && styles.disabled),
root: clsx(className, styles.root),
}

return (
<RadioGroupRadix.Root className={classNames.root} {...rest} ref={ref}>
{options.map(option => (
<Typography as={'label'} className={classNames.label} key={option.value} variant={'body2'}>
<RadioGroupRadix.Item className={classNames.item} value={option.value}>
<RadioGroupRadix.Indicator className={classNames.indicator} />

<span className={styles.frame}></span>
</RadioGroupRadix.Item>

{option.label}
</Typography>
))}

{!!errorMessage && (
<Typography className={styles.error} variant={'caption'}>
{errorMessage}
</Typography>
)}
</RadioGroupRadix.Root>
)
})
6 changes: 6 additions & 0 deletions src/styles/_mixins.scss
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,13 @@
@include square($size);

content: '';

z-index: 1;

display: block;

border-radius: 50%;

transition: 0.3s;
}
}
Expand Down

0 comments on commit d614713

Please sign in to comment.