Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Checkbox #67

Merged
merged 11 commits into from
Apr 3, 2024
51 changes: 51 additions & 0 deletions libs/pxweb2-ui/src/lib/components/Checkbox/Checkbox.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
.checkboxWrapper {
min-height: 44px;
display: flex;
position: relative;
&:hover {
cursor: pointer;
background-color: var(--px-color-surface-subtle-hover);
}

&:active {
background-color: var(--px-color-surface-subtle-active);
}
border-radius: 4px;
}

.checkmark {
margin-top: 10px;
margin-bottom: 10px;
margin-right: 12px;
margin-left: 8px;
min-width: 22px;
width: 22px;
height: 22px;
border: 2px solid var(--px-color-border-action);
border-radius: 4px;
top: 0;
left: 0;
background-color: var(--px-color-background-default);
display: flex;
align-items: center;
justify-content: center;
}

.checkmarkWithoutMargin {
margin-left: 0px;
}

.checked {
background-color: var(--px-color-surface-action);
color: white;
}

.label {
margin-top: 13px;
margin-bottom: 8px;
font-family: PxWeb-font-400;
letter-spacing: 0em;
}
.strong {
font-family: PxWeb-font-700;
}
19 changes: 19 additions & 0 deletions libs/pxweb2-ui/src/lib/components/Checkbox/Checkbox.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { render } from '@testing-library/react';

import Checkbox from './Checkbox';

describe('Checkbox', () => {
it('should render successfully', () => {
const { baseElement } = render(
<Checkbox
id="test"
text="Variable 1"
onChange={(val) => {
console.log(val);
}}
value={true}
/>
);
expect(baseElement).toBeTruthy();
});
});
159 changes: 159 additions & 0 deletions libs/pxweb2-ui/src/lib/components/Checkbox/Checkbox.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import type { Meta, StoryFn } from '@storybook/react';
import { Checkbox, MixedCheckbox } from './Checkbox';
import React from 'react';

/* import { within } from '@storybook/testing-library';
import { expect } from '@storybook/jest'; */

const meta: Meta<typeof Checkbox> = {
component: Checkbox,
title: 'Components/Checkbox',
};
export default meta;

export const MixedStateCheckbox: StoryFn<typeof Checkbox> = () => {
const [selectedMalamute, setSelectedMalamute] = React.useState(true);
const [selectedHusky, setSelectedHusky] = React.useState(true);
const [selectedYorkshireTerrier, setSelectedYorkshireTerrier] =
React.useState(false);
const [allSelected, setAllSelected] = React.useState<
'mixed' | 'true' | 'false'
>('mixed');

React.useEffect(() => {
if (!selectedHusky && !selectedMalamute && !selectedYorkshireTerrier) {
setAllSelected('false');
} else if (selectedHusky && selectedMalamute && selectedYorkshireTerrier) {
setAllSelected('true');
} else {
setAllSelected('mixed');
}
}, [selectedMalamute, selectedHusky, selectedYorkshireTerrier]);

return (
<>
<MixedCheckbox
id="test"
text="Select all"
onChange={(val) => {
if (selectedHusky && selectedMalamute && selectedYorkshireTerrier) {
setSelectedHusky(false);
setSelectedMalamute(false);
setSelectedYorkshireTerrier(false);
}
if (
!selectedHusky ||
!selectedMalamute ||
!selectedYorkshireTerrier
) {
setSelectedHusky(true);
setSelectedMalamute(true);
setSelectedYorkshireTerrier(true);
}
}}
ariaControls={['var1', 'var2', 'var3']}
value={allSelected}
strong={true}
/>

<Checkbox
id="var1"
text="Husky"
onChange={(val) => {
setSelectedHusky(val);
}}
value={selectedHusky}
/>
<Checkbox
id="var2"
text="Malamute"
onChange={(val) => {
setSelectedMalamute(val);
}}
value={selectedMalamute}
/>
<Checkbox
id="var3"
text="Yorkshire Terrier"
onChange={(val) => {
setSelectedYorkshireTerrier(val);
}}
value={selectedYorkshireTerrier}
/>
</>
);
};

export const LongTextOn400pxWideMax: StoryFn<typeof Checkbox> = () => {
const [selectedVar1, setSelectedVar1] = React.useState(true);
const [selectedVar2, setSelectedVar2] = React.useState(true);
const [selectedVar3, setSelectedVar3] = React.useState(false);

return (
<div style={{ width: '400px' }}>
<Checkbox
id="var1"
text="The text on this checkbox spans multiple lines. That's why it is written so long. Here's an additional sentence."
onChange={(val) => {
setSelectedVar2(val);
}}
value={selectedVar2}
/>

<Checkbox
id="var2"
text="Short text"
onChange={(val) => {
setSelectedVar1(val);
}}
value={selectedVar1}
/>
<Checkbox
id="var3"
text="The text on this checkbox spans multiple lines. That's why it is written so long. Here's an additional sentence."
onChange={(val) => {
setSelectedVar3(val);
}}
value={selectedVar3}
/>
</div>
);
};
export const NoMargin: StoryFn<typeof Checkbox> = () => {
const [selectedVar1, setSelectedVar1] = React.useState(true);
const [selectedVar2, setSelectedVar2] = React.useState(true);
const [selectedVar3, setSelectedVar3] = React.useState(false);

return (
<div style={{ width: '400px' }}>
<Checkbox
id="var1"
text="No margin"
onChange={(val) => {
setSelectedVar2(val);
}}
value={selectedVar2}
noMargin={true}
/>

<Checkbox
id="var2"
text="No margin"
onChange={(val) => {
setSelectedVar1(val);
}}
value={selectedVar1}
noMargin={true}
/>
<Checkbox
id="var3"
text="Margin"
onChange={(val) => {
setSelectedVar3(val);
}}
value={selectedVar3}
noMargin={false}
/>
</div>
);
};
128 changes: 128 additions & 0 deletions libs/pxweb2-ui/src/lib/components/Checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import React from 'react';
import cl from 'clsx';
import styles from './Checkbox.module.scss';
import { Icon } from '../Icon/Icon';
import Label from '../Typography/Label/Label';

interface CheckboxProps {
id: string;
text: string;
value: boolean;
onChange: (str: boolean) => void;
tabIndex?: number;
strong?: boolean;
noMargin?: boolean;
}

export const Checkbox: React.FC<CheckboxProps> = ({
id,
value,
text,
onChange,
tabIndex,
strong,
noMargin,
}) => {
return (
<div
id={id}
role="checkbox"
aria-checked={value}
aria-labelledby={id + '-label'}
className={styles.checkboxWrapper}
tabIndex={tabIndex ? tabIndex : 0}
onKeyUp={(event) => {
if (event.key === ' ' || event.key === 'Enter') {
event.preventDefault();
onChange(!value);
}
}}
onClick={(event) => {
event.preventDefault();
onChange(!value);
}}
>
<span
className={cl(styles.checkmark, {
[styles.checked]: value,
[styles.checkmarkWithoutMargin]: noMargin,
})}
>
{value && <Icon iconName="CheckMark"></Icon>}
</span>
<div className={styles.label} id={id + '-label'}>
<span className={cl({ [styles.strong]: strong })}>{text}</span>
</div>
</div>
);
};
interface MixedCheckboxProps {
id: string;
text: string;
value: 'mixed' | 'false' | 'true';
onChange: (str: string) => void;
ariaControls: string[];
tabIndex?: number;
strong?: boolean;
noMargin?: boolean;
}
export const MixedCheckbox: React.FC<MixedCheckboxProps> = ({
id,
value,
text,
onChange,
ariaControls,
tabIndex,
strong,
noMargin,
}) => {
return (
<div
id={id}
role="checkbox"
aria-checked={value}
aria-labelledby={id + '-label'}
aria-controls={ariaControls.join(' ')}
className={styles.checkboxWrapper}
tabIndex={tabIndex ? tabIndex : 0}
onKeyUp={(event) => {
if (event.key === ' ' || event.key === 'Enter') {
event.preventDefault();
if (value === 'false') {
onChange('true');
}
if (value === 'mixed' || value === 'true') {
onChange('false');
}
}
}}
onClick={(event) => {
event.preventDefault();
if (value === 'false') {
onChange('true');
}
if (value === 'mixed' || value === 'true') {
onChange('false');
}
}}
>
<span
className={cl(styles.checkmark, {
[styles.checked]: value === 'mixed' || value === 'true',
[styles.checkmarkWithoutMargin]: noMargin,
})}
>
{value === 'true' && <Icon iconName="CheckMark"></Icon>}
{value === 'mixed' && <Icon iconName="IndeterminateCheckMark"></Icon>}
</span>
<div
className={cl(styles.label, { [styles.strong]: strong })}
id={id + '-label'}
>
{text}
</div>
</div>
);
};

export default Checkbox;
2 changes: 2 additions & 0 deletions libs/pxweb2-ui/src/lib/components/Icon/Icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export const QuestionMarkCircle = <path fill-rule="evenodd" clip-rule="evenodd"
export const QuestionMarkCircleFilled = <path fill-rule="evenodd" clip-rule="evenodd" d="M21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12ZM11.0803 8.97901C10.8899 9.16943 10.75 9.46912 10.75 9.89868C10.75 10.3129 10.4142 10.6487 10 10.6487C9.58579 10.6487 9.25 10.3129 9.25 9.89868C9.25 9.12825 9.51009 8.42794 10.0197 7.91835C10.5293 7.40877 11.2296 7.14868 12 7.14868C12.7704 7.14868 13.4707 7.40877 13.9803 7.91835C14.4899 8.42794 14.75 9.12825 14.75 9.89868C14.75 10.4159 14.5328 10.8431 14.2978 11.1721C14.0738 11.4857 13.7863 11.7731 13.5504 12.009L13.5303 12.029C13.2699 12.2894 13.0674 12.4946 12.9228 12.6971C12.7828 12.8931 12.75 13.0159 12.75 13.0987V13.4987C12.75 13.9129 12.4142 14.2487 12 14.2487C11.5858 14.2487 11.25 13.9129 11.25 13.4987V13.0987C11.25 12.5814 11.4672 12.1543 11.7022 11.8253C11.9262 11.5117 12.2137 11.2243 12.4496 10.9884L12.4496 10.9884L12.4697 10.9684C12.7301 10.7079 12.9326 10.5027 13.0772 10.3003C13.2172 10.1043 13.25 9.98145 13.25 9.89868C13.25 9.46912 13.1101 9.16943 12.9197 8.97901C12.7293 8.7886 12.4296 8.64868 12 8.64868C11.5704 8.64868 11.2707 8.7886 11.0803 8.97901ZM11.9998 14.9517C11.4751 14.9517 11.0498 15.377 11.0498 15.9017C11.0498 16.4263 11.4751 16.8517 11.9998 16.8517C12.5245 16.8517 12.9498 16.4263 12.9498 15.9017C12.9498 15.377 12.5245 14.9517 11.9998 14.9517Z" />;
export const InformationCircle = <path fill-rule="evenodd" clip-rule="evenodd" d="M20.25 12C20.25 16.5563 16.5563 20.25 12 20.25C7.44365 20.25 3.75 16.5563 3.75 12C3.75 7.44365 7.44365 3.75 12 3.75C16.5564 3.75 20.25 7.44365 20.25 12ZM12 21.75C17.3848 21.75 21.75 17.3848 21.75 12C21.75 6.61522 17.3848 2.25 12 2.25C6.61523 2.25 2.25 6.61522 2.25 12C2.25 17.3848 6.61522 21.75 12 21.75ZM11 8C11 7.44771 11.4477 7 12 7C12.5523 7 13 7.44771 13 8C13 8.55228 12.5523 9 12 9C11.4477 9 11 8.55229 11 8ZM12 10.25C12.4142 10.25 12.75 10.5858 12.75 11L12.75 16.5C12.75 16.9142 12.4142 17.25 12 17.25C11.5858 17.25 11.25 16.9142 11.25 16.5L11.25 11C11.25 10.5858 11.5858 10.25 12 10.25Z" />;
export const InformationCircleFilled = <path fill-rule="evenodd" clip-rule="evenodd" d="M12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3ZM11 8C11 7.44771 11.4477 7 12 7C12.5523 7 13 7.44772 13 8C13 8.55228 12.5523 9 12 9C11.4477 9 11 8.55229 11 8ZM12 10.25C12.4142 10.25 12.75 10.5858 12.75 11L12.75 16.5C12.75 16.9142 12.4142 17.25 12 17.25C11.5858 17.25 11.25 16.9142 11.25 16.5L11.25 11C11.25 10.5858 11.5858 10.25 12 10.25Z" />;
export const CheckMark = <path d="M10.3333 17L6 12.7447L7.51667 11.2553L10.3333 14.0213L17.4833 7L19 8.48936L10.3333 17Z" />
export const IndeterminateCheckMark = <><rect x="6" y="10" width="12" height="4" rx="0.5"/></>
export const CheckMarkCircle = <path fill-rule="evenodd" clip-rule="evenodd" d="M3.75 12C3.75 7.44365 7.44365 3.75 12 3.75C16.5563 3.75 20.25 7.44365 20.25 12C20.25 16.5563 16.5563 20.25 12 20.25C7.44365 20.25 3.75 16.5563 3.75 12ZM12 2.25C6.61522 2.25 2.25 6.61522 2.25 12C2.25 17.3848 6.61522 21.75 12 21.75C17.3848 21.75 21.75 17.3848 21.75 12C21.75 6.61522 17.3848 2.25 12 2.25ZM16.5725 9.48446C16.8401 9.16825 16.8007 8.69502 16.4845 8.42746C16.1683 8.1599 15.695 8.19934 15.4275 8.51554L10.454 14.3933L8.03033 11.9697C7.73744 11.6768 7.26256 11.6768 6.96967 11.9697C6.67678 12.2626 6.67678 12.7374 6.96967 13.0303L9.96967 16.0303C10.118 16.1786 10.3216 16.2581 10.5312 16.2494C10.7407 16.2406 10.9371 16.1446 11.0725 15.9845L16.5725 9.48446Z" />;
export const CheckMarkCircleFilled = <path fill-rule="evenodd" clip-rule="evenodd" d="M12 21.75C17.3848 21.75 21.75 17.3848 21.75 12C21.75 6.61522 17.3848 2.25 12 2.25C6.61522 2.25 2.25 6.61522 2.25 12C2.25 17.3848 6.61522 21.75 12 21.75ZM16.9536 9.27485C17.2434 8.93229 17.2007 8.41962 16.8582 8.12977C16.5156 7.83991 16.0029 7.88263 15.7131 8.22519L10.3251 14.5928L7.69952 11.9672C7.38222 11.6499 6.86778 11.6499 6.55048 11.9672C6.23317 12.2845 6.23317 12.7989 6.55048 13.1162L9.80048 16.3662C9.96114 16.5269 10.1817 16.6129 10.4088 16.6035C10.6358 16.594 10.8485 16.49 10.9953 16.3165L16.9536 9.27485Z" />;
export const ExclamationMark = <path fill-rule="evenodd" clip-rule="evenodd" d="M12 2.25C12.2731 2.25 12.5245 2.3984 12.6565 2.63743L22.1828 19.8874C22.3111 20.1198 22.3071 20.4026 22.1722 20.6312C22.0373 20.8597 21.7917 21 21.5263 21H2.47372C2.20832 21 1.96268 20.8597 1.8278 20.6312C1.69292 20.4026 1.68888 20.1198 1.81719 19.8874L11.3435 2.63743C11.4755 2.3984 11.7269 2.25 12 2.25ZM20.2553 19.5L12 4.55142L3.74468 19.5H20.2553ZM12 8.75C12.4142 8.75 12.75 9.08579 12.75 9.5V13.5C12.75 13.9142 12.4142 14.25 12 14.25C11.5858 14.25 11.25 13.9142 11.25 13.5V9.5C11.25 9.08579 11.5858 8.75 12 8.75ZM12 15.5C11.4477 15.5 11 15.9477 11 16.5C11 17.0523 11.4477 17.5 12 17.5C12.5523 17.5 13 17.0523 13 16.5C13 15.9477 12.5523 15.5 12 15.5Z" />;
Expand Down