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

feat(Select): add support for required in overline #1855

Merged
merged 2 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/components/InputField/InputField.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
display: flex;
justify-content: space-between;
margin-bottom: var(--eds-size-half);
color: var(--eds-theme-color-form-label);
}

.input-field__overline--no-label {
justify-content: flex-end;
}
Expand Down
7 changes: 6 additions & 1 deletion src/components/InputField/InputField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,12 @@ export const InputField: InputFieldType = forwardRef(
</InputLabel>
)}
{required && (
<Text as="span" className={requiredTextClassName} size="sm">
<Text
as="span"
className={requiredTextClassName}
preset="body-sm"
size="sm"
>
Required
</Text>
)}
Expand Down
10 changes: 5 additions & 5 deletions src/components/InputField/__snapshots__/InputField.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ exports[`<InputField /> NoVisibleLabel story renders snapshot 1`] = `
class="input-field__overline input-field__overline--no-label"
>
<span
class="text text--sm input-field__required-text"
class="text text--body-sm input-field__required-text"
>
Required
</span>
Expand Down Expand Up @@ -246,7 +246,7 @@ exports[`<InputField /> Required story renders snapshot 1`] = `
Input field with fieldNote
</label>
<span
class="text text--sm input-field__required-text"
class="text text--body-sm input-field__required-text"
>
Required
</span>
Expand Down Expand Up @@ -352,7 +352,7 @@ exports[`<InputField /> WithAMaxLength story renders snapshot 1`] = `
test label
</label>
<span
class="text text--sm input-field__required-text"
class="text text--body-sm input-field__required-text"
>
Required
</span>
Expand Down Expand Up @@ -405,7 +405,7 @@ exports[`<InputField /> WithARecommendedLength story renders snapshot 1`] = `
test label
</label>
<span
class="text text--sm input-field__required-text"
class="text text--body-sm input-field__required-text"
>
Required
</span>
Expand Down Expand Up @@ -457,7 +457,7 @@ exports[`<InputField /> WithBothMaxAndRecommendedLength story renders snapshot 1
test label
</label>
<span
class="text text--sm input-field__required-text"
class="text text--body-sm input-field__required-text"
>
Required
</span>
Expand Down
13 changes: 13 additions & 0 deletions src/components/Select/Select.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@
color: var(--eds-theme-color-form-label);
}

.select__overline--no-label {
justify-content: flex-end;
}

/**
* The container for the individual select options.
*/
Expand All @@ -60,6 +64,15 @@
color: var(--eds-theme-color-icon-brand-primary);
}

.select__required-text {
color: var(--eds-theme-color-text-neutral-subtle);
font: var(--eds-theme-typography-body-sm);
}

.select__required-text--disabled {
color: var(--eds-theme-color-text-disabled);
}

/*------------------------------------*\
# SELECT BUTTON
\*------------------------------------*/
Expand Down
37 changes: 37 additions & 0 deletions src/components/Select/Select.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,19 @@ export const Disabled: StoryObj = {
...Default.args,
disabled: true,
},
parameters: {
axe: {
disabledRules: ['color-contrast'],
},
},
};

export const Required: StoryObj = {
args: {
...Default.args,
required: true,
className: 'w-96',
},
};

/**
Expand All @@ -515,6 +528,30 @@ export const NoVisibleLabel: StoryObj = {
},
};

export const NoVisibleLabelButRequired: StoryObj = {
args: {
...Default.args,
label: undefined,
'aria-label': 'hidden label',
required: true,
className: 'w-96',
},
};

export const DisabledRequired: StoryObj = {
args: {
...Default.args,
disabled: true,
required: true,
className: 'w-96',
},
parameters: {
axe: {
disabledRules: ['color-contrast'],
},
},
};

/**
* Options for each `Select` can be aligned on different sides of the target button. Options for `placement` defined by
* PopperJS.
Expand Down
1 change: 1 addition & 0 deletions src/components/Select/Select.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as stories from './Select.stories';
const {
OpenByDefault,
Disabled,
DisabledRequired,
OptionsRightAligned,
SeparateButtonAndMenuWidth,
EventHandlingOnRenderProp,
Expand Down
39 changes: 34 additions & 5 deletions src/components/Select/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import PopoverContainer, { defaultPopoverModifiers } from '../PopoverContainer';
import type { PopoverContext, PopoverOptions } from '../PopoverContainer';

import PopoverListItem from '../PopoverListItem';
import Text from '../Text';
import styles from './Select.module.css';

export type OptionsAlignType = 'left' | 'right';
Expand Down Expand Up @@ -64,10 +65,15 @@ type SelectProps = ExtractProps<typeof Listbox> &
* Visible text label for the component.
*/
label?: string;
/**
* Indicates that field is required for form to be successfully submitted
*/
required?: boolean;
};

type SelectLabelProps = ExtractProps<typeof Listbox.Label> & {
disabled?: boolean;
required?: boolean;
};
type SelectOptionsProps = ExtractProps<typeof Listbox.Options>;
type SelectOptionProps = ExtractProps<typeof Listbox.Option> & {
Expand Down Expand Up @@ -160,6 +166,7 @@ export function Select(props: SelectProps) {
optionsAlign,
optionsClassName,
placement = 'bottom-start',
required,
strategy,
variant,
onChange: theirOnChange,
Expand Down Expand Up @@ -259,8 +266,10 @@ export function Select(props: SelectProps) {
}
}}
>
{label && (
<Select.Label disabled={props.disabled}>{label}</Select.Label>
{(label || required) && (
<Select.Label disabled={props.disabled} required={required}>
{label}
</Select.Label>
)}
{children}
</Listbox>
Expand All @@ -269,17 +278,37 @@ export function Select(props: SelectProps) {
}

const SelectLabel = (props: SelectLabelProps) => {
const { children, className, disabled } = props;
const { children: label, required, className, disabled } = props;

const componentClassName = clsx(
styles['select__label'],
disabled && clsx(styles['select__label--disabled']),
className,
);
const overlineClassName = clsx(styles['select__overline']);

const requiredTextClassName = clsx(
styles['select__required-text'],
disabled && styles['select__required-text--disabled'],
);

const overlineClassName = clsx(
styles['select__overline'],
!label && styles['select__overline--no-label'],
);

return (
<div className={overlineClassName}>
<Listbox.Label className={componentClassName}>{children}</Listbox.Label>
<Listbox.Label className={componentClassName}>{label}</Listbox.Label>
{required && (
<Text
as="span"
className={requiredTextClassName}
preset="body-sm"
size="sm"
>
Required
</Text>
)}
</div>
);
};
Expand Down
116 changes: 112 additions & 4 deletions src/components/Select/__snapshots__/Select.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -209,12 +209,120 @@ exports[`<Select /> Generated Snapshots NoVisibleLabel story renders snapshot 1`
data-testid="dropdown"
>
<button
aria-controls="headlessui-listbox-options-:r1l:"
aria-controls="headlessui-listbox-options-:r1r:"
aria-expanded="true"
aria-haspopup="listbox"
class="select-button"
data-headlessui-state="open"
id="headlessui-listbox-button-:r1k:"
id="headlessui-listbox-button-:r1q:"
type="button"
>
<span
class=""
>
Dogs
</span>
<svg
aria-hidden="true"
class="icon select-button__icon select-button__icon--reversed"
fill="currentColor"
height="1.25rem"
style="--icon-size: 1.25rem;"
viewBox="0 0 24 24"
width="1.25rem"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M15.88 9.29L12 13.17 8.12 9.29c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41l4.59 4.59c.39.39 1.02.39 1.41 0l4.59-4.59c.39-.39.39-1.02 0-1.41-.39-.38-1.03-.39-1.42 0z"
/>
</svg>
</button>
</div>
`;

exports[`<Select /> Generated Snapshots NoVisibleLabelButRequired story renders snapshot 1`] = `
<div
class="select w-96"
data-headlessui-state="open"
data-testid="dropdown"
>
<div
class="select__overline select__overline--no-label"
>
<label
class="select__label"
data-headlessui-state="open"
id="headlessui-listbox-label-:r1v:"
/>
<span
class="text text--body-sm select__required-text"
>
Required
</span>
</div>
<button
aria-controls="headlessui-listbox-options-:r21:"
aria-expanded="true"
aria-haspopup="listbox"
aria-labelledby="headlessui-listbox-label-:r1v: headlessui-listbox-button-:r20:"
class="select-button"
data-headlessui-state="open"
id="headlessui-listbox-button-:r20:"
type="button"
>
<span
class=""
>
Dogs
</span>
<svg
aria-hidden="true"
class="icon select-button__icon select-button__icon--reversed"
fill="currentColor"
height="1.25rem"
style="--icon-size: 1.25rem;"
viewBox="0 0 24 24"
width="1.25rem"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M15.88 9.29L12 13.17 8.12 9.29c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41l4.59 4.59c.39.39 1.02.39 1.41 0l4.59-4.59c.39-.39.39-1.02 0-1.41-.39-.38-1.03-.39-1.42 0z"
/>
</svg>
</button>
</div>
`;

exports[`<Select /> Generated Snapshots Required story renders snapshot 1`] = `
<div
class="select w-96"
data-headlessui-state="open"
data-testid="dropdown"
>
<div
class="select__overline"
>
<label
class="select__label"
data-headlessui-state="open"
id="headlessui-listbox-label-:r1k:"
>
Favorite Animal
</label>
<span
class="text text--body-sm select__required-text"
>
Required
</span>
</div>
<button
aria-controls="headlessui-listbox-options-:r1m:"
aria-expanded="true"
aria-haspopup="listbox"
aria-labelledby="headlessui-listbox-label-:r1k: headlessui-listbox-button-:r1l:"
class="select-button"
data-headlessui-state="open"
id="headlessui-listbox-button-:r1l:"
type="button"
>
<span
Expand Down Expand Up @@ -306,12 +414,12 @@ exports[`<Select /> Generated Snapshots UsingFunctionProps story renders snapsho
data-testid="dropdown"
>
<button
aria-controls="headlessui-listbox-options-:r1q:"
aria-controls="headlessui-listbox-options-:r26:"
aria-expanded="true"
aria-haspopup="listbox"
class="fpo"
data-headlessui-state="open"
id="headlessui-listbox-button-:r1p:"
id="headlessui-listbox-button-:r25:"
type="button"
>
Select
Expand Down
Loading