-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Radio)!: introduce 2.0 component
- Loading branch information
1 parent
e6fffa2
commit 0455d0f
Showing
6 changed files
with
569 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
@import '../../design-tokens/mixins.css'; | ||
|
||
/*------------------------------------*\ | ||
# INPUT LABEL | ||
\*------------------------------------*/ | ||
|
||
/** | ||
* Text labeling the input component. | ||
*/ | ||
.label { | ||
color: var(--eds-theme-color-text-utility-neutral-primary); | ||
} | ||
|
||
/** | ||
* Disabled variant of the input label. | ||
*/ | ||
.label--disabled { | ||
color: var(--eds-theme-color-text-utility-disabled-primary); | ||
} | ||
|
||
/** | ||
* Input label size variants. | ||
*/ | ||
.label--md { | ||
font: var(--eds-theme-typography-body-sm); | ||
} | ||
.label--lg { | ||
font: var(--eds-theme-typography-body-md); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import type { StoryObj, Meta } from '@storybook/react'; | ||
import type { ComponentProps } from 'react'; | ||
|
||
import { InputLabel } from './InputLabel-v2'; | ||
|
||
export default { | ||
title: 'Components/V2/InputLabel', | ||
component: InputLabel, | ||
args: { | ||
children: 'Label', | ||
}, | ||
parameters: { | ||
badges: ['intro-1.0'], | ||
}, | ||
} as Meta<Args>; | ||
|
||
type Args = ComponentProps<typeof InputLabel>; | ||
|
||
export const Default: StoryObj<Args> = {}; | ||
|
||
export const Medium: StoryObj<Args> = { | ||
args: { | ||
size: 'md', | ||
}, | ||
}; | ||
|
||
export const LargeDisabled: StoryObj<Args> = { | ||
args: { | ||
disabled: true, | ||
}, | ||
parameters: { | ||
axe: { | ||
disabledRules: ['color-contrast'], | ||
}, | ||
}, | ||
}; | ||
|
||
export const MediumDisabled: StoryObj<Args> = { | ||
args: { | ||
disabled: true, | ||
size: 'md', | ||
}, | ||
parameters: { | ||
axe: { | ||
disabledRules: ['color-contrast'], | ||
}, | ||
}, | ||
}; | ||
|
||
export const LongCopy: StoryObj<Args> = { | ||
args: { | ||
children: | ||
'Long label lorem ipsum dolor sit amet, consectetur adipiscing elit. Ac id velit ut egestas arcu. Atmaecenas urna, risus donec praesent eu consectetur.', | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import clsx from 'clsx'; | ||
import React from 'react'; | ||
import type { ReactNode } from 'react'; | ||
import type { Size } from '../../util/variant-types'; | ||
import styles from './InputLabel-v2.module.css'; | ||
|
||
export type InputLabelProps = { | ||
/** | ||
* Text to render in label. | ||
*/ | ||
children: ReactNode; | ||
/** | ||
* Additional classnames passed in for styling. | ||
*/ | ||
className?: string; | ||
/** | ||
* ID of input that label is associated with. | ||
*/ | ||
htmlFor: string; | ||
/** | ||
* Size of the label. | ||
* | ||
* **Default is `"lg"`**. | ||
*/ | ||
size?: Extract<Size, 'md' | 'lg'>; | ||
/** | ||
* Indicates disabled state of the input. | ||
*/ | ||
disabled?: boolean; | ||
}; | ||
|
||
/** | ||
* `import {InputLabel} from "@chanzuckerberg/eds";` | ||
* | ||
* Label associated with an input element such as a radio or checkbox. | ||
*/ | ||
export const InputLabel = ({ | ||
children, | ||
className, | ||
htmlFor, | ||
size = 'lg', | ||
disabled, | ||
}: InputLabelProps) => { | ||
const componentClassName = clsx( | ||
styles['label'], | ||
size === 'md' && styles['label--md'], | ||
size === 'lg' && styles['label--lg'], | ||
disabled && styles['label--disabled'], | ||
className, | ||
); | ||
return ( | ||
<label className={componentClassName} htmlFor={htmlFor}> | ||
{children} | ||
</label> | ||
); | ||
}; | ||
|
||
InputLabel.displayName = 'InputLabel'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
/*------------------------------------*\ | ||
# RADIO BUTTON | ||
\*------------------------------------*/ | ||
|
||
/** | ||
* A custom individual radio control | ||
*/ | ||
.radio { | ||
display: flex; | ||
gap: 0.5rem; | ||
} | ||
|
||
/** | ||
* Wraps the visually hidden radio input element and the visible sibling svg element. | ||
*/ | ||
.input__wrapper { | ||
position: relative; | ||
/* Centers the radio icon in the wrapper. */ | ||
display: inline-flex; | ||
align-items: center; | ||
/* Aligns the radio with the first line of the label. */ | ||
align-self: flex-start; | ||
} | ||
/** | ||
* The visually hidden input element for the radio. The visual radio is provided by an svg element. | ||
*/ | ||
.radio__input { | ||
/* Removes default margins placed by browser for radioes. */ | ||
margin: 0; | ||
/* Remove the radio from the page flow, positioning it on top of the SVG. */ | ||
position: absolute; | ||
/* Set same dimensions as the RadioSvg element. */ | ||
height: 1.5rem; | ||
width: 1.5rem; | ||
/** | ||
* Hide the input element visually. | ||
* This ensures the radio is still "physically" present so that all users, | ||
* especially on touch screen readers, still interact with the real radio element | ||
* where it would naturally be present. | ||
*/ | ||
opacity: 0; | ||
} | ||
|
||
/** | ||
* The disabled status of the visually hidden input element. | ||
*/ | ||
.radio__input:disabled { | ||
/* Needed since the input element overlays the visible svg icon for user input and cursor. */ | ||
cursor: not-allowed; | ||
pointer-events: none; | ||
} | ||
|
||
.radio__labels { | ||
position: relative; | ||
} | ||
|
||
/** | ||
* Text that labels a radio input. | ||
*/ | ||
.radio__label { | ||
position: relative; | ||
} | ||
|
||
.radio__sub-label { | ||
display: block; | ||
|
||
color: var(--eds-theme-color-text-utility-neutral-secondary); | ||
} | ||
|
||
/** | ||
* The visible radio svg icon element | ||
*/ | ||
.radio__icon { | ||
/* Creates space for the border so that there's no jitter when the focus border is visible. */ | ||
border: 0.125rem solid transparent; | ||
|
||
/* Theming when unchecked */ | ||
.radio__input:not(:checked) + & { | ||
color: var(--eds-theme-color-border-utility-neutral-medium-emphasis); | ||
} | ||
|
||
.radio__input:not(:checked):hover + & { | ||
color: var(--eds-theme-color-border-utility-neutral-medium-emphasis-hover); | ||
} | ||
|
||
.radio__input:not(:checked):active + & { | ||
color: var(--eds-theme-color-border-utility-neutral-medium-emphasis-active); | ||
} | ||
|
||
/* Theming when checked */ | ||
.radio__input:checked + & { | ||
color: var(--eds-theme-color-background-utility-interactive-high-emphasis); | ||
} | ||
|
||
.radio__input:checked:hover ~ & { | ||
color: var(--eds-theme-color-background-utility-interactive-high-emphasis-hover); | ||
} | ||
|
||
.radio__input:checked:active ~ & { | ||
color: var(--eds-theme-color-background-utility-interactive-high-emphasis-active); | ||
} | ||
|
||
.radio__input:checked:disabled ~ & { | ||
color: var(--eds-theme-color-border-utility-disabled); | ||
} | ||
|
||
/** | ||
* Error Theming | ||
*/ | ||
.radio__input.radio--error ~ & { | ||
color: var(--eds-theme-color-border-utility-critical); | ||
} | ||
|
||
.radio__input.radio--error:hover ~ & { | ||
color: var(--eds-theme-color-border-utility-critical-hover); | ||
} | ||
|
||
.radio__input.radio--error:active ~ & { | ||
color: var(--eds-theme-color-border-utility-critical-active); | ||
} | ||
} | ||
|
||
/** | ||
* Handling focus ring | ||
*/ | ||
.radio__input:focus-visible + .radio__icon { | ||
border: 0.125rem solid var(--eds-theme-color-border-utility-focus); | ||
border-radius: var(--eds-border-radius-full); | ||
} | ||
|
||
@supports not selector(:focus-visible) { | ||
.radio__input:focus + .radio__icon { | ||
border: 0.125rem solid var(--eds-theme-color-border-utility-focus); | ||
border-radius: var(--eds-border-radius-full); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import type { StoryObj, Meta } from '@storybook/react'; | ||
import React from 'react'; | ||
|
||
import { Radio } from './Radio-v2'; | ||
|
||
const meta: Meta<typeof Radio> = { | ||
title: 'Components/V2/Radio', | ||
component: Radio, | ||
parameters: { | ||
badges: ['intro-1.0', 'current-2.0'], | ||
}, | ||
decorators: [(Story) => <div className="p-8">{Story()}</div>], | ||
}; | ||
|
||
export default meta; | ||
|
||
type Args = React.ComponentProps<typeof Radio>; | ||
type Story = StoryObj<Args>; | ||
|
||
export const Default: Story = { | ||
args: { | ||
name: 'option-1', | ||
label: 'Option 1', | ||
disabled: false, | ||
checked: false, | ||
readOnly: true, | ||
}, | ||
}; | ||
|
||
export const Checked: Story = { | ||
args: { | ||
...Default.args, | ||
name: 'option-checked', | ||
checked: true, | ||
readOnly: true, | ||
}, | ||
}; | ||
|
||
export const Disabled: Story = { | ||
args: { | ||
...Default.args, | ||
name: 'option-disabled', | ||
disabled: true, | ||
}, | ||
parameters: { | ||
axe: { | ||
disabledRules: ['color-contrast'], | ||
}, | ||
}, | ||
}; | ||
|
||
export const Error: Story = { | ||
args: { | ||
...Default.args, | ||
name: 'option-error', | ||
isError: true, | ||
}, | ||
}; | ||
|
||
export const ErrorAndChecked: Story = { | ||
args: { | ||
...Error.args, | ||
name: 'option-error', | ||
checked: true, | ||
readOnly: true, | ||
}, | ||
}; | ||
|
||
export const WithSublabel: Story = { | ||
args: { | ||
...Default.args, | ||
subLabel: 'Some additional label text', | ||
}, | ||
}; | ||
|
||
export const WithoutVisibleLabel: Story = { | ||
args: { | ||
...Default.args, | ||
label: undefined, | ||
'aria-label': 'unchecked radio button', | ||
}, | ||
parameters: { | ||
axe: { | ||
disabledRules: ['color-contrast'], | ||
}, | ||
}, | ||
}; | ||
|
||
export const LongLabels = { | ||
render: () => { | ||
const label = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit'; | ||
|
||
return ( | ||
<div | ||
style={{ | ||
display: 'grid', | ||
width: '20rem', | ||
gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', | ||
gap: '1rem', | ||
}} | ||
> | ||
<Radio checked label={label} name="option-long-label" readOnly /> | ||
</div> | ||
); | ||
}, | ||
parameters: { | ||
axe: { | ||
disabledRules: ['color-contrast'], | ||
}, | ||
}, | ||
}; |
Oops, something went wrong.