Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: make input field clearable and add prefix icon #1619

Merged
merged 3 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions components/input/src/input-field/input-field.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class InputField extends React.Component {
validationText,
inputWidth,
autoComplete,
clearable,
prefixIcon,
dataTest = 'dhis2-uiwidgets-inputfield',
} = this.props

Expand Down Expand Up @@ -79,6 +81,9 @@ class InputField extends React.Component {
initialFocus={initialFocus}
readOnly={readOnly}
autoComplete={autoComplete}
clearable={clearable}
prefixIcon={prefixIcon}
width={inputWidth}
/>
</Box>
</Field>
Expand All @@ -90,6 +95,8 @@ const InputFieldProps = {
/** The [native `autocomplete` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-autocomplete) */
autoComplete: PropTypes.string,
className: PropTypes.string,
/** Makes the input field clearable */
clearable: PropTypes.bool,
dataTest: PropTypes.string,
/** Makes the input smaller */
dense: PropTypes.bool,
Expand All @@ -115,6 +122,8 @@ const InputFieldProps = {
name: PropTypes.string,
/** Placeholder text for the input */
placeholder: PropTypes.string,
/** Add prefix icon */
prefixIcon: PropTypes.element,
/** Makes the input read-only */
readOnly: PropTypes.bool,
/** Indicates this input is required */
Expand Down
39 changes: 38 additions & 1 deletion components/input/src/input-field/input-field.prod.stories.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { sharedPropTypes } from '@dhis2/ui-constants'
import React from 'react'
import { IconLocation16, IconSearch16 } from '@dhis2/ui-icons'
import React, { useState } from 'react'
import { InputField } from './index.js'

const subtitle = 'Allows a user to enter data, usually text'
Expand Down Expand Up @@ -156,3 +157,39 @@ ValueTextOverflow.args = {

export const Required = Template.bind({})
Required.args = { required: true }

export const InputWithPrefixIcon = (args) => (
<>
<InputField
{...args}
name="prefix-icon-input"
label="Search"
placeholder={'Search'}
prefixIcon={<IconSearch16 />}
/>
<InputField
{...args}
name="prefix-icon-input"
label="Location"
placeholder={'Enter Location'}
prefixIcon={<IconLocation16 />}
inputWidth={'200px'}
/>
</>
)

export const ClearableInput = (args) => {
const [value, setValue] = useState('value')
return (
<InputField
{...args}
name="clearable-input"
label="This field can be cleared"
placeholder={''}
onChange={(e) => setValue(e.value)}
clearable
clearText={() => setValue('')}
value={value}
/>
)
}
79 changes: 77 additions & 2 deletions components/input/src/input/input.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { theme, colors, spacers, sharedPropTypes } from '@dhis2/ui-constants'
import { IconCross16 } from '@dhis2/ui-icons'
import { StatusIcon } from '@dhis2-ui/status-icon'
import cx from 'classnames'
import PropTypes from 'prop-types'
Expand All @@ -8,8 +9,9 @@ import { inputTypes } from './inputTypes.js'

const styles = css`
.input {
display: flex;
display: inline-flex;
align-items: center;
position: relative;
gap: ${spacers.dp8};
}

Expand Down Expand Up @@ -127,6 +129,15 @@ export class Input extends Component {
}
}

handleClear = () => {
if (this.props.onChange) {
this.props.onChange({
value: '',
name: this.props.name,
})
}
}

createHandlerPayload(e) {
return {
value: e.target.value,
Expand Down Expand Up @@ -155,10 +166,22 @@ export class Input extends Component {
step,
autoComplete,
dataTest = 'dhis2-uicore-input',
clearable,
prefixIcon,
width,
} = this.props

return (
<div className={cx('input', className)} data-test={dataTest}>
<div
className={cx(
'input',
className,
{ 'input-prefix-icon': prefixIcon },
{ 'input-clearable': clearable }
)}
data-test={dataTest}
>
{prefixIcon && <span className="prefix">{prefixIcon}</span>}
<input
role={role}
id={name}
Expand Down Expand Up @@ -187,6 +210,15 @@ export class Input extends Component {
'read-only': readOnly,
})}
/>
{clearable && value?.length ? (
<button
type="button"
onClick={this.handleClear}
className="clear-button"
>
<IconCross16 color={colors.white} />
</button>
) : null}
<StatusIcon
error={error}
valid={valid}
Expand All @@ -196,9 +228,46 @@ export class Input extends Component {

<style jsx>{styles}</style>
<style jsx>{`
.input {
width: ${width ? width : `100%`};
}

input {
width: 100%;
}

.input-prefix-icon input {
padding-inline-start: 30px;
}

.input-clearable input {
padding-inline-end: 30px;
}

.prefix {
position: absolute;
display: flex;
align-items: center;
pointer-events: none;
left: 10px;
padding: 0;
color: ${colors.grey600};
}

.clear-button {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
border: none;
cursor: pointer;
height: 16px;
width: 16px;
border-radius: 50%;
right: 10px;
background: ${colors.grey500};
padding: 1px;
}
`}</style>
</div>
)
Expand All @@ -209,6 +278,8 @@ Input.propTypes = {
/** The [native `autocomplete` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-autocomplete) */
autoComplete: PropTypes.string,
className: PropTypes.string,
/** Makes the input field clearable */
clearable: PropTypes.bool,
dataTest: PropTypes.string,
/** Makes the input smaller */
dense: PropTypes.bool,
Expand All @@ -228,6 +299,8 @@ Input.propTypes = {
name: PropTypes.string,
/** Placeholder text for the input */
placeholder: PropTypes.string,
/** Add prefix icon */
prefixIcon: PropTypes.element,
/** Makes the input read-only */
readOnly: PropTypes.bool,
/** Sets a role attribute on the input */
Expand All @@ -243,6 +316,8 @@ Input.propTypes = {
value: PropTypes.string,
/** Applies 'warning' appearance for validation feedback. Mutually exclusive with `valid` and `error` props */
warning: sharedPropTypes.statusPropType,
/** Defines the width of the input. Can be any valid CSS measurement */
width: PropTypes.string,
/** Called with signature `({ name: string, value: string }, event)` */
onBlur: PropTypes.func,
/** Called with signature `({ name: string, value: string }, event)` */
Expand Down
16 changes: 16 additions & 0 deletions components/input/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ export interface InputProps {
*/
autoComplete?: string
className?: string
/**
* Makes the input clearable
*/
clearable?: boolean
dataTest?: string
/**
* Makes the input smaller
Expand Down Expand Up @@ -78,6 +82,10 @@ export interface InputProps {
* Placeholder text for the input
*/
placeholder?: string
/**
* Add prefix icon
*/
prefixIcon?: Element
/**
* Makes the input read-only
*/
Expand Down Expand Up @@ -135,6 +143,10 @@ export interface InputFieldProps {
*/
autoComplete?: string
className?: string
/**
* Makes the input field clearable
*/
clearable?: boolean
dataTest?: string
/**
* Makes the input smaller
Expand Down Expand Up @@ -184,6 +196,10 @@ export interface InputFieldProps {
* Placeholder text for the input
*/
placeholder?: string
/**
* Add prefix icon to input
*/
prefixIcon?: Element
/**
* Makes the input read-only
*/
Expand Down
Loading