Skip to content

Commit 10ad4f1

Browse files
fix(fuselage, regression): Select filter and anchor overhaul (#654)
1 parent 874604a commit 10ad4f1

File tree

4 files changed

+128
-75
lines changed

4 files changed

+128
-75
lines changed

packages/fuselage/src/components/Select/Select.tsx

+30-12
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,16 @@ import {
33
useMutableCallback,
44
useResizeObserver,
55
} from '@rocket.chat/fuselage-hooks';
6-
import type { ComponentProps, DependencyList, Ref, ElementType } from 'react';
6+
import type {
7+
ComponentProps,
8+
DependencyList,
9+
Ref,
10+
ElementType,
11+
ReactNode,
12+
} from 'react';
713
import React, { useState, useRef, useEffect, forwardRef, useMemo } from 'react';
814

15+
import { isForwardRefType } from '../../helpers/isForwardRefType';
916
import AnimatedVisibility from '../AnimatedVisibility';
1017
import { Box } from '../Box';
1118
import { Icon } from '../Icon';
@@ -14,6 +21,7 @@ import type { OptionType } from '../Options';
1421
import { Options, useCursor } from '../Options';
1522
import PositionAnimated from '../PositionAnimated';
1623
import SelectAddon from './SelectAddon';
24+
import type { SelectAnchorParams } from './SelectAnchorParams';
1725
import SelectFocus from './SelectFocus';
1826

1927
export type SelectOption = readonly [
@@ -116,6 +124,18 @@ export const Select = forwardRef(
116124
const innerRef = useRef<HTMLInputElement | null>(null);
117125
const anchorRef = useMergedRefs(ref, innerRef);
118126

127+
const renderAnchor = (params: SelectAnchorParams) => {
128+
if (isForwardRefType(Anchor)) {
129+
return <Anchor {...params} />;
130+
}
131+
132+
if (typeof Anchor === 'function') {
133+
return (Anchor as (params: SelectAnchorParams) => ReactNode)(params);
134+
}
135+
136+
return null;
137+
};
138+
119139
const { ref: containerRef, borderBoxSize } = useResizeObserver();
120140

121141
useDidUpdate(reset, [filter, internalValue]);
@@ -171,17 +191,15 @@ export const Select = forwardRef(
171191
{visibleText}
172192
</Box>
173193
))}
174-
<Anchor
175-
disabled={disabled}
176-
rcx-input-box--undecorated
177-
filter={filter}
178-
ref={anchorRef}
179-
aria-haspopup='listbox'
180-
onClick={show}
181-
onBlur={hide}
182-
onKeyUp={handleKeyUp}
183-
onKeyDown={handleKeyDown}
184-
/>
194+
{renderAnchor({
195+
ref: anchorRef,
196+
children: !value ? option || placeholder : null,
197+
disabled: disabled ?? false,
198+
onClick: show,
199+
onBlur: hide,
200+
onKeyDown: handleKeyDown,
201+
onKeyUp: handleKeyUp,
202+
})}
185203
<Margins inline='x4'>
186204
<SelectAddon
187205
children={
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import type {
2+
FocusEventHandler,
3+
KeyboardEventHandler,
4+
MouseEventHandler,
5+
ReactNode,
6+
Ref,
7+
} from 'react';
8+
9+
export type SelectAnchorParams = {
10+
ref: Ref<Element>;
11+
children: ReactNode;
12+
disabled: boolean;
13+
onClick: MouseEventHandler;
14+
onBlur: FocusEventHandler;
15+
onKeyUp: KeyboardEventHandler;
16+
onKeyDown: KeyboardEventHandler;
17+
};
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,40 @@
1-
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
2-
import type {
3-
ComponentProps,
4-
Dispatch,
5-
FormEvent,
6-
Ref,
7-
SetStateAction,
8-
} from 'react';
9-
import React, { forwardRef, useCallback, useState } from 'react';
1+
import type { ComponentProps, Dispatch, SetStateAction } from 'react';
2+
import React, { useState } from 'react';
103

114
import { Select } from '.';
125
import type { Icon } from '..';
13-
import { InputBox } from '../InputBox';
6+
import type { SelectAnchorParams } from './SelectAnchorParams';
7+
import SelectFilteredAnchor from './SelectFilteredAnchor';
148

159
export type SelectFilteredProps = ComponentProps<typeof Select> & {
1610
filter?: string;
1711
setFilter?: Dispatch<SetStateAction<string>>;
1812
addonIcon?: ComponentProps<typeof Icon>['name'];
1913
};
2014

21-
export const SelectFiltered = forwardRef(
22-
(
23-
{
24-
options,
25-
placeholder,
26-
filter: propFilter,
27-
setFilter: propSetFilter,
28-
...props
29-
}: SelectFilteredProps,
30-
ref: Ref<HTMLInputElement>
31-
) => {
32-
const [filter, setFilter] = useState('');
33-
const anchor = useCallback(
34-
forwardRef(
35-
(
36-
{
37-
children: _children,
38-
filter,
39-
...props
40-
}: ComponentProps<typeof InputBox>,
41-
ref: Ref<HTMLInputElement>
42-
) => (
43-
<InputBox.Input
44-
mi='x4'
45-
flexGrow={1}
46-
className='rcx-select__focus'
47-
ref={ref}
48-
placeholder={placeholder}
49-
value={propFilter || filter}
50-
onChange={useMutableCallback((e: FormEvent<HTMLInputElement>) =>
51-
propSetFilter
52-
? propSetFilter(e.currentTarget.value)
53-
: setFilter(e.currentTarget.value)
54-
)}
55-
{...props}
56-
rcx-input-box--undecorated
57-
/>
58-
)
59-
),
60-
[]
61-
);
15+
export const SelectFiltered = ({
16+
options,
17+
placeholder,
18+
filter: propFilter,
19+
setFilter: propSetFilter,
20+
...props
21+
}: SelectFilteredProps) => {
22+
const [filter, setFilter] = useState('');
6223

63-
return (
64-
<Select
65-
ref={ref}
66-
placeholder={placeholder}
67-
filter={filter}
68-
options={options}
69-
{...props}
70-
anchor={anchor}
71-
/>
72-
);
73-
}
74-
);
24+
return (
25+
<Select
26+
placeholder={placeholder}
27+
filter={propFilter || filter}
28+
options={options}
29+
{...props}
30+
anchor={(params: SelectAnchorParams) => (
31+
<SelectFilteredAnchor
32+
placeholder={placeholder}
33+
filter={propFilter || filter}
34+
onChangeFilter={propSetFilter || setFilter}
35+
{...params}
36+
/>
37+
)}
38+
/>
39+
);
40+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import type {
2+
FocusEventHandler,
3+
FormEvent,
4+
KeyboardEventHandler,
5+
MouseEventHandler,
6+
ReactNode,
7+
Ref,
8+
} from 'react';
9+
import React, { forwardRef } from 'react';
10+
11+
import { InputBox } from '../InputBox';
12+
13+
type SelectFilteredAnchorProps = {
14+
children: ReactNode;
15+
disabled: boolean;
16+
filter: string;
17+
onChangeFilter: (filter: string) => void;
18+
placeholder?: string;
19+
onClick: MouseEventHandler;
20+
onBlur: FocusEventHandler;
21+
onKeyUp: KeyboardEventHandler;
22+
onKeyDown: KeyboardEventHandler;
23+
};
24+
25+
const SelectFilteredAnchor = forwardRef(function SelectFilteredAnchor(
26+
{
27+
children: _children,
28+
filter,
29+
onChangeFilter,
30+
placeholder,
31+
...props
32+
}: SelectFilteredAnchorProps,
33+
ref: Ref<Element>
34+
) {
35+
return (
36+
<InputBox.Input
37+
mi='x4'
38+
flexGrow={1}
39+
className='rcx-select__focus'
40+
ref={ref}
41+
placeholder={placeholder}
42+
value={filter}
43+
onInput={(e: FormEvent<HTMLInputElement>) =>
44+
onChangeFilter(e.currentTarget.value)
45+
}
46+
{...props}
47+
rcx-input-box--undecorated
48+
/>
49+
);
50+
});
51+
52+
export default SelectFilteredAnchor;

0 commit comments

Comments
 (0)