Skip to content

Commit 2c4db99

Browse files
author
Hector Arce De Las Heras
committed
Enhance InputSearch and Option components with focus states and updates
This commit enhances the InputSearch and Option components by removing valueSearchSelected from useInputSearch.ts and inputSearch.tsx, adding focus states in option.tsx and optionStandAlone.tsx, and updating the corresponding tests and styles. The InputSearchComponent now uses searchText instead of value. The Option and OptionStandAlone components have been updated to handle focus states, with updates to the IOptionStandAlone interface and OptionStateType enum. Tests and styles have been updated to reflect these changes, improving the user experience and accessibility of these components.
1 parent 7e86825 commit 2c4db99

File tree

11 files changed

+136
-20
lines changed

11 files changed

+136
-20
lines changed

src/components/inputSearch/hooks/useInputSearch.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ type ReturnType = {
8484
handleChangeInputSearch: ChangeEventHandler<HTMLInputElement>;
8585
handleInputKeyDown: KeyboardEventHandler<HTMLInputElement>;
8686
handleInputPopoverKeyDown: KeyboardEventHandler<HTMLInputElement>;
87-
valueSearchSelected?: string;
8887
state: InputState;
8988
handleBlurInternal: FocusEventHandler<HTMLInputElement>;
9089
handleFocusInternal: FocusEventHandler<HTMLInputElement>;

src/components/inputSearch/inputSearch.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ const InputSearchComponent = React.forwardRef(
5656
optionsFiltered,
5757
searchText,
5858
inputPopoverText,
59-
valueSearchSelected,
6059
showHighlightedOption,
6160
handleChangeInputSearch,
6261
handleClickInputSearch,
@@ -128,7 +127,7 @@ const InputSearchComponent = React.forwardRef(
128127
state={state}
129128
styles={styles}
130129
type={type}
131-
value={valueSearchSelected}
130+
value={searchText}
132131
onBlur={handleBlurInternal}
133132
onChange={handleChangeInputSearch}
134133
onClick={handleClickInputSearch}

src/components/inputSearch/inputSearchStandAlone.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { isKeyTabPressed } from '@/utils';
99
import { AUTOCOMPLETE_TYPE } from '../input/types/input';
1010
import { PopoverSearchList } from './components';
1111
// helpers
12-
import { getAriaControls } from './helpers';
1312
import { MultipleRef } from './hooks/useInputSearch';
1413
// styles
1514
import { InputSearchStyled } from './inputSearch.styled';
@@ -26,7 +25,6 @@ export const InputSearchStandAloneComponent = (
2625
const { refInput, refIcon } = ref as unknown as MultipleRef;
2726

2827
const sendRef = { refInput: refInput, refIcon: refIcon };
29-
3028
return (
3129
<InputSearchStyled
3230
data-testid={`${props.dataTestId}InputSearch`}
@@ -40,7 +38,7 @@ export const InputSearchStandAloneComponent = (
4038
<Input
4139
{...props}
4240
ref={sendRef as unknown as ForwardedRef<HTMLInputElement | undefined | null> | undefined}
43-
aria-controls={props.open ? getAriaControls(props.optionList, ariaControls) : undefined}
41+
aria-controls={props.open ? ariaControls : undefined}
4442
aria-expanded={props.open}
4543
aria-haspopup={PopoverComponentType.DIALOG}
4644
autocomplete={props.autocomplete || AUTOCOMPLETE_TYPE.OFF}
@@ -96,6 +94,7 @@ export const InputSearchStandAloneComponent = (
9694
noResultsText={props.noResultsText}
9795
open={props.open}
9896
optionList={props.optionList}
97+
optionsListDefaultArias={props.optionsListDefaultArias}
9998
preventCloseOnClickElements={[
10099
(refInput as MutableRefObject<HTMLInputElement | null | undefined>)?.current,
101100
(refIcon as MutableRefObject<HTMLSpanElement | null | undefined>)?.current,

src/components/option/__tests__/option.test.tsx

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,19 +80,41 @@ describe('Option component', () => {
8080
const option = screen.getByText(mockProps.label);
8181

8282
const disabled = undefined;
83+
const focused = false;
8384
const selected = undefined;
8485
const multiSelected = undefined;
8586
let hover = false;
8687
const filling = false;
87-
expect(getStateSpy).toHaveBeenCalledWith(disabled, selected, multiSelected, hover, filling);
88+
expect(getStateSpy).toHaveBeenCalledWith(
89+
disabled,
90+
focused,
91+
selected,
92+
multiSelected,
93+
hover,
94+
filling
95+
);
8896

8997
fireEvent.mouseEnter(option);
9098
hover = true;
91-
expect(getStateSpy).toHaveBeenCalledWith(disabled, selected, multiSelected, hover, filling);
99+
expect(getStateSpy).toHaveBeenCalledWith(
100+
disabled,
101+
focused,
102+
selected,
103+
multiSelected,
104+
hover,
105+
filling
106+
);
92107

93108
fireEvent.mouseLeave(option);
94109
hover = false;
95-
expect(getStateSpy).toHaveBeenCalledWith(disabled, selected, multiSelected, hover, filling);
110+
expect(getStateSpy).toHaveBeenCalledWith(
111+
disabled,
112+
focused,
113+
selected,
114+
multiSelected,
115+
hover,
116+
filling
117+
);
96118
});
97119

98120
it('A checked icon could be shown when its multiselect and its selected', () => {

src/components/option/option.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,19 @@ import { ErrorBoundary, FallbackComponent } from '@/provider/errorBoundary';
66
import { useGenericComponents } from '@/provider/genericComponents';
77

88
import { OptionStandAlone } from './optionStandAlone';
9-
import { IOption, IOptionStandAlone, OptionPropsStylesType } from './types';
9+
import { IOption, IOptionStandAlone, OptionPropsStylesType, OptionStateType } from './types';
1010

1111
export const OptionComponent = React.forwardRef(
1212
<V extends string | unknown>(
13-
{ variant, focus, ctv, ...props }: IOption<V>,
13+
{ variant, focus, onFocus, onBlur, ctv, ...props }: IOption<V>,
1414
ref: React.ForwardedRef<HTMLElement> | undefined | null
1515
): JSX.Element => {
1616
const styles = useStyles<OptionPropsStylesType, V>(STYLES_NAME.OPTION, variant, ctv);
17+
const hasFocusStyles = !!styles?.[OptionStateType.FOCUS];
1718
const { LINK: genericLinkComponent } = useGenericComponents();
1819
const innerRef = React.useRef<HTMLElement>();
1920
const [hover, setHover] = React.useState(false);
21+
const [focused, setFocused] = React.useState(false);
2022

2123
React.useImperativeHandle(
2224
ref,
@@ -32,13 +34,25 @@ export const OptionComponent = React.forwardRef(
3234
}
3335
}, [focus]);
3436

37+
const _onFocus = (event: React.FocusEvent<HTMLElement>) => {
38+
onFocus?.(event);
39+
setFocused(hasFocusStyles);
40+
};
41+
const _onBlur = (event: React.FocusEvent<HTMLElement>) => {
42+
onBlur?.(event);
43+
setFocused(false);
44+
};
45+
3546
return (
3647
<OptionStandAlone
3748
{...props}
3849
ref={innerRef}
3950
componentLink={genericLinkComponent}
51+
focus={focused}
4052
hover={hover}
4153
styles={styles}
54+
onBlur={_onBlur}
55+
onFocus={_onFocus}
4256
onMouseEnter={() => setHover(true)}
4357
onMouseLeave={() => setHover(false)}
4458
/>

src/components/option/optionStandAlone.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,14 @@ const OptionStandAlone = React.forwardRef(
2525
(props: IOptionStandAlone, ref: React.ForwardedRef<HTMLElement | undefined>) => {
2626
const ariaProps = pickAriaProps(props);
2727
const filling = !!props.labelCharsHighlighted && props.labelCharsHighlighted?.length > 0;
28-
const state = getState(props.disabled, props.selected, props.multiSelect, props.hover, filling);
28+
const state = getState(
29+
props.disabled,
30+
props.focus,
31+
props.selected,
32+
props.multiSelect,
33+
props.hover,
34+
filling
35+
);
2936
const stateStyles = props.styles[state];
3037
const disabled = state === OptionStateType.DISABLED;
3138

@@ -59,6 +66,7 @@ const OptionStandAlone = React.forwardRef(
5966
role={props.role}
6067
tabIndex={props.tabIndex}
6168
url={props.url}
69+
onBlur={props.onBlur}
6270
onClick={handleClickOption}
6371
onFocus={props.onFocus}
6472
onKeyDown={(event: React.KeyboardEvent<HTMLDivElement>) => {

src/components/option/types/option.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export interface IOptionStandAlone extends OptionAriaAttributes {
3737
onMouseEnter: React.MouseEventHandler<HTMLElement>;
3838
onMouseLeave: React.MouseEventHandler<HTMLElement>;
3939
onFocus?: React.FocusEventHandler<HTMLElement>;
40+
onBlur?: React.FocusEventHandler<HTMLElement>;
4041
role?: ROLES;
4142
componentLink?: GenericLinkType;
4243
dataTestId?: string;

src/components/option/types/state.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ export enum OptionStateType {
1212
MULTIPLE_SELECTED = 'MULTIPLE_SELECTED',
1313
MULTIPLE_SELECTED_HOVER = 'MULTIPLE_SELECTED_HOVER',
1414
DISABLED = 'DISABLED',
15+
FOCUS = 'FOCUS',
1516
}

src/components/option/utils/__tests__/option.utils.test.ts

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,86 +4,108 @@ import { getHighlightedIndexes, getState } from '../option.utils';
44
describe('OptionUtils - getState', () => {
55
it('Should be DISABLED when disabled is true', () => {
66
const disabled = true;
7+
const focused = false;
78
const selected = false;
89
const multiSelected = false;
910
const hover = false;
1011
const filling = false;
11-
expect(getState(disabled, selected, multiSelected, hover, filling)).toBe(
12+
expect(getState(disabled, focused, selected, multiSelected, hover, filling)).toBe(
1213
OptionStateType.DISABLED
1314
);
1415
});
1516

17+
it('Should be FOCUS when focused is true', () => {
18+
const disabled = false;
19+
const focused = true;
20+
const selected = false;
21+
const multiSelected = false;
22+
const hover = false;
23+
const filling = false;
24+
expect(getState(disabled, focused, selected, multiSelected, hover, filling)).toBe(
25+
OptionStateType.FOCUS
26+
);
27+
});
28+
1629
it('Should be MULTIPLE_SELECTED_HOVER when selected, multiSelected and hover', () => {
1730
const disabled = false;
31+
const focused = false;
1832
const selected = true;
1933
const multiSelected = true;
2034
const hover = true;
2135
const filling = false;
22-
expect(getState(disabled, selected, multiSelected, hover, filling)).toBe(
36+
expect(getState(disabled, focused, selected, multiSelected, hover, filling)).toBe(
2337
OptionStateType.MULTIPLE_SELECTED_HOVER
2438
);
2539
});
2640

2741
it('Should be MULTIPLE_SELECTED when selected and multiSelected', () => {
2842
const disabled = false;
43+
const focused = false;
2944
const selected = true;
3045
const multiSelected = true;
3146
const hover = false;
3247
const filling = false;
33-
expect(getState(disabled, selected, multiSelected, hover, filling)).toBe(
48+
expect(getState(disabled, focused, selected, multiSelected, hover, filling)).toBe(
3449
OptionStateType.MULTIPLE_SELECTED
3550
);
3651
});
3752

3853
it('Should be SELECTED_HOVER when selected and hover', () => {
3954
const disabled = false;
55+
const focused = false;
4056
const selected = true;
4157
const multiSelected = false;
4258
const hover = true;
4359
const filling = false;
44-
expect(getState(disabled, selected, multiSelected, hover, filling)).toBe(
60+
expect(getState(disabled, focused, selected, multiSelected, hover, filling)).toBe(
4561
OptionStateType.SELECTED_HOVER
4662
);
4763
});
4864

4965
it('Should be SELECTED when selected', () => {
5066
const disabled = false;
67+
const focused = false;
5168
const selected = true;
5269
const multiSelected = false;
5370
const hover = false;
5471
const filling = false;
55-
expect(getState(disabled, selected, multiSelected, hover, filling)).toBe(
72+
expect(getState(disabled, focused, selected, multiSelected, hover, filling)).toBe(
5673
OptionStateType.SELECTED
5774
);
5875
});
5976

6077
it('Should be HOVER when hover', () => {
6178
const disabled = false;
79+
const focused = false;
6280
const selected = false;
6381
const multiSelected = false;
6482
const hover = true;
6583
const filling = false;
66-
expect(getState(disabled, selected, multiSelected, hover, filling)).toBe(OptionStateType.HOVER);
84+
expect(getState(disabled, focused, selected, multiSelected, hover, filling)).toBe(
85+
OptionStateType.HOVER
86+
);
6787
});
6888

6989
it('Should be FILLING when filling', () => {
7090
const disabled = false;
91+
const focused = false;
7192
const selected = false;
7293
const multiSelected = false;
7394
const hover = false;
7495
const filling = true;
75-
expect(getState(disabled, selected, multiSelected, hover, filling)).toBe(
96+
expect(getState(disabled, focused, selected, multiSelected, hover, filling)).toBe(
7697
OptionStateType.FILLING
7798
);
7899
});
79100

80101
it('Should be DEFAULT in other case', () => {
81102
const disabled = false;
103+
const focused = false;
82104
const selected = false;
83105
const multiSelected = false;
84106
const hover = false;
85107
const filling = false;
86-
expect(getState(disabled, selected, multiSelected, hover, filling)).toBe(
108+
expect(getState(disabled, focused, selected, multiSelected, hover, filling)).toBe(
87109
OptionStateType.DEFAULT
88110
);
89111
});

src/components/option/utils/option.utils.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { OptionStateType } from '../types';
33
// eslint-disable-next-line complexity
44
const getState = (
55
disabled: boolean | undefined,
6+
focused: boolean | undefined,
67
selected: boolean | undefined,
78
multiSelected: boolean | undefined,
89
hover: boolean,
@@ -11,6 +12,9 @@ const getState = (
1112
if (disabled) {
1213
return OptionStateType.DISABLED;
1314
}
15+
if (focused) {
16+
return OptionStateType.FOCUS;
17+
}
1418
if (selected) {
1519
if (multiSelected) {
1620
return hover ? OptionStateType.MULTIPLE_SELECTED_HOVER : OptionStateType.MULTIPLE_SELECTED;

0 commit comments

Comments
 (0)