Skip to content
This repository was archived by the owner on Mar 25, 2025. It is now read-only.

Commit 5a5b946

Browse files
committed
feat(searchselect): ux improvements
- Shows the label of the selected value instead of the value - Select all text on focus - Revert to label for current value on blur - Highlight the searched string in the option list so the user can identify why that option matched. - Updates selected value if it is changed from the outside
1 parent 1b4ebbf commit 5a5b946

File tree

7 files changed

+119
-61
lines changed

7 files changed

+119
-61
lines changed

packages/core/src/Input/__snapshots__/index.test.tsx.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4604,7 +4604,7 @@ exports[`TextInput TimeInput 1`] = `
46044604
background-color: rgb(245,245,245);
46054605
}
46064606
4607-
.c10:focus {
4607+
.c10:focus-within {
46084608
outline: none;
46094609
background-color: transparent;
46104610
border: 2px solid rgb(69,39,160);

packages/core/src/Select/BaseSelectSelector.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ const SelectInput = styled.div<SelectInputProps>`
8585
: undefined}
8686
}
8787
88-
&:focus {
88+
&:focus-within {
8989
outline: none;
9090
9191
${({ openedFocus, visibleFocus, variant, theme }) =>

packages/core/src/Select/SearchSelect.tsx

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import React, { useMemo, useState, useCallback } from 'react'
1+
import React, { useMemo, useState, useCallback, useRef, useEffect } from 'react'
22
import styled from 'styled-components'
33

44
import { useBoolean } from 'react-hooks-shareable'
55

66
import { BaseSelect } from './BaseSelect'
7-
import { SelectProps } from './Select'
7+
import { SelectProps, Option } from './Select'
88
import { FieldProps, withField } from '../utils/withField'
99

1010
const InputNative = styled.input`
@@ -26,16 +26,41 @@ const InputNative = styled.input`
2626
}
2727
`
2828

29+
const getLabel = <V extends string = string>(
30+
value: V,
31+
options: ReadonlyArray<Option<V>>
32+
): string => {
33+
return options.find(option => option.value === value)?.label ?? value
34+
}
35+
36+
const getLabelComponent = (label: string, filter: string): React.ReactNode => {
37+
const offset = label.toLocaleLowerCase().indexOf(filter.toLocaleLowerCase())
38+
39+
if (offset === -1) {
40+
return label
41+
}
42+
43+
return (
44+
<span>
45+
{label.substring(0, offset)}
46+
<strong>{label.substring(offset, offset + filter.length)}</strong>
47+
{label.substring(offset + filter.length)}
48+
</span>
49+
)
50+
}
51+
2952
export function SearchSelect<V extends string = string>({
3053
value: currentValue,
3154
options,
3255
onChange,
3356
placeholder,
3457
...props
3558
}: SelectProps<V>): JSX.Element {
36-
const [filter, setFilter] = useState('')
59+
const [filter, setFilter] = useState<string>(currentValue)
3760
const [isTyping, startTyping, stopTyping] = useBoolean(false)
3861

62+
const inputRef = useRef<HTMLInputElement>(null)
63+
3964
const filterLabel = useCallback(
4065
(text: string) =>
4166
text.toLocaleLowerCase().includes(filter.toLocaleLowerCase()),
@@ -46,17 +71,17 @@ export function SearchSelect<V extends string = string>({
4671
return options
4772
.filter(({ label }) => !isTyping || filterLabel(label))
4873
.map(({ value, disabled, label }) => {
49-
return { value, disabled, component: label }
74+
return { value, disabled, component: getLabelComponent(label, filter) }
5075
})
51-
}, [options, filterLabel, isTyping])
76+
}, [options, isTyping, filterLabel, filter])
5277

5378
const onSelectChange = useCallback(
54-
value => {
55-
setFilter(value)
56-
onChange?.(value)
79+
nextValue => {
80+
setFilter(getLabel(nextValue, options) ?? nextValue)
81+
onChange?.(nextValue)
5782
stopTyping()
5883
},
59-
[onChange, stopTyping]
84+
[onChange, options, stopTyping]
6085
)
6186

6287
const onFilterChange = useCallback(
@@ -67,10 +92,25 @@ export function SearchSelect<V extends string = string>({
6792
[startTyping]
6893
)
6994

95+
const selectInputText = useCallback(() => inputRef.current?.select(), [])
96+
97+
const resetInputText = useCallback(
98+
() => setFilter(getLabel(currentValue, options)),
99+
[currentValue, options]
100+
)
101+
102+
useEffect(() => setFilter(getLabel(currentValue, options)), [
103+
currentValue,
104+
options,
105+
])
106+
70107
const input = (
71108
<InputNative
109+
ref={inputRef}
72110
value={filter}
73111
onChange={onFilterChange}
112+
onFocus={selectInputText}
113+
onBlur={resetInputText}
74114
placeholder={placeholder}
75115
/>
76116
)

packages/core/src/Select/__snapshots__/MultiSelect.test.tsx.snap

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ exports[`Select Direction 1`] = `
120120
background-color: rgb(240,240,240);
121121
}
122122
123-
.c3:focus {
123+
.c3:focus-within {
124124
outline: none;
125125
background-color: rgb(240,240,240);
126126
border: 2px solid rgb(69,39,160);
@@ -519,7 +519,7 @@ exports[`Select Direction 2`] = `
519519
background-color: rgb(240,240,240);
520520
}
521521
522-
.c3:focus {
522+
.c3:focus-within {
523523
outline: none;
524524
background-color: rgb(240,240,240);
525525
border: 2px solid rgb(69,39,160);
@@ -918,7 +918,7 @@ exports[`Select Other 1`] = `
918918
background-color: rgb(240,240,240);
919919
}
920920
921-
.c3:focus {
921+
.c3:focus-within {
922922
outline: none;
923923
background-color: rgb(240,240,240);
924924
border: 2px solid rgb(69,39,160);
@@ -1330,7 +1330,7 @@ exports[`Select Other 2`] = `
13301330
background-color: rgb(240,240,240);
13311331
}
13321332
1333-
.c3:focus {
1333+
.c3:focus-within {
13341334
outline: none;
13351335
background-color: rgb(240,240,240);
13361336
border: 2px solid rgb(69,39,160);
@@ -1729,7 +1729,7 @@ exports[`Select Width 1`] = `
17291729
background-color: rgb(240,240,240);
17301730
}
17311731
1732-
.c3:focus {
1732+
.c3:focus-within {
17331733
outline: none;
17341734
background-color: rgb(240,240,240);
17351735
border: 2px solid rgb(69,39,160);
@@ -2128,7 +2128,7 @@ exports[`Select Width 2`] = `
21282128
background-color: rgb(240,240,240);
21292129
}
21302130
2131-
.c3:focus {
2131+
.c3:focus-within {
21322132
outline: none;
21332133
background-color: rgb(240,240,240);
21342134
border: 2px solid rgb(69,39,160);
@@ -2559,7 +2559,7 @@ exports[`Select Width 3`] = `
25592559
background-color: rgb(240,240,240);
25602560
}
25612561
2562-
.c3:focus {
2562+
.c3:focus-within {
25632563
outline: none;
25642564
background-color: rgb(240,240,240);
25652565
border: 2px solid rgb(69,39,160);

packages/core/src/Select/__snapshots__/SearchSelect.test.tsx.snap

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ exports[`Select Direction 1`] = `
8585
background-color: rgb(240,240,240);
8686
}
8787
88-
.c3:focus {
88+
.c3:focus-within {
8989
outline: none;
9090
background-color: rgb(240,240,240);
9191
border: 2px solid rgb(69,39,160);
@@ -184,8 +184,10 @@ exports[`Select Direction 1`] = `
184184
>
185185
<input
186186
className="c6"
187+
onBlur={[Function]}
187188
onChange={[Function]}
188-
value=""
189+
onFocus={[Function]}
190+
value="test1"
189191
/>
190192
<span
191193
className="c7"
@@ -310,7 +312,7 @@ exports[`Select Direction 2`] = `
310312
background-color: rgb(240,240,240);
311313
}
312314
313-
.c3:focus {
315+
.c3:focus-within {
314316
outline: none;
315317
background-color: rgb(240,240,240);
316318
border: 2px solid rgb(69,39,160);
@@ -409,8 +411,10 @@ exports[`Select Direction 2`] = `
409411
>
410412
<input
411413
className="c6"
414+
onBlur={[Function]}
412415
onChange={[Function]}
413-
value=""
416+
onFocus={[Function]}
417+
value="test1"
414418
/>
415419
<span
416420
className="c7"
@@ -535,7 +539,7 @@ exports[`Select Other 1`] = `
535539
background-color: rgb(240,240,240);
536540
}
537541
538-
.c3:focus {
542+
.c3:focus-within {
539543
outline: none;
540544
background-color: rgb(240,240,240);
541545
border: 2px solid rgb(69,39,160);
@@ -641,8 +645,10 @@ exports[`Select Other 1`] = `
641645
>
642646
<input
643647
className="c6"
648+
onBlur={[Function]}
644649
onChange={[Function]}
645-
value=""
650+
onFocus={[Function]}
651+
value="test1"
646652
/>
647653
<span
648654
className="c7"
@@ -773,7 +779,7 @@ exports[`Select Other 2`] = `
773779
background-color: rgb(240,240,240);
774780
}
775781
776-
.c3:focus {
782+
.c3:focus-within {
777783
outline: none;
778784
background-color: rgb(240,240,240);
779785
border: 2px solid rgb(69,39,160);
@@ -872,8 +878,10 @@ exports[`Select Other 2`] = `
872878
>
873879
<input
874880
className="c6"
881+
onBlur={[Function]}
875882
onChange={[Function]}
876-
value=""
883+
onFocus={[Function]}
884+
value="test1"
877885
/>
878886
<span
879887
className="c7"
@@ -998,7 +1006,7 @@ exports[`Select SelectMarker 1`] = `
9981006
background-color: rgb(240,240,240);
9991007
}
10001008
1001-
.c3:focus {
1009+
.c3:focus-within {
10021010
outline: none;
10031011
background-color: rgb(240,240,240);
10041012
border: 2px solid rgb(69,39,160);
@@ -1097,8 +1105,10 @@ exports[`Select SelectMarker 1`] = `
10971105
>
10981106
<input
10991107
className="c6"
1108+
onBlur={[Function]}
11001109
onChange={[Function]}
1101-
value=""
1110+
onFocus={[Function]}
1111+
value="test1"
11021112
/>
11031113
<span
11041114
className="c7"
@@ -1223,7 +1233,7 @@ exports[`Select SelectMarker 2`] = `
12231233
background-color: rgb(240,240,240);
12241234
}
12251235
1226-
.c3:focus {
1236+
.c3:focus-within {
12271237
outline: none;
12281238
background-color: rgb(240,240,240);
12291239
border: 2px solid rgb(69,39,160);
@@ -1322,8 +1332,10 @@ exports[`Select SelectMarker 2`] = `
13221332
>
13231333
<input
13241334
className="c6"
1335+
onBlur={[Function]}
13251336
onChange={[Function]}
1326-
value=""
1337+
onFocus={[Function]}
1338+
value="test1"
13271339
/>
13281340
<span
13291341
className="c7"
@@ -1448,7 +1460,7 @@ exports[`Select Width 1`] = `
14481460
background-color: rgb(240,240,240);
14491461
}
14501462
1451-
.c3:focus {
1463+
.c3:focus-within {
14521464
outline: none;
14531465
background-color: rgb(240,240,240);
14541466
border: 2px solid rgb(69,39,160);
@@ -1547,8 +1559,10 @@ exports[`Select Width 1`] = `
15471559
>
15481560
<input
15491561
className="c6"
1562+
onBlur={[Function]}
15501563
onChange={[Function]}
1551-
value=""
1564+
onFocus={[Function]}
1565+
value="test1"
15521566
/>
15531567
<span
15541568
className="c7"
@@ -1673,7 +1687,7 @@ exports[`Select Width 2`] = `
16731687
background-color: rgb(240,240,240);
16741688
}
16751689
1676-
.c3:focus {
1690+
.c3:focus-within {
16771691
outline: none;
16781692
background-color: rgb(240,240,240);
16791693
border: 2px solid rgb(69,39,160);
@@ -1772,8 +1786,10 @@ exports[`Select Width 2`] = `
17721786
>
17731787
<input
17741788
className="c6"
1789+
onBlur={[Function]}
17751790
onChange={[Function]}
1776-
value=""
1791+
onFocus={[Function]}
1792+
value="test2"
17771793
/>
17781794
<span
17791795
className="c7"
@@ -1898,7 +1914,7 @@ exports[`Select Width 3`] = `
18981914
background-color: rgb(240,240,240);
18991915
}
19001916
1901-
.c3:focus {
1917+
.c3:focus-within {
19021918
outline: none;
19031919
background-color: rgb(240,240,240);
19041920
border: 2px solid rgb(69,39,160);
@@ -1997,8 +2013,10 @@ exports[`Select Width 3`] = `
19972013
>
19982014
<input
19992015
className="c6"
2016+
onBlur={[Function]}
20002017
onChange={[Function]}
2001-
value=""
2018+
onFocus={[Function]}
2019+
value="test1"
20022020
/>
20032021
<span
20042022
className="c7"

0 commit comments

Comments
 (0)