Skip to content

Commit ca89a03

Browse files
authored
Merge pull request #2358 from dxc-technology/PelayoFelgueroso/select-search-options
Add searchable select with 'startsWith' mode and update filtering logic
2 parents 3a1e3cd + 87f1f0d commit ca89a03

File tree

5 files changed

+70
-5
lines changed

5 files changed

+70
-5
lines changed

apps/website/screens/components/select/code/SelectCodePage.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,22 @@ const sections = [
255255
<TableCode>false</TableCode>
256256
</td>
257257
</tr>
258+
<tr>
259+
<td>
260+
<DxcFlex direction="column" gap="var(--spacing-gap-xs)" alignItems="baseline">
261+
<StatusBadge status="new" />
262+
searchByStartsWith
263+
</DxcFlex>
264+
</td>
265+
<td>
266+
<TableCode>boolean</TableCode>
267+
</td>
268+
<td>
269+
Defines the search mode when searchable is true. If true, matches options that start with the search text.
270+
If false, matches options that contain the search text anywhere in their label.
271+
</td>
272+
<td>false</td>
273+
</tr>
258274
<tr>
259275
<td>size</td>
260276
<td>

packages/lib/src/select/Select.stories.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ const singleOptions = [
3535
{ label: "Option 04", value: "4" },
3636
];
3737

38+
const startsWithSingleOptions = [
39+
{ label: "Option 01", value: "1" },
40+
{ label: "This is option 02", value: "2" },
41+
{ label: "Is option 03", value: "3" },
42+
{ label: "And Option 04", value: "4" },
43+
];
44+
3845
const single_options_virtualized = [
3946
...Array.from({ length: 10000 }, (_, i) => ({
4047
label: `Option ${String(i + 1).padStart(2, "0")}`,
@@ -593,6 +600,19 @@ const SearchableSelect = () => (
593600
</ExampleContainer>
594601
);
595602

603+
const startsWithSearchableSelect = () => (
604+
<ExampleContainer expanded>
605+
<Title title="Searchable contains select" theme="light" level={4} />
606+
<DxcSelect
607+
label="Select Label"
608+
searchable
609+
searchByStartsWith
610+
options={startsWithSingleOptions}
611+
placeholder="Choose an option"
612+
/>
613+
</ExampleContainer>
614+
);
615+
596616
const SearchValue = () => (
597617
<ExampleContainer expanded>
598618
<Title title="Searchable select with value" theme="light" level={4} />
@@ -740,6 +760,14 @@ export const Searchable: Story = {
740760
},
741761
};
742762

763+
export const StartsWithSearchable: Story = {
764+
render: startsWithSearchableSelect,
765+
play: async ({ canvasElement }) => {
766+
const canvas = within(canvasElement);
767+
await userEvent.type(await canvas.findByRole("combobox"), "t");
768+
},
769+
};
770+
743771
export const SearchableWithValue: Story = {
744772
render: SearchValue,
745773
play: async ({ canvasElement }) => {

packages/lib/src/select/Select.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ const DxcSelect = forwardRef<RefType, SelectPropsType>(
191191
options,
192192
placeholder = "",
193193
searchable = false,
194+
searchByStartsWith = false,
194195
size = "medium",
195196
tabIndex = 0,
196197
value,
@@ -217,7 +218,10 @@ const DxcSelect = forwardRef<RefType, SelectPropsType>(
217218
const translatedLabels = useContext(HalstackLanguageContext);
218219

219220
const optionalItem = useMemo(() => ({ label: placeholder, value: "" }), [placeholder]);
220-
const filteredOptions = useMemo(() => filterOptionsBySearchValue(options, searchValue), [options, searchValue]);
221+
const filteredOptions = useMemo(
222+
() => filterOptionsBySearchValue(options, searchValue, searchByStartsWith),
223+
[options, searchValue, searchByStartsWith]
224+
);
221225
const lastOptionIndex = useMemo(
222226
() => getLastOptionIndex(options, filteredOptions, searchable, optional, multiple, enableSelectAll),
223227
[options, filteredOptions, searchable, optional, multiple, enableSelectAll]

packages/lib/src/select/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ type CommonProps = {
9191
* If true, enables search functionality.
9292
*/
9393
searchable?: boolean;
94+
/**
95+
* Defines the search mode when searchable is true.
96+
* If true, matches options that start with the search text.
97+
* If false, matches options that contain the search text anywhere in their label.
98+
*/
99+
searchByStartsWith?: boolean;
94100
/**
95101
* Size of the component.
96102
*/

packages/lib/src/select/utils.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,20 +51,31 @@ export const canOpenListbox = (options: Props["options"], disabled: boolean) =>
5151
/**
5252
* Filters the options by the search value.
5353
*/
54-
export const filterOptionsBySearchValue = (options: Props["options"], searchValue: string): Props["options"] =>
55-
options.length > 0
54+
export const filterOptionsBySearchValue = (
55+
options: Props["options"],
56+
searchValue: string,
57+
searchByStartsWith: Props["searchByStartsWith"] = false
58+
): Props["options"] => {
59+
const matchesSearch = (label: string, search: string, searchByStartsWith: boolean) => {
60+
const upperLabel = label.toUpperCase();
61+
const upperSearch = search.toUpperCase();
62+
return searchByStartsWith ? upperLabel.startsWith(upperSearch) : upperLabel.includes(upperSearch);
63+
};
64+
65+
return options.length > 0
5666
? isArrayOfGroupedOptions(options)
5767
? options.map((optionGroup) => {
5868
const group = {
5969
label: optionGroup.label,
6070
options: optionGroup.options.filter((option) =>
61-
option.label.toUpperCase().includes(searchValue.toUpperCase())
71+
matchesSearch(option.label, searchValue, searchByStartsWith)
6272
),
6373
};
6474
return group;
6575
})
66-
: options.filter((option) => option.label.toUpperCase().includes(searchValue.toUpperCase()))
76+
: options.filter((option) => matchesSearch(option.label, searchValue, searchByStartsWith))
6777
: [];
78+
};
6879

6980
/**
7081
* Returns the index of the last option, depending on several conditions.

0 commit comments

Comments
 (0)