Skip to content

Commit dc2bb28

Browse files
authored
Merge pull request #6 from couvq/autocomplete
autocomplete styling and basic integration tests
2 parents 6de9824 + 2d96ad6 commit dc2bb28

File tree

4 files changed

+158
-10
lines changed

4 files changed

+158
-10
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import AxeBuilder from "@axe-core/playwright";
2+
import { expect, test } from "@playwright/test";
3+
import { baseUrl } from "./constants";
4+
5+
const basicTriggerSelector = '[data-testId="basic-autocomplete-trigger"]';
6+
const basicExampleFirstItemSelector = '[data-testId="basic-autocomplete-item-1"]'
7+
const basicExampleSelectedId = '[data-testId="basic-autocomplete-selected-id"]'
8+
9+
test.describe("<Link /> integration tests", () => {
10+
test.beforeEach(async ({ page }) => {
11+
await page.goto(`${process.env.LOCALHOST ?? baseUrl}`);
12+
await page.locator('[href="/autocomplete"]').click();
13+
});
14+
15+
test("has no axe-core a11y violations", async ({ page }) => {
16+
const a11yScanResults = await new AxeBuilder({ page })
17+
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"])
18+
.analyze();
19+
20+
expect(a11yScanResults.violations).toEqual([]);
21+
});
22+
23+
test("has no axe-core a11y violations in dark mode", async ({ page }) => {
24+
await page.locator('[data-testId="theme-toggle"]').click();
25+
const a11yScanResults = await new AxeBuilder({ page })
26+
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"])
27+
.analyze();
28+
29+
expect(a11yScanResults.violations).toEqual([]);
30+
});
31+
32+
test("can select matching input with mouse", async ({ page }) => {
33+
await page.locator(basicTriggerSelector).type("aero");
34+
await page.locator(basicExampleFirstItemSelector).click()
35+
expect(page.locator(basicExampleSelectedId)).toHaveText("Selected id is 1")
36+
});
37+
});

apps/flourish-rapid/src/components/examplePages/AutocompletePage/index.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import ExampleGroup from '@/components/common-components/ExampleGroup'
22
import ExamplePage from '@/components/common-components/ExamplePage'
3-
import { Autocomplete, AutocompleteItem } from 'flourish-ui'
3+
import { Autocomplete, AutocompleteItem, Typography } from 'flourish-ui'
44
import { useState } from 'react'
55

66
const options = [
@@ -26,13 +26,14 @@ const AutocompletePage = () => {
2626
return (
2727
<ExamplePage exampleName="Autocomplete examples">
2828
<ExampleGroup groupName="Basic autocomplete">
29-
Selected id is {majorId}
29+
<Typography data-testId="basic-autocomplete-selected-id">Selected id is {majorId}</Typography>
3030
<Autocomplete
3131
label="Pick a major"
3232
onSelectionChange={onSelectionChange}
33+
data-testId="basic-autocomplete"
3334
>
3435
{options.map((option) => (
35-
<AutocompleteItem key={option.id} id={option.id}>
36+
<AutocompleteItem key={option.id} id={option.id} data-testId={`basic-autocomplete-item-${option.id}`}>
3637
{option.name}
3738
</AutocompleteItem>
3839
))}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
@import '../../index.scss';
2+
3+
$autocomplete_width: 200px;
4+
5+
.react-aria-ComboBox {
6+
max-width: max-content;
7+
}
8+
9+
.react-aria-Input {
10+
width: $autocomplete_width;
11+
padding: 5px calc($f-spacing-4 * 1px);
12+
background-color: $f-color-bg-light;
13+
border: 2px solid $f-color-primary-light;
14+
border-radius: calc($f-border-soft-corner * 1px);
15+
font:
16+
calc($f-typography-fs-h6 * 1px) $f-typography-font-primary,
17+
sans-serif;
18+
19+
&[data-focused] {
20+
outline: none;
21+
}
22+
23+
&[data-hovered] {
24+
color: $f-color-primary-light-hover;
25+
background-color: rgb(0, 77, 64, 0.1);
26+
border: 2px solid $f-color-primary-light-hover;
27+
}
28+
}
29+
30+
.react-aria-ListBox {
31+
width: $autocomplete_width;
32+
z-index: 1000;
33+
width: 200px;
34+
max-height: 150px;
35+
background-color: $f-color-bg-light;
36+
border: 2px solid $f-color-primary-light;
37+
border-radius: calc($f-border-soft-corner * 1px);
38+
overflow-y: scroll;
39+
scroll-behavior: smooth;
40+
}
41+
42+
.react-aria-ListBoxItem {
43+
cursor: pointer;
44+
color: $f-color-primary-light;
45+
font:
46+
calc($f-typography-fs-h6 * 1px) $f-typography-font-primary,
47+
sans-serif;
48+
background-color: $f-color-bg-light;
49+
padding: 5px calc($f-spacing-7 * 1px);
50+
51+
display: flex;
52+
flex-direction: row;
53+
justify-content: flex-start;
54+
align-items: center;
55+
flex-wrap: nowrap;
56+
57+
&[data-hovered] {
58+
color: $f-color-primary-light-hover;
59+
background-color: rgb(0, 77, 64, 0.1);
60+
}
61+
62+
&[data-selected] {
63+
color: $f-color-primary-light-hover;
64+
background-color: rgb(0, 77, 64, 0.1);
65+
}
66+
}
67+
68+
.#{$prefix}dark-mode {
69+
.react-aria-Input {
70+
color: $f-color-primary-dark;
71+
background-color: $f-color-bg-dark;
72+
border: 2px solid $f-color-primary-dark;
73+
74+
&[data-hovered] {
75+
color: $f-color-primary-dark-hover;
76+
background-color: rgb(100, 255, 218, 0.1);
77+
border: 2px solid $f-color-primary-dark-hover;
78+
}
79+
}
80+
81+
.react-aria-ListBox {
82+
color: $f-color-primary-dark;
83+
background-color: $f-color-bg-dark;
84+
border: 2px solid $f-color-primary-dark;
85+
}
86+
87+
.react-aria-ListBoxItem {
88+
color: $f-color-primary-dark;
89+
background-color: $f-color-bg-dark;
90+
91+
&[data-hovered] {
92+
color: $f-color-primary-dark-hover;
93+
background-color: rgb(100, 255, 218, 0.1);
94+
}
95+
96+
&[data-selected] {
97+
color: $f-color-primary-dark-hover;
98+
background-color: rgb(100, 255, 218, 0.1);
99+
}
100+
}
101+
}

packages/flourish-ui/src/components/Autocomplete/index.tsx

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,41 @@ import {
33
ComboBox,
44
ComboBoxProps,
55
Input,
6-
Label,
76
ListBox,
87
ListBoxItem,
98
ListBoxItemProps,
109
Popover
1110
} from 'react-aria-components'
11+
import { Testable } from '../../common-props'
1212
import './Autocomplete.scss'
1313

14-
interface AutocompleteProps<T extends object> extends ComboBoxProps<T> {
14+
export interface AutocompleteProps<T extends object>
15+
extends ComboBoxProps<T>,
16+
Testable {
1517
/** Accessible label for the component */
1618
label: string
1719
}
1820

19-
export const AutocompleteItem = (props: ListBoxItemProps) => {
20-
return <ListBoxItem {...props} />
21+
export interface AutocompleteItemProps extends ListBoxItemProps, Testable {
22+
23+
}
24+
25+
export const AutocompleteItem = ({
26+
'data-testId': testId,
27+
...props
28+
}: AutocompleteItemProps) => {
29+
return <ListBoxItem {...props} data-testId={testId} />
2130
}
2231

2332
export const Autocomplete = <T extends object>({
2433
label,
2534
children,
35+
'data-testId': testId,
2636
...props
2737
}: AutocompleteProps<T>) => {
2838
return (
29-
<ComboBox {...props}>
30-
<Label>{label}</Label>
31-
<Input />
39+
<ComboBox {...props} data-testId={testId}>
40+
<Input aria-label={label} data-testId={`${testId}-trigger`} />
3241
<Popover>
3342
<ListBox>{children}</ListBox>
3443
</Popover>

0 commit comments

Comments
 (0)