Skip to content

Commit f7277ba

Browse files
Add search input component
1 parent 24fbc14 commit f7277ba

File tree

10 files changed

+402
-0
lines changed

10 files changed

+402
-0
lines changed

packages/nhsuk-frontend-review/src/layouts/page.njk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
{% from "nhsuk/components/panel/macro.njk" import panel %}
3232
{% from "nhsuk/components/password-input/macro.njk" import passwordInput %}
3333
{% from "nhsuk/components/radios/macro.njk" import radios %}
34+
{% from "nhsuk/components/search-input/macro.njk" import searchInput %}
3435
{% from "nhsuk/components/select/macro.njk" import select %}
3536
{% from "nhsuk/components/skip-link/macro.njk" import skipLink %}
3637
{% from "nhsuk/components/summary-list/macro.njk" import summaryList %}

packages/nhsuk-frontend/src/nhsuk/components/_index.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
@forward "checkboxes";
2929
@forward "password-input";
3030
@forward "radios";
31+
@forward "search-input";
3132
@forward "select";
3233
@forward "skip-link";
3334
@forward "summary-list";
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Search input
2+
3+
## Installation
4+
5+
See the [main README quick start guide](https://github.com/nhsuk/nhsuk-frontend#quick-start) for how to install this component.
6+
7+
## Guidance and examples
8+
9+
To learn more about the search input component and when to use it, visit the [design system in the NHS digital service manual](https://service-manual.nhs.uk/design-system/components/search-input) for guidance, examples and options.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
@use "../../core/settings" as *;
2+
@use "../../core/tools" as *;
3+
@use "../../core/helpers" as *;
4+
@forward "../button";
5+
@forward "../input";
6+
7+
////
8+
/// Search input component
9+
///
10+
/// @group components/search-input
11+
////
12+
13+
@include nhsuk-exports("nhsuk/components/search-input") {
14+
.nhsuk-search-input__input {
15+
&[type="search"]::-webkit-search-cancel-button {
16+
width: nhsuk-px-to-rem(28px);
17+
height: nhsuk-px-to-rem(28px);
18+
margin: 0;
19+
20+
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23212b32'><path d='M13.41 12l5.3-5.29a1 1 0 1 0-1.42-1.42L12 10.59l-5.29-5.3a1 1 0 0 0-1.42 1.42l5.3 5.29-5.3 5.29a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l5.29-5.3 5.29 5.3a1 1 0 0 0 1.42 0 1 1 0 0 0 0-1.42z'/></svg>");
21+
22+
cursor: pointer;
23+
24+
appearance: none;
25+
}
26+
}
27+
28+
@include nhsuk-media-query($from: mobile) {
29+
.nhsuk-search-input__input {
30+
margin-right: -$nhsuk-border-width-form-element;
31+
}
32+
33+
// Remove border radius from left side
34+
.nhsuk-search-input__button,
35+
.nhsuk-search-input__button::before,
36+
.nhsuk-search-input__button::after {
37+
border-radius: 0 $nhsuk-border-radius $nhsuk-border-radius 0;
38+
}
39+
40+
// Remove inner box shadow from left side
41+
.nhsuk-search-input__button:not(:focus)::after {
42+
left: -2px;
43+
}
44+
45+
// Remove margin from left side
46+
.nhsuk-input-wrapper .nhsuk-search-input__button {
47+
margin-left: 0;
48+
49+
// Prevent height change when pressed
50+
&:active,
51+
&:active:focus {
52+
top: 0;
53+
min-height: nhsuk-px-to-rem(36px + $nhsuk-button-shadow-size);
54+
margin-bottom: 0;
55+
}
56+
}
57+
}
58+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@forward ".";
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import {
2+
axe,
3+
getPage,
4+
getOptions,
5+
goToComponent
6+
} from '@nhsuk/frontend-helpers/puppeteer.mjs'
7+
8+
import { examples } from './fixtures.mjs'
9+
10+
describe('Search input', () => {
11+
/** @type {Page} */
12+
let page
13+
14+
beforeAll(async () => {
15+
page = await getPage(browser)
16+
})
17+
18+
describe.each(Object.entries(examples))('%s', (name, example) => {
19+
it.each(getOptions(name, example))(
20+
'$title passes accessibility tests',
21+
async (options) => {
22+
await goToComponent(page, 'search-input', options)
23+
return expect(axe(page)).resolves.toHaveNoViolations()
24+
}
25+
)
26+
})
27+
})
28+
29+
/**
30+
* @import { Page } from 'puppeteer'
31+
*/
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
* Nunjucks macro option examples
3+
*
4+
* @satisfies {{ [example: string]: MacroExample }}
5+
*/
6+
export const examples = {
7+
'default': {
8+
context: {
9+
label: {
10+
text: 'Search patients by NHS number',
11+
classes: 'nhsuk-u-visually-hidden'
12+
},
13+
placeholder: 'NHS number',
14+
name: 'example',
15+
classes: 'nhsuk-input--width-10',
16+
inputmode: 'numeric'
17+
}
18+
},
19+
'with hint': {
20+
context: {
21+
label: {
22+
text: 'Search patients by NHS number',
23+
classes: 'nhsuk-u-visually-hidden'
24+
},
25+
hint: {
26+
text: 'Your NHS number is a 10 digit number that you find on any letter the NHS has sent you, for example, 485 777 3456'
27+
},
28+
placeholder: 'NHS number',
29+
id: 'with-hint',
30+
name: 'example',
31+
classes: 'nhsuk-input--width-10',
32+
inputmode: 'numeric'
33+
}
34+
},
35+
'with button text': {
36+
context: {
37+
label: {
38+
text: 'Search patients by NHS number',
39+
classes: 'nhsuk-u-visually-hidden'
40+
},
41+
button: {
42+
text: 'Search',
43+
visuallyHiddenText: ''
44+
},
45+
placeholder: 'NHS number',
46+
name: 'example',
47+
classes: 'nhsuk-input--width-10',
48+
inputmode: 'numeric'
49+
}
50+
},
51+
'with button secondary': {
52+
context: {
53+
label: {
54+
text: 'Search patients by NHS number',
55+
classes: 'nhsuk-u-visually-hidden'
56+
},
57+
button: {
58+
classes: 'nhsuk-button--secondary'
59+
},
60+
placeholder: 'NHS number',
61+
name: 'example',
62+
classes: 'nhsuk-input--width-10',
63+
inputmode: 'numeric'
64+
}
65+
},
66+
'with button secondary, solid background': {
67+
context: {
68+
label: {
69+
text: 'Search patients by NHS number',
70+
classes: 'nhsuk-u-visually-hidden'
71+
},
72+
button: {
73+
classes: 'nhsuk-button--secondary-solid'
74+
},
75+
placeholder: 'NHS number',
76+
name: 'example',
77+
classes: 'nhsuk-input--width-10',
78+
inputmode: 'numeric'
79+
},
80+
options: {
81+
layout: 'background-grey'
82+
}
83+
}
84+
}
85+
86+
/**
87+
* @import { MacroExample } from '@nhsuk/frontend-lib/components.mjs'
88+
*/
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
export const name = 'Search input'
2+
3+
/**
4+
* Nunjucks macro option params
5+
*
6+
* @satisfies {{ [param: string]: MacroParam }}
7+
*/
8+
export const params = {
9+
id: {
10+
type: 'string',
11+
required: false,
12+
description: 'The ID of the input. Defaults to the value of `name`.'
13+
},
14+
name: {
15+
type: 'string',
16+
required: true,
17+
description: 'The name of the input, which is submitted with the form data.'
18+
},
19+
value: {
20+
type: 'string',
21+
required: false,
22+
description: 'Optional initial value of the input.'
23+
},
24+
disabled: {
25+
type: 'boolean',
26+
required: false,
27+
description: 'If `true`, input will be disabled.'
28+
},
29+
describedBy: {
30+
type: 'string',
31+
required: false,
32+
description:
33+
'One or more element IDs to add to the `aria-describedby` attribute, used to provide additional descriptive information for screenreader users.'
34+
},
35+
label: {
36+
type: 'object',
37+
required: true,
38+
description: 'The label used by the text input component.',
39+
isComponent: true
40+
},
41+
hint: {
42+
type: 'object',
43+
required: false,
44+
description: 'Can be used to add a hint to a text input component.',
45+
isComponent: true
46+
},
47+
errorMessage: {
48+
type: 'object',
49+
required: false,
50+
description:
51+
'Can be used to add an error message to the text input component. The error message component will not display if you use a falsy value for `errorMessage`, for example `false` or `null`.',
52+
isComponent: true
53+
},
54+
formGroup: {
55+
type: 'object',
56+
required: false,
57+
description:
58+
'Additional options for the form group containing the text input component.',
59+
params: {
60+
classes: {
61+
type: 'string',
62+
required: false,
63+
description:
64+
'Classes to add to the form group (for example to show error state for the whole group).'
65+
},
66+
attributes: {
67+
type: 'object',
68+
required: false,
69+
description:
70+
'HTML attributes (for example data attributes) to add to the form group.'
71+
},
72+
beforeInput: {
73+
type: 'object',
74+
required: false,
75+
description:
76+
'Content to add before the input used by the text input component.',
77+
params: {
78+
text: {
79+
type: 'string',
80+
required: true,
81+
description:
82+
'Text to add before the input. If `html` is provided, the `text` option will be ignored.'
83+
},
84+
html: {
85+
type: 'string',
86+
required: true,
87+
description:
88+
'HTML to add before the input. If `html` is provided, the `text` option will be ignored.'
89+
}
90+
}
91+
},
92+
afterInput: {
93+
type: 'object',
94+
required: false,
95+
description:
96+
'Content to add after the input used by the text input component.',
97+
params: {
98+
text: {
99+
type: 'string',
100+
required: true,
101+
description:
102+
'Text to add after the input. If `html` is provided, the `text` option will be ignored.'
103+
},
104+
html: {
105+
type: 'string',
106+
required: true,
107+
description:
108+
'HTML to add after the input. If `html` is provided, the `text` option will be ignored.'
109+
}
110+
}
111+
}
112+
}
113+
},
114+
classes: {
115+
type: 'string',
116+
required: false,
117+
description: 'Classes to add to the input.'
118+
},
119+
autocomplete: {
120+
type: 'string',
121+
required: false,
122+
description:
123+
'Attribute to meet [WCAG success criterion 1.3.5: Identify input purpose](https://www.w3.org/WAI/WCAG22/Understanding/identify-input-purpose.html), for instance `"bday-day"`. See the [Autofill section in the HTML standard](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill) for a full list of attributes that can be used. Default is `"off"`.'
124+
},
125+
pattern: {
126+
type: 'string',
127+
required: false,
128+
description:
129+
'Attribute to provide a regular expression pattern, used to match allowed character combinations for the input value.'
130+
},
131+
attributes: {
132+
type: 'object',
133+
required: false,
134+
description:
135+
'HTML attributes (for example data attributes) to add to the input.'
136+
},
137+
button: {
138+
type: 'object',
139+
required: false,
140+
description: 'Optional object allowing customisation of the search button.',
141+
params: {
142+
classes: {
143+
type: 'string',
144+
required: false,
145+
description: 'Classes to add to the search button.'
146+
},
147+
visuallyHiddenText: {
148+
type: 'string',
149+
required: false,
150+
description:
151+
'Visually hidden text for the search button icon. Defaults to `"Search"`.'
152+
}
153+
}
154+
}
155+
}
156+
157+
/**
158+
* @import { MacroParam } from '@nhsuk/frontend-lib/components.mjs'
159+
*/
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{% macro searchInput(params) %}
2+
{%- include "./template.njk" -%}
3+
{% endmacro %}

0 commit comments

Comments
 (0)