Skip to content

Commit 7e2a4d4

Browse files
author
Hector Arce De Las Heras
committed
Enhance Icon Interactivity in Input and InputDropdown Components
This commit enhances the user experience by updating the icon behavior in both Input and InputDropdown components to ensure icons are interactive only when they have an associated action. A new test confirms the correct rendering of decorative icons without actions. Changes include the introduction of a hasAction variable for icon components and a $pointerEvents prop for controlling CSS pointer events, improving accessibility and usability.
1 parent cfc2a39 commit 7e2a4d4

File tree

7 files changed

+54
-9
lines changed

7 files changed

+54
-9
lines changed

src/components/input/__tests__/input.test.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ function backspace(element) {
5656

5757
const writeText = jest.fn();
5858

59-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
6059
Object.assign(navigator, {
6160
clipboard: {
6261
writeText,
@@ -192,6 +191,24 @@ describe('New Input Component', () => {
192191
expect(results).toHaveNoViolations();
193192
});
194193

194+
it('Should render a decorative icon, when onClick callback is not set', async () => {
195+
const iconDataTestId = 'IconTestId';
196+
const { container, getByTestId } = renderProvider(
197+
<Input
198+
{...commonProps}
199+
placeholder={'placeholder'}
200+
rightIcon={{ icon: 'UNICORN', dataTestId: iconDataTestId }}
201+
/>
202+
);
203+
204+
const triggerButton = getByTestId(iconDataTestId);
205+
expect(triggerButton).toBeInTheDocument();
206+
207+
const results = await axe(container);
208+
expect(container).toHTMLValidate();
209+
expect(results).toHaveNoViolations();
210+
});
211+
195212
it('Should be autoformated the text with mask', async () => {
196213
const onChange = jest.fn();
197214
const { container, getByRole } = renderProvider(

src/components/input/components/inputIcon.tsx

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,26 @@ const InputIconStandAloneComponent = (
1515
return null;
1616
}
1717

18+
const hasAction = !!props.rightIcon?.onClick || !!props.leftIcon?.onClick;
19+
1820
const onClick: React.MouseEventHandler<HTMLButtonElement> = event => {
1921
props.rightIcon?.onClick?.(event);
2022
props.leftIcon?.onClick?.(event);
2123
};
2224

2325
return (
24-
<InputIconStyled ref={ref} iconPosition={props.iconPosition} styles={props.styles}>
26+
<InputIconStyled
27+
ref={ref}
28+
$pointerEvents={hasAction}
29+
iconPosition={props.iconPosition}
30+
styles={props.styles}
31+
>
2532
<ElementOrIcon
2633
customIconStyles={props.styles?.inputIcon}
2734
disabled={props.disabled}
2835
{...props.rightIcon}
2936
{...props.leftIcon}
30-
onClick={onClick}
37+
onClick={hasAction ? onClick : undefined}
3138
/>
3239
</InputIconStyled>
3340
);
@@ -43,17 +50,25 @@ const InputIconStandAloneDeprecatedComponent = (
4350
if (!props.icon || props.loading) {
4451
return null;
4552
}
53+
54+
const hasAction = !!props.icon.onClick;
55+
4656
const onClick: React.MouseEventHandler<HTMLButtonElement> = event => {
4757
props.icon?.onClick?.(event);
4858
};
4959

5060
return (
51-
<InputIconStyled ref={ref} iconPosition={props.iconPosition} styles={props.styles}>
61+
<InputIconStyled
62+
ref={ref}
63+
$pointerEvents={hasAction}
64+
iconPosition={props.iconPosition}
65+
styles={props.styles}
66+
>
5267
<ElementOrIcon
5368
customIconStyles={props.styles?.inputIcon}
5469
disabled={props.disabled}
5570
{...props.icon}
56-
onClick={onClick}
71+
onClick={hasAction ? onClick : undefined}
5772
/>
5873
</InputIconStyled>
5974
);

src/components/input/components/stories/stories.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,17 @@ import * as React from 'react';
33
import { ICONS } from '@/assets';
44
import { ReplaceContent } from '@/components/storybook/replaceContent/replaceContent';
55

6-
const themeWithAdditionalInfo = ['kubit'];
6+
const themeWithAdditionalInfo = [
7+
'kubit',
8+
'flameLightAlt',
9+
'modelBankLightAlt',
10+
'horizonLightAlt',
11+
'novaLightAlt',
12+
'flameLightRegular',
13+
'modelBankLightRegular',
14+
'horizonLightRegular',
15+
'novaLightRegular',
16+
];
717

818
const themeWithoutIcon = themeWithAdditionalInfo.filter(theme => theme !== 'kubit');
919

src/components/input/input.styled.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export const LoaderWrapperStyled = styled.div<LoaderStyledProps>`
8787
`;
8888

8989
export const InputIconStyled = styled.div<InputIconStyledProps>`
90+
pointer-events: ${({ $pointerEvents }) => ($pointerEvents ? 'auto' : 'none')};
9091
${({ iconPosition, styles }) => css`
9192
${getStyles(
9293
iconPosition === InputIconPosition.RIGHT

src/components/input/types/inputStyledPropsType.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export type LoaderStyledProps = {
4747
export type InputIconStyledProps = {
4848
iconPosition?: InputIconPosition;
4949
styles?: InputStateProps;
50+
$pointerEvents?: boolean;
5051
};
5152

5253
// Input

src/components/inputDropdown/inputDropdown.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const InputDropdownComponent = React.forwardRef(
4343
ctv
4444
);
4545
const device = useMediaDevice();
46+
const hasIconAction = !!icon?.onClick || !!props.rightIcon?.onClick;
4647

4748
const {
4849
openOptions,
@@ -93,15 +94,15 @@ const InputDropdownComponent = React.forwardRef(
9394
{...props}
9495
ref={innerRef as unknown as React.Ref<unknown> | undefined}
9596
device={device}
96-
icon={{ ...icon, onClick: handleClickIconInputDropdown }}
97+
icon={{ ...icon, onClick: hasIconAction ? handleClickIconInputDropdown : undefined }}
9798
informationAssociatedValue={informationAssociatedValue}
9899
inputPopoverValue={inputPopoverText}
99100
listOptionsHeight={listOptionsHeight}
100101
open={openOptions}
101102
optionList={optionsFiltered}
102103
rightIcon={{
103104
...props.rightIcon,
104-
onClick: handleClickIconInputDropdown,
105+
onClick: hasIconAction ? handleClickIconInputDropdown : undefined,
105106
}}
106107
searchText={searchText}
107108
state={state}

src/components/inputDropdown/stories/inputDropdown.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ const commonArgs: IInputDropdown = {
4646
loader: {
4747
altText: 'loading',
4848
},
49-
icon: { icon: ICONS.ICON_CHEVRON_DOWN, tabIndex: -1 },
49+
icon: { icon: ICONS.ICON_CHEVRON_DOWN },
5050
optionList: {
5151
options: [
5252
{

0 commit comments

Comments
 (0)