Skip to content

Commit

Permalink
Created RadioButton component (#2842)
Browse files Browse the repository at this point in the history
* Created RadioButton component

* Moving files into design-system folders and changing directions

* Deleting commented code in files

* Refactored RadioButton loader logic
  • Loading branch information
SofiiaYevush authored Nov 28, 2024
1 parent ff59e01 commit d312938
Show file tree
Hide file tree
Showing 4 changed files with 461 additions and 0 deletions.
108 changes: 108 additions & 0 deletions src/design-system/components/radio-button/RadioButton.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
@use '~scss/utilities' as *;

.radio-btn-loader {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}

.#{$prefix}radio-btn {
display: inline-flex;
align-items: center;
justify-content: flex-start;

&:hover {
.MuiRadio-root {
box-shadow: 0 0 15px rgba(0, 0, 0, 0.4);
}
}

&-sm {
font-size: $font-size-sm;
}

&-md {
font-size: $font-size-md;
}

&-lg {
font-size: $font-size-lg;
}

&-primary {
.MuiRadio-root {
color: $neutral-950;
}
}

&-success {
.MuiRadio-root {
color: $green-600;
}
}

&-error {
.MuiRadio-root {
color: $red-500;
}
}

&-disabled {
.MuiRadio-root {
opacity: 0.5;
pointer-events: none;
color: $neutral-600 !important;
box-shadow: none !important;
}
}

.radio-sm {
width: $border-width-sm;
height: $border-width-sm;
}

.radio-md {
width: $border-width-md;
height: $border-width-md;
}

.radio-lg {
width: $border-width-lg;
height: $border-width-lg;
}

.Mui-checked {
&.radio-primary {
color: $neutral-950;
}

&.radio-success {
color: $green-600;
}

&.radio-error {
color: $red-500;
}
}

.MuiFormControlLabel-label {
font-size: inherit;
color: $body-text-color;
font-weight: normal;
margin-left: 8px;
margin-right: 8px;

&.radio-sm {
font-size: $font-size-sm;
}

&.radio-md {
font-size: $font-size-md;
}

&.radio-lg {
font-size: $font-size-lg;
}
}
}
83 changes: 83 additions & 0 deletions src/design-system/components/radio-button/RadioButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { forwardRef } from 'react'
import {
Radio,
RadioProps as MuiRadioProps,
FormControlLabel,
CircularProgress
} from '@mui/material'
import { cn } from '~/utils/cn'
import './RadioButton.scss'

const sizes = ['sm', 'md', 'lg'] as const
const colors = ['primary', 'success', 'error'] as const

type BaseRadioButtonProps = {
label: string
labelPosition?: 'top' | 'bottom' | 'end'
size?: (typeof sizes)[number]
color?: (typeof colors)[number]
loading?: boolean
checked?: boolean
}

export type RadioButtonProps = BaseRadioButtonProps &
Omit<MuiRadioProps, keyof BaseRadioButtonProps>

const RadioButton = forwardRef<HTMLDivElement, RadioButtonProps>(
(
{
label,
labelPosition = 'end',
size = 'md',
color = 'primary',
className,
disabled = false,
loading = false,
checked = false,
...props
},
forwardedRef
) => {
const isDisabled = disabled || loading

const radioClassNames = cn(
`radio-${size}`,
`radio-${color}`,
{ 'radio-checked': checked, 'radio-disabled': isDisabled },
className
)

const formControlClassNames = cn(
's2s-radio-btn',
`s2s-radio-btn-${size}`,
`s2s-radio-btn-${color}`,
{ 's2s-radio-btn-disabled': isDisabled },
className
)

return (
<FormControlLabel
className={formControlClassNames}
control={
(loading && (
<CircularProgress className='radio-btn-loader' size={20} />
)) || (
<Radio
{...props}
checked={checked}
className={radioClassNames}
disabled={isDisabled}
/>
)
}
label={label}
labelPlacement={labelPosition}
ref={forwardedRef}
/>
)
}
)

RadioButton.displayName = 'RadioButton'

export default RadioButton
185 changes: 185 additions & 0 deletions src/design-system/stories/RadioButton.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import type { Meta, StoryObj } from '@storybook/react'
import RadioButton from '~scss-components/radio-button/RadioButton'
import { fn } from '@storybook/test'

const meta: Meta<typeof RadioButton> = {
title: 'Components/RadioButton',
component: RadioButton,
parameters: {
layout: 'centered',
docs: {
description: {
component: `
The \`RadioButton\` component is a customizable radio button element used for selecting one option from a group. It supports various sizes, colors, and states to suit different use cases in your application.
#### Key Features:
- **Sizes:** Adjust the size of the radio button to fit your UI requirements: sm (small), md (medium), lg (large).
- **Colors:** Choose from several predefined color options to align with your design: primary, success, error.
- **Checked/Unchecked States:** Toggle the button between selected and unselected states.
- **Loading State:** Use a loading indicator to signal an ongoing process while the button is clicked.
- **Disabled State:** Disable the button to prevent interaction.
`
}
}
},
tags: ['autodocs'],
argTypes: {
label: {
description: 'The label that describes the purpose of the radio button.',
control: { type: 'text' }
},
size: {
description: 'The size of the radio button.',
options: ['sm', 'md', 'lg'],
control: { type: 'radio' }
},
color: {
description: 'The visual style of the radio button.',
options: ['primary', 'success', 'error'],
control: { type: 'radio' }
},
checked: {
description: 'Indicates whether the radio button is selected.',
control: { type: 'boolean' }
},
disabled: {
description: 'Disables the radio button, preventing user interaction.',
control: { type: 'boolean' }
},
loading: {
description:
'Displays a loading spinner instead of the radio button when true.',
control: { type: 'boolean' }
},
labelPosition: {
description: 'The position of the label relative to the radio button.',
options: ['top', 'bottom', 'end'],
control: { type: 'radio' }
}
},
args: {
label: 'Radio Button',
checked: false,
disabled: false,
loading: false,
size: 'md',
color: 'primary',
labelPosition: 'end',
onChange: fn()
}
}

export default meta
type Story = StoryObj<typeof meta>

export const Default: Story = {
args: {
label: 'Default Radio Button',
color: 'primary'
},
parameters: {
docs: {
description: {
story: 'A basic unselected radio button.'
}
}
}
}

export const Checked: Story = {
args: {
label: 'Checked Radio Button',
checked: true
},
parameters: {
docs: {
description: {
story: 'A radio button that is checked (selected).'
}
}
}
}

export const Loading: Story = {
args: {
label: 'Loading Radio Button',
loading: true
},
parameters: {
docs: {
description: {
story:
'A radio button with a loading spinner, indicating a process is ongoing.'
}
}
}
}

export const Disabled: Story = {
args: {
label: 'Disabled Radio Button',
disabled: true
},
parameters: {
docs: {
description: {
story: 'A disabled radio button that cannot be interacted with.'
}
}
}
}

export const CustomSize: Story = {
args: {
label: 'Small Radio Button',
size: 'sm'
},
parameters: {
docs: {
description: {
story: 'A small-sized radio button.'
}
}
}
}

export const CustomColor: Story = {
args: {
label: 'Error Radio Button',
color: 'error'
},
parameters: {
docs: {
description: {
story: 'A radio button with the error color variant.'
}
}
}
}

export const LabelPositionTop: Story = {
args: {
label: 'Radio Button with Top Label',
labelPosition: 'top'
},
parameters: {
docs: {
description: {
story: 'A radio button with the label positioned above the button.'
}
}
}
}

export const LabelPositionBottom: Story = {
args: {
label: 'Radio Button with Bottom Label',
labelPosition: 'bottom'
},
parameters: {
docs: {
description: {
story: 'A radio button with the label positioned below the button.'
}
}
}
}
Loading

0 comments on commit d312938

Please sign in to comment.