-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Vincent Smedinga <v.smedinga@amsterdam.nl> Co-authored-by: Aram <37216945+alimpens@users.noreply.github.com>
- Loading branch information
1 parent
ccec68e
commit 7679acc
Showing
15 changed files
with
548 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
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,9 @@ | ||
<!-- @license CC0-1.0 --> | ||
|
||
# Select | ||
|
||
A form control that allows users to select one or more options from a list. | ||
|
||
## References | ||
|
||
- [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select) |
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,60 @@ | ||
/** | ||
* @license EUPL-1.2+ | ||
* Copyright Gemeente Amsterdam | ||
*/ | ||
|
||
@mixin reset { | ||
appearance: none; | ||
border: 0; | ||
border-radius: 0; // Reset rounded borders for Safari on MacOS | ||
} | ||
|
||
.ams-select { | ||
background-color: var(--ams-select-background-color); | ||
box-shadow: var(--ams-select-box-shadow); | ||
color: var(--ams-select-color); | ||
font-family: var(--ams-select-font-family); | ||
font-size: var(--ams-select-font-size); | ||
font-weight: var(--ams-select-font-weight); | ||
line-height: var(--ams-select-line-height); | ||
max-inline-size: 100%; | ||
outline-offset: var(--ams-select-outline-offset); | ||
padding-block: var(--ams-select-padding-block); | ||
padding-inline: var(--ams-select-padding-inline); | ||
touch-action: manipulation; | ||
|
||
&:not([multiple]) { | ||
background-image: var(--ams-select-background-image); | ||
background-position: var(--ams-select-background-position); | ||
background-repeat: no-repeat; | ||
background-size: 1em 1em; | ||
} | ||
|
||
&:hover { | ||
box-shadow: var(--ams-select-hover-box-shadow); | ||
} | ||
|
||
@include reset; | ||
} | ||
|
||
.ams-select[aria-invalid="true"] { | ||
box-shadow: var(--ams-select-invalid-box-shadow); | ||
|
||
&:hover { | ||
box-shadow: var(--ams-select-invalid-hover-box-shadow); | ||
} | ||
} | ||
|
||
.ams-select:disabled { | ||
box-shadow: var(--ams-select-disabled-box-shadow); | ||
color: var(--ams-select-disabled-color); | ||
cursor: not-allowed; | ||
|
||
&:not([multiple]) { | ||
background-image: var(--ams-select-disabled-background-image); | ||
} | ||
} | ||
|
||
.ams-select__option:disabled { | ||
color: var(--ams-select-option-disabled-color); | ||
} |
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,5 @@ | ||
<!-- @license CC0-1.0 --> | ||
|
||
# React Select component | ||
|
||
[Select documentation](../../../css/src/components/select/README.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,90 @@ | ||
import { render, screen } from '@testing-library/react' | ||
import { createRef } from 'react' | ||
import { Select } from './Select' | ||
import '@testing-library/jest-dom' | ||
|
||
describe('Select', () => { | ||
it('renders', () => { | ||
render(<Select />) | ||
|
||
const component = screen.getByRole('combobox') | ||
|
||
expect(component).toBeInTheDocument() | ||
expect(component).toBeVisible() | ||
}) | ||
|
||
it('renders a design system BEM class name', () => { | ||
render(<Select />) | ||
|
||
const component = screen.getByRole('combobox') | ||
|
||
expect(component).toHaveClass('ams-select') | ||
}) | ||
|
||
it('renders an additional class name', () => { | ||
render(<Select className="extra" />) | ||
|
||
const component = screen.getByRole('combobox') | ||
|
||
expect(component).toHaveClass('ams-select extra') | ||
}) | ||
|
||
it('supports ForwardRef in React', () => { | ||
const ref = createRef<HTMLSelectElement>() | ||
|
||
render(<Select ref={ref} />) | ||
|
||
const component = screen.getByRole('combobox') | ||
|
||
expect(ref.current).toBe(component) | ||
}) | ||
|
||
it('renders options', () => { | ||
render( | ||
<Select> | ||
<Select.Option value="a">Option A</Select.Option> | ||
<Select.Option value="b">Option B</Select.Option> | ||
</Select>, | ||
) | ||
|
||
const select = screen.getByRole('combobox') | ||
|
||
const option = screen.getByRole('option', { | ||
name: 'Option B', | ||
}) | ||
|
||
expect(select).toContain(option) | ||
}) | ||
|
||
it('can be disabled', () => { | ||
render(<Select disabled />) | ||
|
||
const component = screen.getByRole('combobox') | ||
|
||
expect(component).toBeDisabled() | ||
}) | ||
|
||
it('can be invalid', () => { | ||
render(<Select invalid />) | ||
|
||
const component = screen.getByRole('combobox') | ||
|
||
expect(component).toHaveClass('ams-select--invalid') | ||
}) | ||
|
||
it('is not required by default', () => { | ||
render(<Select />) | ||
|
||
const component = screen.getByRole('combobox') | ||
|
||
expect(component).not.toBeRequired() | ||
}) | ||
|
||
it('omits the required attribute when not required', () => { | ||
render(<Select required={false} />) | ||
|
||
const component = screen.getByRole('combobox') | ||
|
||
expect(component).not.toHaveAttribute('required') | ||
}) | ||
}) |
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,32 @@ | ||
/** | ||
* @license EUPL-1.2+ | ||
* Copyright Gemeente Amsterdam | ||
*/ | ||
|
||
import clsx from 'clsx' | ||
import { forwardRef } from 'react' | ||
import type { ForwardedRef, PropsWithChildren, SelectHTMLAttributes } from 'react' | ||
import { SelectOption } from './SelectOption' | ||
import { SelectOptionGroup } from './SelectOptionGroup' | ||
|
||
export type SelectProps = { | ||
/** There is no native invalid attribute for select, but you can use this to get the same result as other form components */ | ||
invalid?: boolean | ||
} & PropsWithChildren<SelectHTMLAttributes<HTMLSelectElement>> | ||
|
||
const SelectRoot = forwardRef( | ||
({ children, className, invalid, ...restProps }: SelectProps, ref: ForwardedRef<HTMLSelectElement>) => ( | ||
<select | ||
{...restProps} | ||
ref={ref} | ||
className={clsx('ams-select', invalid && 'ams-select--invalid', className)} | ||
aria-invalid={invalid || undefined} | ||
> | ||
{children} | ||
</select> | ||
), | ||
) | ||
|
||
SelectRoot.displayName = 'Select' | ||
|
||
export const Select = Object.assign(SelectRoot, { Option: SelectOption, Group: SelectOptionGroup }) |
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,59 @@ | ||
import { render, screen } from '@testing-library/react' | ||
import { createRef } from 'react' | ||
import { Select } from './Select' | ||
import '@testing-library/jest-dom' | ||
|
||
describe('Select option', () => { | ||
it('renders', () => { | ||
render(<Select.Option />) | ||
|
||
const component = screen.getByRole('option') | ||
|
||
expect(component).toBeInTheDocument() | ||
expect(component).toBeVisible() | ||
}) | ||
|
||
it('renders an option role element with a text label', () => { | ||
render(<Select.Option>Option</Select.Option>) | ||
|
||
const option = screen.getByRole('option', { | ||
name: 'Option', | ||
}) | ||
|
||
expect(option).toBeInTheDocument() | ||
}) | ||
|
||
it('renders a design system BEM class name', () => { | ||
render(<Select.Option />) | ||
|
||
const component = screen.getByRole('option') | ||
|
||
expect(component).toHaveClass('ams-select__option') | ||
}) | ||
|
||
it('renders an additional class name', () => { | ||
render(<Select.Option className="extra" />) | ||
|
||
const component = screen.getByRole('option') | ||
|
||
expect(component).toHaveClass('ams-select__option extra') | ||
}) | ||
|
||
it('supports ForwardRef in React', () => { | ||
const ref = createRef<HTMLOptionElement>() | ||
|
||
render(<Select.Option ref={ref} />) | ||
|
||
const component = screen.getByRole('option') | ||
|
||
expect(ref.current).toBe(component) | ||
}) | ||
|
||
it('can be disabled', () => { | ||
render(<Select.Option disabled />) | ||
|
||
const component = screen.getByRole('option') | ||
|
||
expect(component).toBeDisabled() | ||
}) | ||
}) |
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,23 @@ | ||
/** | ||
* @license EUPL-1.2+ | ||
* Copyright Gemeente Amsterdam | ||
*/ | ||
|
||
import clsx from 'clsx' | ||
import { forwardRef } from 'react' | ||
import type { ForwardedRef, OptionHTMLAttributes, PropsWithChildren } from 'react' | ||
|
||
export type SelectOptionProps = OptionHTMLAttributes<HTMLOptionElement> | ||
|
||
export const SelectOption = forwardRef( | ||
( | ||
{ children, className, ...restProps }: PropsWithChildren<SelectOptionProps>, | ||
ref: ForwardedRef<HTMLOptionElement>, | ||
) => ( | ||
<option {...restProps} ref={ref} className={clsx('ams-select__option', className)}> | ||
{children} | ||
</option> | ||
), | ||
) | ||
|
||
SelectOption.displayName = 'Select.Option' |
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,59 @@ | ||
import { render, screen } from '@testing-library/react' | ||
import { createRef } from 'react' | ||
import { Select } from './Select' | ||
import '@testing-library/jest-dom' | ||
|
||
describe('Select option group', () => { | ||
it('renders', () => { | ||
render(<Select.Group />) | ||
|
||
const component = screen.getByRole('group') | ||
|
||
expect(component).toBeInTheDocument() | ||
expect(component).toBeVisible() | ||
}) | ||
|
||
it('renders an group role element with a text label', () => { | ||
render(<Select.Group label="Options" />) | ||
|
||
const option = screen.getByRole('group', { | ||
name: 'Options', | ||
}) | ||
|
||
expect(option).toBeInTheDocument() | ||
}) | ||
|
||
it('renders a design system BEM class name', () => { | ||
render(<Select.Group />) | ||
|
||
const component = screen.getByRole('group') | ||
|
||
expect(component).toHaveClass('ams-select__group') | ||
}) | ||
|
||
it('renders an additional class name', () => { | ||
render(<Select.Group className="extra" />) | ||
|
||
const component = screen.getByRole('group') | ||
|
||
expect(component).toHaveClass('ams-select__group extra') | ||
}) | ||
|
||
it('supports ForwardRef in React', () => { | ||
const ref = createRef<HTMLOptGroupElement>() | ||
|
||
render(<Select.Group ref={ref} />) | ||
|
||
const component = screen.getByRole('group') | ||
|
||
expect(ref.current).toBe(component) | ||
}) | ||
|
||
it('can be disabled', () => { | ||
render(<Select.Group disabled />) | ||
|
||
const component = screen.getByRole('group') | ||
|
||
expect(component).toBeDisabled() | ||
}) | ||
}) |
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,23 @@ | ||
/** | ||
* @license EUPL-1.2+ | ||
* Copyright Gemeente Amsterdam | ||
*/ | ||
|
||
import clsx from 'clsx' | ||
import { forwardRef } from 'react' | ||
import type { ForwardedRef, OptgroupHTMLAttributes, PropsWithChildren } from 'react' | ||
|
||
export type SelectOptionGroupProps = OptgroupHTMLAttributes<HTMLOptGroupElement> | ||
|
||
export const SelectOptionGroup = forwardRef( | ||
( | ||
{ children, className, ...restProps }: PropsWithChildren<SelectOptionGroupProps>, | ||
ref: ForwardedRef<HTMLOptGroupElement>, | ||
) => ( | ||
<optgroup {...restProps} ref={ref} className={clsx('ams-select__group', className)}> | ||
{children} | ||
</optgroup> | ||
), | ||
) | ||
|
||
SelectOptionGroup.displayName = 'Select.Group' |
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,3 @@ | ||
export { Select } from './Select' | ||
export type { SelectProps } from './Select' | ||
export type { SelectOptionProps } from './SelectOption' |
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
Oops, something went wrong.