Skip to content

Commit 832806d

Browse files
committed
WIP: Add autocompletion
1 parent 8d3efd5 commit 832806d

File tree

5 files changed

+140
-27
lines changed

5 files changed

+140
-27
lines changed

components/search/forms/simple.jsx

+64-25
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,74 @@
1-
import React from 'react'
2-
import { Button, InputGroup, InputGroupAddon, Input } from 'reactstrap'
1+
import React, { useRef } from 'react'
2+
import { Select } from '@oacore/design'
33

4-
const SearchField = ({
5-
size = '',
6-
id = 'search-form-field',
7-
label = 'Search in CORE',
8-
...fieldProps
9-
}) => (
10-
<>
11-
<label className="sr-only" htmlFor={id}>
12-
{label}
13-
</label>
14-
<InputGroup size={size}>
15-
<Input type="search" id={id} {...fieldProps} />
16-
<InputGroupAddon addonType="append">
17-
<Button color="primary">Search</Button>
18-
</InputGroupAddon>
19-
</InputGroup>
20-
</>
21-
)
4+
import styles from './styles.module.css'
5+
6+
const options = [
7+
{ id: 1, icon: '#magnify', value: 'Option A' },
8+
{ id: 2, icon: '#magnify', value: 'Option B' },
9+
{ id: 3, icon: '#magnify', value: 'Option C' },
10+
{ id: 4, icon: '#magnify', value: 'Option D' },
11+
{ id: 5, icon: '#magnify', value: 'Option E' },
12+
]
13+
14+
const SearchAutocompletion = ({ formRef, ...passProps }) => {
15+
const [suggestions, setSuggestions] = React.useState(options)
16+
const [value, setValue] = React.useState('')
17+
18+
const handleOnChange = data => {
19+
if (data.value === '') return
20+
formRef.current.submit()
21+
}
22+
23+
const handleOnInput = data => {
24+
// if id doesn't exists it means user type own text
25+
// and didn't use suggestion
26+
if (!data.id) {
27+
setSuggestions(
28+
options.slice(0, Math.max(0, options.length - data.value.length))
29+
)
30+
}
31+
32+
setValue(data.value)
33+
}
34+
35+
return (
36+
<Select
37+
id="search-select"
38+
value={value}
39+
onChange={handleOnChange}
40+
onInput={handleOnInput}
41+
prependIcon="#magnify"
42+
className={styles['search-box']}
43+
{...passProps}
44+
>
45+
{suggestions.map(el => (
46+
<Select.Option key={el.id} id={el.id} value={el.value} icon={el.icon}>
47+
{el.value}
48+
</Select.Option>
49+
))}
50+
{
51+
<Select.Option key={6} id={6} value={value} icon="#magnify">
52+
{`All results for "${value}"`}
53+
</Select.Option>
54+
}
55+
</Select>
56+
)
57+
}
2258

2359
const SearchForm = ({
2460
action,
2561
method,
2662
onSubmit,
2763
id = 'search-form',
2864
...fieldProps
29-
}) => (
30-
<form id={id} action={action} method={method} onSubmit={onSubmit}>
31-
<SearchField id={`${id}-field`} size="lg" {...fieldProps} />
32-
</form>
33-
)
65+
}) => {
66+
const ref = useRef(null)
67+
return (
68+
<form ref={ref} id={id} action={action} method={method} onSubmit={onSubmit}>
69+
<SearchAutocompletion id={`${id}-field`} {...fieldProps} formRef={ref} />
70+
</form>
71+
)
72+
}
3473

3574
export default SearchForm
+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
.search-box {
2+
--form-control-corner-radius: 0.3rem;
3+
--form-control-color: var(--gray-500);
4+
--form-label-color: var(--gray-500);
5+
--select-option-color: var(--gray-700);
6+
--select-option-icon-color: var(--black);
7+
8+
border-bottom: 1px solid transparent;
9+
}
10+
11+
.search-box:focus-within,
12+
.search-box ul > * {
13+
padding-left: 0.25rem;
14+
padding-right: 0.25rem;
15+
}
16+
17+
.search-box:focus-within {
18+
--form-control-color: var(--primary);
19+
}
20+
21+
.search-box:focus-within div:nth-child(2) {
22+
border-bottom: 1px solid var(--primary);
23+
}
24+
25+
.search-box:focus-within {
26+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05), 0 1px 2px rgba(0, 0, 0, 0.25);
27+
}
28+
29+
.search-box:focus-within div:nth-child(2) > * {
30+
border-color: transparent;
31+
}
32+
33+
.search-box:focus-within ul {
34+
color: var(--gray-500)
35+
}
36+
37+
.search-box:focus-within ul > li:last-child {
38+
position: relative;
39+
}
40+
.search-box:focus-within ul > li:last-child::after {
41+
content: '';
42+
left: 0.25rem;
43+
right: 0.25rem;
44+
position: absolute;
45+
border-top: 1px solid var(--gray-300);
46+
}

design.config.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
const path = require('path')
2+
3+
const icons = ['magnify']
4+
5+
const iconsRoot = path.join(
6+
path.dirname(require.resolve('@mdi/svg/package.json')),
7+
'./svg'
8+
)
9+
10+
const config = {
11+
icons: {
12+
path: iconsRoot,
13+
files: icons,
14+
},
15+
16+
output: {
17+
path: path.join(__dirname, 'public/design'),
18+
publicPath: '/design',
19+
icons: {
20+
files: 'icons',
21+
sprite: 'icons.svg',
22+
},
23+
},
24+
}
25+
26+
module.exports = config

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
"test-staging": "blc https://${CORE_STAGING_AREA}core.ac.uk -ro --exclude='/browse' --exclude='/search' --exclude='/public'",
7171
"test": "npm run test-dev",
7272
"dev": "next",
73-
"build": "next build",
73+
"build": "node ./node_modules/.bin/design build icons && next build",
7474
"export": "next build && next export",
7575
"start": "next start"
7676
}

pages/index.jsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ const IndexPage = () => (
8585
<SearchForm
8686
action="/search"
8787
name="q"
88-
placeholder={patchStats(page.searchPlaceholder, page.statistics)}
88+
label={patchStats(page.searchPlaceholder, page.statistics)}
89+
placeholder="e.g. article title or author name"
90+
variant="pure"
8991
/>
9092
<SearchIntro>
9193
<Markdown>{page.covid19Notice}</Markdown>

0 commit comments

Comments
 (0)