feat(app): introduce button selector component [EE-2004] (#6112)

pull/4441/head
Chaim Lev-Ari 2021-11-21 11:39:26 +02:00 committed by GitHub
parent 17a20cb2c6
commit 8e83a95996
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 101 additions and 2 deletions

View File

@ -82,6 +82,7 @@ overrides:
'@typescript-eslint/explicit-module-boundary-types': off
'@typescript-eslint/no-unused-vars': 'error'
'@typescript-eslint/no-explicit-any': 'error'
'jsx-a11y/label-has-associated-control': ['error', { 'assert': 'either' }]
- files:
- app/**/*.test.*
extends:

View File

@ -1,17 +1,19 @@
import { PropsWithChildren } from 'react';
import clsx from 'clsx';
type Size = 'xsmall' | 'small' | 'large';
export type Size = 'xsmall' | 'small' | 'large';
export interface Props {
size?: Size;
className?: string;
}
export function ButtonGroup({
size = 'small',
children,
className,
}: PropsWithChildren<Props>) {
return (
<div className={clsx('btn-group', sizeClass(size))} role="group">
<div className={clsx('btn-group', sizeClass(size), className)} role="group">
{children}
</div>
);

View File

@ -0,0 +1,10 @@
.group input {
display: none;
}
.group input:checked + label {
color: #fff;
background-color: #286090;
background-image: none;
border-color: #204d74;
}

View File

@ -0,0 +1,29 @@
import { Meta } from '@storybook/react';
import { useState } from 'react';
import { ButtonSelector, Option } from './ButtonSelector';
export default {
component: ButtonSelector,
title: 'Components/ButtonSelector',
} as Meta;
export function TwoOptionsSelector() {
const options: Option<string>[] = [
{ value: 'sAMAccountName', label: 'username' },
{ value: 'userPrincipalName', label: 'user@domainname' },
];
const [value, setValue] = useState('sAMAccountName');
return (
<ButtonSelector<string>
onChange={handleChange}
value={value}
options={options}
/>
);
function handleChange(value: string) {
setValue(value);
}
}

View File

@ -0,0 +1,57 @@
import clsx from 'clsx';
import { PropsWithChildren } from 'react';
import { ButtonGroup, Size } from '../../Button/ButtonGroup';
import styles from './ButtonSelector.module.css';
export interface Option<T> {
value: T;
label?: string;
}
interface Props<T> {
value: T;
onChange(value: T): void;
options: Option<T>[];
size?: Size;
}
export function ButtonSelector<T extends string | number>({
value,
onChange,
size,
options,
}: Props<T>) {
return (
<ButtonGroup size={size} className={styles.group}>
{options.map((option) => (
<OptionItem
key={option.value}
selected={value === option.value}
onChange={() => onChange(option.value)}
>
{option.label || option.value.toString()}
</OptionItem>
))}
</ButtonGroup>
);
}
interface OptionItemProps {
selected: boolean;
onChange(): void;
}
function OptionItem({
selected,
children,
onChange,
}: PropsWithChildren<OptionItemProps>) {
return (
<label className={clsx('btn btn-primary', { active: selected })}>
{children}
<input type="radio" checked={selected} onChange={onChange} />
</label>
);
}