Skip to content

Commit

Permalink
feat(Radio)!: introduce 2.0 component (#1895)
Browse files Browse the repository at this point in the history
  • Loading branch information
booc0mtaco authored Mar 19, 2024
1 parent 3f540f9 commit 8ef797f
Show file tree
Hide file tree
Showing 7 changed files with 587 additions and 0 deletions.
29 changes: 29 additions & 0 deletions src/components/InputLabel/InputLabel-v2.module.css
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);
}
55 changes: 55 additions & 0 deletions src/components/InputLabel/InputLabel-v2.stories.ts
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.',
},
};
58 changes: 58 additions & 0 deletions src/components/InputLabel/InputLabel-v2.tsx
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';
143 changes: 143 additions & 0 deletions src/components/Radio/Radio-v2.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*------------------------------------*\
# 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 radios. */
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);
}

.radio__input:not(:checked):disabled + & {
color: var(--eds-theme-color-border-utility-disabled);
pointer-events: none;
}

/* 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
* TODO-AH: focus ring clamped to the actual radio button, not its bounding label (browser default)
*/
.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);
}
}
Loading

0 comments on commit 8ef797f

Please sign in to comment.