Skip to content
80 changes: 80 additions & 0 deletions components/atom/input/demo/articles/ArticleAddonAndIcon.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,86 @@ const ArticleAddonAndIcon = ({className}) => {
/>
</Cell>
</Grid>
<H2>Interactive Icons</H2>
<Paragraph>
Icons can be made interactive by adding click handlers. This is useful for actions like search, clear input, or
triggering additional functionality.
</Paragraph>

<Grid cols={1} gutter={[8, 8]}>
<Paragraph>Left Icon Actions</Paragraph>
<Cell>
<PrimitiveVisuallyHidden>
<AtomLabel htmlFor="input-info-action" text="Information input with help action" />
</PrimitiveVisuallyHidden>
<AtomInput
id="input-info-action"
placeholder="Enter your email address"
leftIcon={<AntDesignIcon icon="AiOutlineInfoCircle" style={{color: '#1890ff'}} />}
ariaLabelLeftIcon="Show help information"
onClickLeftIcon={() => alert('Email format: [email protected]')}
/>
</Cell>

<Cell>
<Paragraph>Right Icon Actions</Paragraph>
<PrimitiveVisuallyHidden>
<AtomLabel htmlFor="input-clear-action" text="Input with clear action" />
</PrimitiveVisuallyHidden>
<AtomInput
id="input-clear-action"
placeholder="Type something to clear..."
rightIcon={<AntDesignIcon icon="AiOutlineClose" />}
ariaLabelRightIcon="Clear input"
onClickRightIcon={() => {
document.getElementById('input-clear-action').value = ''
}}
/>
</Cell>
</Grid>

<H2>Button Customization</H2>
<Paragraph>
Use <Code>leftIconButtonProps</Code> and <Code>rightIconButtonProps</Code> to customize the appearance and
behavior of icon buttons.
</Paragraph>

<Grid cols={1} gutter={[8, 8]}>
<Cell>
<Paragraph>
<strong>Custom Button Designs</strong>
</Paragraph>
<PrimitiveVisuallyHidden>
<AtomLabel htmlFor="input-solid-design" text="Search input with solid design button" />
</PrimitiveVisuallyHidden>
<AtomInput
id="input-solid-design"
placeholder="Search with solid design..."
leftIcon={<AntDesignIcon icon="AiOutlineSearch" />}
ariaLabelLeftIcon="Search"
onClickLeftIcon={() => alert('Searching...')}
leftIconButtonProps={{
style: {backgroundColor: '#1890ff', color: '#fff'}
}}
/>
</Cell>

<Cell>
<PrimitiveVisuallyHidden>
<AtomLabel htmlFor="input-outline-design" text="Filter input with outline design button" />
</PrimitiveVisuallyHidden>
<AtomInput
id="input-outline-design"
placeholder="Filter with outline design..."
rightIcon={<AntDesignIcon icon="AiOutlineFilter" />}
ariaLabelRightIcon="Apply filters"
onClickRightIcon={() => alert('Applying filters...')}
rightIconButtonProps={{
style: {backgroundColor: 'red', color: '#fff'}
}}
/>
</Cell>
</Grid>
</Article>
)
}
Expand Down
90 changes: 71 additions & 19 deletions components/atom/input/src/Input/Wrappers/Icons/InputIcons.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,26 @@ import PropTypes from 'prop-types'

import {
BASE_CLASS_ICON,
BASE_CLASS_ICON_BUTTON,
BASE_CLASS_ICON_BUTTON_CONTAINER,
BASE_CLASS_ICON_COMPONENT,
BASE_CLASS_ICON_COMPONENT_HANDLER,
BASE_CLASS_ICON_COMPONENT_LEFT,
BASE_CLASS_ICON_COMPONENT_RIGHT,
BASE_CLASS_ICON_LEFT,
BASE_CLASS_ICON_RIGHT
} from './config.js'

const InputIcons = ({leftIcon, rightIcon, onClickLeftIcon, onClickRightIcon, children}) => {
const InputIcons = ({
leftIcon,
rightIcon,
onClickLeftIcon,
onClickRightIcon,
ariaLabelLeftIcon,
ariaLabelRightIcon,
leftIconButtonProps,
rightIconButtonProps,
children
}) => {
if (!(leftIcon || rightIcon)) {
return children
}
Expand All @@ -23,6 +34,10 @@ const InputIcons = ({leftIcon, rightIcon, onClickLeftIcon, onClickRightIcon, chi
onClickRightIcon && onClickRightIcon(event)
}

const defaultButtonProps = {
type: 'button'
}

return (
<div
className={cx(BASE_CLASS_ICON, {
Expand All @@ -31,25 +46,50 @@ const InputIcons = ({leftIcon, rightIcon, onClickLeftIcon, onClickRightIcon, chi
})}
>
{leftIcon && (
<span
className={cx(BASE_CLASS_ICON_COMPONENT, BASE_CLASS_ICON_COMPONENT_LEFT, {
[BASE_CLASS_ICON_COMPONENT_HANDLER]: onClickLeftIcon
})}
onClick={handleLeftClick}
>
{leftIcon}
</span>
<>
{onClickLeftIcon ? (
<button
className={cx(
BASE_CLASS_ICON_COMPONENT,
BASE_CLASS_ICON_COMPONENT_LEFT,
BASE_CLASS_ICON_BUTTON,
leftIconButtonProps?.className
)}
{...defaultButtonProps}
{...leftIconButtonProps}
onClick={handleLeftClick}
aria-label={ariaLabelLeftIcon}
tabIndex={0}
>
<div className={BASE_CLASS_ICON_BUTTON_CONTAINER}>{leftIcon}</div>
</button>
) : (
<span className={cx(BASE_CLASS_ICON_COMPONENT, BASE_CLASS_ICON_COMPONENT_LEFT)}>{leftIcon}</span>
)}
</>
)}
{children}
{rightIcon && (
<span
className={cx(BASE_CLASS_ICON_COMPONENT, BASE_CLASS_ICON_COMPONENT_RIGHT, {
[BASE_CLASS_ICON_COMPONENT_HANDLER]: onClickRightIcon
})}
onClick={handleRightClick}
>
{rightIcon}
</span>
<>
{onClickRightIcon ? (
<button
className={cx(
BASE_CLASS_ICON_COMPONENT,
BASE_CLASS_ICON_COMPONENT_RIGHT,
BASE_CLASS_ICON_BUTTON,
rightIconButtonProps?.className
)}
{...defaultButtonProps}
{...rightIconButtonProps}
onClick={handleRightClick}
aria-label={ariaLabelRightIcon}
>
<div className={BASE_CLASS_ICON_BUTTON_CONTAINER}>{rightIcon}</div>
</button>
) : (
<span className={cx(BASE_CLASS_ICON_COMPONENT, BASE_CLASS_ICON_COMPONENT_RIGHT)}>{rightIcon}</span>
)}
</>
)}
</div>
)
Expand All @@ -69,7 +109,19 @@ InputIcons.propTypes = {
onClickLeftIcon: PropTypes.func,

/* Right icon click callback */
onClickRightIcon: PropTypes.func
onClickRightIcon: PropTypes.func,

/* Right icon aria-label */
ariaLabelRightIcon: PropTypes.string,

/* Left icon aria-label */
ariaLabelLeftIcon: PropTypes.string,

/* Left icon button props */
leftIconButtonProps: PropTypes.object,

/* Right icon button props */
rightIconButtonProps: PropTypes.object
}

export default InputIcons
3 changes: 2 additions & 1 deletion components/atom/input/src/Input/Wrappers/Icons/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const BASE_CLASS_ICON = `${BASE}--withIcon`
export const BASE_CLASS_ICON_LEFT = `${BASE_CLASS_ICON}--${ICON_TYPES.LEFT}`
export const BASE_CLASS_ICON_RIGHT = `${BASE_CLASS_ICON}--${ICON_TYPES.RIGHT}`
export const BASE_CLASS_ICON_COMPONENT = `${BASE_CLASS_ICON}-icon`
export const BASE_CLASS_ICON_COMPONENT_HANDLER = `${BASE_CLASS_ICON_COMPONENT}--withHandler`
export const BASE_CLASS_ICON_BUTTON = `${BASE_CLASS_ICON}-button`
export const BASE_CLASS_ICON_BUTTON_CONTAINER = `${BASE_CLASS_ICON}-button-container`
export const BASE_CLASS_ICON_COMPONENT_LEFT = `${BASE_CLASS_ICON_COMPONENT}--${ICON_TYPES.LEFT}`
export const BASE_CLASS_ICON_COMPONENT_RIGHT = `${BASE_CLASS_ICON_COMPONENT}--${ICON_TYPES.RIGHT}`
38 changes: 27 additions & 11 deletions components/atom/input/src/Input/Wrappers/Icons/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,33 @@
padding-right: $pr-atom-input-input;
}

&-button {
@include reset-button;
position: relative;
padding: 0;
min-width: 0;

// Expand clickable area
&::before {
content: '';
position: absolute;
top: -$m-m;
bottom: -$m-m;
left: -$m-m;
right: -$m-m;
background: transparent;
}

&-container {
width: $sz-icon-m;
}

&:hover {
background-color: $c-gray-light-3;
border-radius: $p-xs;
}
}

&-icon {
align-items: center;
color: $c-atom-input-icon;
Expand All @@ -21,12 +48,6 @@
top: $t-atom-input-icon;
transform: translateY($trf-ty-atom-input-icon);
width: $w-atom-input-icon;
pointer-events: none;

&--withHandler {
cursor: pointer;
pointer-events: auto;
}

&--left {
left: $l-atom-input-icon;
Expand All @@ -35,10 +56,5 @@
&--right {
right: $r-atom-input-icon;
}

& > * {
height: 100%;
width: 100%;
}
}
}
16 changes: 16 additions & 0 deletions components/atom/input/src/Input/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ const BaseInput = forwardRef(
children,
onClickLeftIcon,
onClickRightIcon,
ariaLabelLeftIcon,
ariaLabelRightIcon,
leftIconButtonProps,
rightIconButtonProps,
size = SIZES.MEDIUM,
shape,
...inputProps
Expand All @@ -33,6 +37,10 @@ const BaseInput = forwardRef(
rightIcon={rightIcon}
onClickLeftIcon={onClickLeftIcon}
onClickRightIcon={onClickRightIcon}
ariaLabelLeftIcon={ariaLabelLeftIcon}
ariaLabelRightIcon={ariaLabelRightIcon}
leftIconButtonProps={leftIconButtonProps}
rightIconButtonProps={rightIconButtonProps}
>
<Input ref={forwardedRef} shape={shape} {...inputProps} size={size}>
{children}
Expand Down Expand Up @@ -61,6 +69,14 @@ BaseInput.propTypes = {
onClickLeftIcon: PropTypes.func,
/* Right icon click callback */
onClickRightIcon: PropTypes.func,
/* Right icon aria-label */
ariaLabelRightIcon: PropTypes.string,
/* Left icon aria-label */
ariaLabelLeftIcon: PropTypes.string,
/* Left icon button props */
leftIconButtonProps: PropTypes.object,
/* Right icon button props */
rightIconButtonProps: PropTypes.object,
/* Sets the size of the inputAddon */
size: PropTypes.oneOf(Object.values(SIZES)),
/* Sets the shape of the input */
Expand Down
Loading