@dhis2-ui/input 10.16.2 → 10.16.3-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +10 -9
- package/src/index.js +2 -0
- package/src/input/__tests__/input.test.js +29 -0
- package/src/input/features/accepts_initial_focus/index.js +9 -0
- package/src/input/features/accepts_initial_focus.feature +5 -0
- package/src/input/features/can_be_blurred/index.js +18 -0
- package/src/input/features/can_be_blurred.feature +6 -0
- package/src/input/features/can_be_changed/index.js +18 -0
- package/src/input/features/can_be_changed.feature +6 -0
- package/src/input/features/can_be_disabled/index.js +13 -0
- package/src/input/features/can_be_disabled.feature +6 -0
- package/src/input/features/can_be_focused/index.js +18 -0
- package/src/input/features/can_be_focused.feature +6 -0
- package/src/input/index.js +2 -0
- package/src/input/input.e2e.stories.js +39 -0
- package/src/input/input.js +344 -0
- package/src/input/input.prod.stories.js +134 -0
- package/src/input/inputTypes.js +15 -0
- package/src/input-field/__tests__/input-field.test.js +27 -0
- package/src/input-field/features/can_be_required/index.js +11 -0
- package/src/input-field/features/can_be_required.feature +5 -0
- package/src/input-field/index.js +1 -0
- package/src/input-field/input-field.e2e.stories.js +7 -0
- package/src/input-field/input-field.js +156 -0
- package/src/input-field/input-field.prod.stories.js +195 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dhis2-ui/input",
|
|
3
|
-
"version": "10.16.
|
|
3
|
+
"version": "10.16.3-alpha.1",
|
|
4
4
|
"description": "UI Input",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -33,19 +33,20 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@dhis2/prop-types": "^3.1.2",
|
|
36
|
-
"@dhis2-ui/box": "10.16.
|
|
37
|
-
"@dhis2-ui/field": "10.16.
|
|
38
|
-
"@dhis2-ui/input": "10.16.
|
|
39
|
-
"@dhis2-ui/loader": "10.16.
|
|
40
|
-
"@dhis2-ui/status-icon": "10.16.
|
|
41
|
-
"@dhis2/ui-constants": "10.16.
|
|
42
|
-
"@dhis2/ui-icons": "10.16.
|
|
36
|
+
"@dhis2-ui/box": "10.16.3-alpha.1",
|
|
37
|
+
"@dhis2-ui/field": "10.16.3-alpha.1",
|
|
38
|
+
"@dhis2-ui/input": "10.16.3-alpha.1",
|
|
39
|
+
"@dhis2-ui/loader": "10.16.3-alpha.1",
|
|
40
|
+
"@dhis2-ui/status-icon": "10.16.3-alpha.1",
|
|
41
|
+
"@dhis2/ui-constants": "10.16.3-alpha.1",
|
|
42
|
+
"@dhis2/ui-icons": "10.16.3-alpha.1",
|
|
43
43
|
"classnames": "^2.3.1",
|
|
44
44
|
"prop-types": "^15.7.2"
|
|
45
45
|
},
|
|
46
46
|
"files": [
|
|
47
47
|
"build",
|
|
48
|
-
"types"
|
|
48
|
+
"types",
|
|
49
|
+
"src"
|
|
49
50
|
],
|
|
50
51
|
"devDependencies": {
|
|
51
52
|
"react": "^18.3.1",
|
package/src/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { render, fireEvent, screen } from '@testing-library/react'
|
|
2
|
+
import { shallow } from 'enzyme'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { Input } from '../input.js'
|
|
5
|
+
|
|
6
|
+
describe('<Input>', () => {
|
|
7
|
+
it('passes min, max, and step props as attributes to the native <input> element', () => {
|
|
8
|
+
const testProps = { min: '0', max: '10', step: '0.5' }
|
|
9
|
+
const wrapper = shallow(<Input type="number" {...testProps} />)
|
|
10
|
+
|
|
11
|
+
const inputEl = wrapper.find('input')
|
|
12
|
+
expect(inputEl.props()).toMatchObject(testProps)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('should call the onKeyDown callback when provided', () => {
|
|
16
|
+
const onKeyDown = jest.fn()
|
|
17
|
+
|
|
18
|
+
render(<Input name="foo" value="bar" onKeyDown={onKeyDown} />)
|
|
19
|
+
|
|
20
|
+
fireEvent.keyDown(screen.getByRole('textbox'), {})
|
|
21
|
+
|
|
22
|
+
expect(onKeyDown).toHaveBeenCalledWith(
|
|
23
|
+
{ name: 'foo', value: 'bar' },
|
|
24
|
+
expect.objectContaining({})
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
expect(onKeyDown).toHaveBeenCalledTimes(1)
|
|
28
|
+
})
|
|
29
|
+
})
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Given, Then } from '@badeball/cypress-cucumber-preprocessor'
|
|
2
|
+
|
|
3
|
+
Given('a Input with initialFocus is rendered', () => {
|
|
4
|
+
cy.visitStory('Input', 'With initial focus')
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
Then('the Input is focused', () => {
|
|
8
|
+
cy.focused().parent('[data-test="dhis2-uicore-input"]').should('exist')
|
|
9
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'
|
|
2
|
+
|
|
3
|
+
Given('an Input with initialFocus and onBlur handler is rendered', () => {
|
|
4
|
+
cy.visitStory('Input', 'With initial focus and on blur')
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
When('the Input is blurred', () => {
|
|
8
|
+
cy.get('[data-test="dhis2-uicore-input"] input').blur()
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
Then('the onBlur handler is called', () => {
|
|
12
|
+
cy.window().should((win) => {
|
|
13
|
+
expect(win.onBlur).to.be.calledWith({
|
|
14
|
+
value: '',
|
|
15
|
+
name: 'Default',
|
|
16
|
+
})
|
|
17
|
+
})
|
|
18
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'
|
|
2
|
+
|
|
3
|
+
Given('a Input with onChange handler is rendered', () => {
|
|
4
|
+
cy.visitStory('Input', 'With on change')
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
When('the Input is filled with a character', () => {
|
|
8
|
+
cy.get('[data-test="dhis2-uicore-input"]').click().type('a')
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
Then('the onChange handler is called', () => {
|
|
12
|
+
cy.window().should((win) => {
|
|
13
|
+
expect(win.onChange).to.be.calledWith({
|
|
14
|
+
value: 'a',
|
|
15
|
+
name: 'Default',
|
|
16
|
+
})
|
|
17
|
+
})
|
|
18
|
+
})
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'
|
|
2
|
+
|
|
3
|
+
Given('a disabled Input is rendered', () => {
|
|
4
|
+
cy.visitStory('Input', 'With disabled')
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
When('the user clicks the input', () => {
|
|
8
|
+
cy.get('[data-test="dhis2-uicore-input"] input').click({ force: true })
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
Then('the Input is not focused', () => {
|
|
12
|
+
cy.focused().should('not.exist')
|
|
13
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'
|
|
2
|
+
|
|
3
|
+
Given('a Input with onFocus handler is rendered', () => {
|
|
4
|
+
cy.visitStory('Input', 'With on focus')
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
When('the Input is focused', () => {
|
|
8
|
+
cy.get('[data-test="dhis2-uicore-input"] input').focus()
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
Then('the onFocus handler is called', () => {
|
|
12
|
+
cy.window().should((win) => {
|
|
13
|
+
expect(win.onFocus).to.be.calledWith({
|
|
14
|
+
value: '',
|
|
15
|
+
name: 'Default',
|
|
16
|
+
})
|
|
17
|
+
})
|
|
18
|
+
})
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Input } from './index.js'
|
|
3
|
+
|
|
4
|
+
window.onChange = window.Cypress && window.Cypress.cy.stub()
|
|
5
|
+
window.onBlur = window.Cypress && window.Cypress.cy.stub()
|
|
6
|
+
window.onFocus = window.Cypress && window.Cypress.cy.stub()
|
|
7
|
+
|
|
8
|
+
export default { title: 'Input' }
|
|
9
|
+
export const WithOnChange = () => (
|
|
10
|
+
<Input
|
|
11
|
+
label="Default label"
|
|
12
|
+
name="Default"
|
|
13
|
+
value=""
|
|
14
|
+
onChange={window.onChange}
|
|
15
|
+
/>
|
|
16
|
+
)
|
|
17
|
+
export const WithInitialFocusAndOnBlur = () => (
|
|
18
|
+
<Input
|
|
19
|
+
label="Default label"
|
|
20
|
+
name="Default"
|
|
21
|
+
value=""
|
|
22
|
+
initialFocus
|
|
23
|
+
onBlur={window.onBlur}
|
|
24
|
+
/>
|
|
25
|
+
)
|
|
26
|
+
export const WithOnFocus = () => (
|
|
27
|
+
<Input
|
|
28
|
+
label="Default label"
|
|
29
|
+
name="Default"
|
|
30
|
+
value=""
|
|
31
|
+
onFocus={window.onFocus}
|
|
32
|
+
/>
|
|
33
|
+
)
|
|
34
|
+
export const WithInitialFocus = () => (
|
|
35
|
+
<Input label="Default label" name="Default" value="" initialFocus />
|
|
36
|
+
)
|
|
37
|
+
export const WithDisabled = () => (
|
|
38
|
+
<Input label="Default label" name="Default" value="" disabled />
|
|
39
|
+
)
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { theme, colors, spacers, sharedPropTypes } from '@dhis2/ui-constants'
|
|
2
|
+
import { IconCross16 } from '@dhis2/ui-icons'
|
|
3
|
+
import { StatusIcon } from '@dhis2-ui/status-icon'
|
|
4
|
+
import cx from 'classnames'
|
|
5
|
+
import PropTypes from 'prop-types'
|
|
6
|
+
import React, { Component } from 'react'
|
|
7
|
+
import css from 'styled-jsx/css'
|
|
8
|
+
import { inputTypes } from './inputTypes.js'
|
|
9
|
+
|
|
10
|
+
const styles = css`
|
|
11
|
+
.input {
|
|
12
|
+
display: inline-flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
position: relative;
|
|
15
|
+
gap: ${spacers.dp8};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
input {
|
|
19
|
+
box-sizing: border-box;
|
|
20
|
+
|
|
21
|
+
font-size: 14px;
|
|
22
|
+
line-height: 16px;
|
|
23
|
+
user-select: text;
|
|
24
|
+
|
|
25
|
+
color: ${colors.grey900};
|
|
26
|
+
background-color: white;
|
|
27
|
+
|
|
28
|
+
padding: 11px 12px;
|
|
29
|
+
max-height: 40px;
|
|
30
|
+
|
|
31
|
+
outline: 0;
|
|
32
|
+
border: 1px solid ${colors.grey500};
|
|
33
|
+
border-radius: 3px;
|
|
34
|
+
box-shadow: inset 0 0 1px 0 rgba(48, 54, 60, 0.1);
|
|
35
|
+
text-overflow: ellipsis;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
input.dense {
|
|
39
|
+
max-height: 32px;
|
|
40
|
+
padding: 7px 8px;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
input:focus {
|
|
44
|
+
outline: none;
|
|
45
|
+
box-shadow: inset 0 0 0 2px ${theme.focus};
|
|
46
|
+
border-color: ${theme.focus};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
input::placeholder {
|
|
50
|
+
color: ${colors.grey600};
|
|
51
|
+
opacity: 1;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
input[type='date']::-webkit-inner-spin-button,
|
|
55
|
+
input[type='date']::-webkit-calendar-picker-indicator,
|
|
56
|
+
input[type='time']::-webkit-inner-spin-button,
|
|
57
|
+
input[type='time']::-webkit-calendar-picker-indicator,
|
|
58
|
+
input[type='datetime-local']::-webkit-inner-spin-button,
|
|
59
|
+
input[type='datetime-local']::-webkit-calendar-picker-indicator {
|
|
60
|
+
height: 14px;
|
|
61
|
+
padding-top: 1px;
|
|
62
|
+
padding-bottom: 1px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
input[type='date']::-webkit-datetime-edit-fields-wrapper,
|
|
66
|
+
input[type='datetime-local']::-webkit-datetime-edit-fields-wrapper,
|
|
67
|
+
input[type='time']::-webkit-datetime-edit-fields-wrapper {
|
|
68
|
+
padding: 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
input.warning {
|
|
72
|
+
border-color: ${theme.warning};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
input.error {
|
|
76
|
+
border-color: ${theme.error};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
input.read-only {
|
|
80
|
+
background-color: ${colors.grey050};
|
|
81
|
+
border-color: ${colors.grey300};
|
|
82
|
+
box-shadow: none;
|
|
83
|
+
cursor: text;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
input.disabled {
|
|
87
|
+
background-color: ${colors.grey100};
|
|
88
|
+
border-color: ${colors.grey500};
|
|
89
|
+
color: ${theme.disabled};
|
|
90
|
+
cursor: not-allowed;
|
|
91
|
+
}
|
|
92
|
+
`
|
|
93
|
+
|
|
94
|
+
export class Input extends Component {
|
|
95
|
+
static defaultProps = {
|
|
96
|
+
type: 'text',
|
|
97
|
+
dataTest: 'dhis2-uicore-input',
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
inputRef = React.createRef()
|
|
101
|
+
|
|
102
|
+
componentDidMount() {
|
|
103
|
+
if (this.props.initialFocus) {
|
|
104
|
+
this.inputRef.current.focus()
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
handleChange = (e) => {
|
|
109
|
+
if (this.props.onChange) {
|
|
110
|
+
this.props.onChange(this.createHandlerPayload(e), e)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
handleBlur = (e) => {
|
|
115
|
+
if (this.props.onBlur) {
|
|
116
|
+
this.props.onBlur(this.createHandlerPayload(e), e)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
handleFocus = (e) => {
|
|
121
|
+
if (this.props.onFocus) {
|
|
122
|
+
this.props.onFocus(this.createHandlerPayload(e), e)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
handleKeyDown = (e) => {
|
|
127
|
+
if (this.props.onKeyDown) {
|
|
128
|
+
this.props.onKeyDown(this.createHandlerPayload(e), e)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
handleClear = () => {
|
|
133
|
+
if (this.props.onChange) {
|
|
134
|
+
this.props.onChange({
|
|
135
|
+
value: '',
|
|
136
|
+
name: this.props.name,
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
createHandlerPayload(e) {
|
|
142
|
+
return {
|
|
143
|
+
value: e.target.value,
|
|
144
|
+
name: this.props.name,
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
render() {
|
|
149
|
+
const {
|
|
150
|
+
role,
|
|
151
|
+
ariaLabel,
|
|
152
|
+
ariaControls,
|
|
153
|
+
ariaHaspopup,
|
|
154
|
+
className,
|
|
155
|
+
type = 'text',
|
|
156
|
+
dense,
|
|
157
|
+
disabled,
|
|
158
|
+
readOnly,
|
|
159
|
+
placeholder,
|
|
160
|
+
name,
|
|
161
|
+
valid,
|
|
162
|
+
error,
|
|
163
|
+
warning,
|
|
164
|
+
loading,
|
|
165
|
+
value,
|
|
166
|
+
tabIndex,
|
|
167
|
+
max,
|
|
168
|
+
min,
|
|
169
|
+
step,
|
|
170
|
+
autoComplete,
|
|
171
|
+
dataTest = 'dhis2-uicore-input',
|
|
172
|
+
clearable,
|
|
173
|
+
prefixIcon,
|
|
174
|
+
width,
|
|
175
|
+
} = this.props
|
|
176
|
+
|
|
177
|
+
const statusIcon = error || loading || valid || warning
|
|
178
|
+
const clearButtonPadding = statusIcon ? '40px' : '10px'
|
|
179
|
+
|
|
180
|
+
return (
|
|
181
|
+
<div
|
|
182
|
+
className={cx(
|
|
183
|
+
'input',
|
|
184
|
+
className,
|
|
185
|
+
{ 'input-prefix-icon': prefixIcon },
|
|
186
|
+
{ 'input-clearable': clearable }
|
|
187
|
+
)}
|
|
188
|
+
data-test={dataTest}
|
|
189
|
+
>
|
|
190
|
+
{prefixIcon && <span className="prefix">{prefixIcon}</span>}
|
|
191
|
+
<input
|
|
192
|
+
aria-label={ariaLabel}
|
|
193
|
+
aria-controls={ariaControls}
|
|
194
|
+
aria-haspopup={ariaHaspopup}
|
|
195
|
+
role={role}
|
|
196
|
+
id={name}
|
|
197
|
+
name={name}
|
|
198
|
+
placeholder={placeholder}
|
|
199
|
+
ref={this.inputRef}
|
|
200
|
+
type={type}
|
|
201
|
+
value={value}
|
|
202
|
+
max={max}
|
|
203
|
+
min={min}
|
|
204
|
+
step={step}
|
|
205
|
+
disabled={disabled}
|
|
206
|
+
readOnly={readOnly}
|
|
207
|
+
tabIndex={tabIndex}
|
|
208
|
+
autoComplete={autoComplete}
|
|
209
|
+
onFocus={this.handleFocus}
|
|
210
|
+
onBlur={this.handleBlur}
|
|
211
|
+
onChange={this.handleChange}
|
|
212
|
+
onKeyDown={this.handleKeyDown}
|
|
213
|
+
className={cx({
|
|
214
|
+
dense,
|
|
215
|
+
disabled,
|
|
216
|
+
error,
|
|
217
|
+
valid,
|
|
218
|
+
warning,
|
|
219
|
+
'read-only': readOnly,
|
|
220
|
+
})}
|
|
221
|
+
/>
|
|
222
|
+
{clearable && value?.length ? (
|
|
223
|
+
<button
|
|
224
|
+
type="button"
|
|
225
|
+
onClick={this.handleClear}
|
|
226
|
+
className="clear-button"
|
|
227
|
+
>
|
|
228
|
+
<IconCross16 color={colors.white} />
|
|
229
|
+
</button>
|
|
230
|
+
) : null}
|
|
231
|
+
<StatusIcon
|
|
232
|
+
error={error}
|
|
233
|
+
valid={valid}
|
|
234
|
+
loading={loading}
|
|
235
|
+
warning={warning}
|
|
236
|
+
/>
|
|
237
|
+
|
|
238
|
+
<style jsx>{styles}</style>
|
|
239
|
+
<style jsx>{`
|
|
240
|
+
.input {
|
|
241
|
+
width: ${width || `100%`};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
input {
|
|
245
|
+
width: 100%;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.input-prefix-icon input {
|
|
249
|
+
padding-inline-start: 30px;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.input-clearable input {
|
|
253
|
+
padding-inline-end: 30px;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.prefix {
|
|
257
|
+
position: absolute;
|
|
258
|
+
display: flex;
|
|
259
|
+
align-items: center;
|
|
260
|
+
pointer-events: none;
|
|
261
|
+
inset-inline-start: 10px;
|
|
262
|
+
padding: 0;
|
|
263
|
+
color: ${colors.grey600};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.clear-button {
|
|
267
|
+
position: absolute;
|
|
268
|
+
display: flex;
|
|
269
|
+
align-items: center;
|
|
270
|
+
justify-content: center;
|
|
271
|
+
border: none;
|
|
272
|
+
cursor: pointer;
|
|
273
|
+
height: 16px;
|
|
274
|
+
width: 16px;
|
|
275
|
+
border-radius: 50%;
|
|
276
|
+
inset-inline-end: ${clearButtonPadding};
|
|
277
|
+
background: ${colors.grey500};
|
|
278
|
+
padding: 1px;
|
|
279
|
+
}
|
|
280
|
+
`}</style>
|
|
281
|
+
</div>
|
|
282
|
+
)
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
Input.propTypes = {
|
|
287
|
+
/** Add an aria-controls attribute to the input element **/
|
|
288
|
+
ariaControls: PropTypes.string,
|
|
289
|
+
/** Add an aria-haspopup attribute to the input element **/
|
|
290
|
+
ariaHaspopup: PropTypes.string,
|
|
291
|
+
/** Add an aria-label attribute to the input element **/
|
|
292
|
+
ariaLabel: PropTypes.string,
|
|
293
|
+
/** The [native `autocomplete` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-autocomplete) */
|
|
294
|
+
autoComplete: PropTypes.string,
|
|
295
|
+
className: PropTypes.string,
|
|
296
|
+
/** Makes the input field clearable */
|
|
297
|
+
clearable: PropTypes.bool,
|
|
298
|
+
dataTest: PropTypes.string,
|
|
299
|
+
/** Makes the input smaller */
|
|
300
|
+
dense: PropTypes.bool,
|
|
301
|
+
/** Disables the input */
|
|
302
|
+
disabled: PropTypes.bool,
|
|
303
|
+
/** Applies 'error' appearance for validation feedback. Mutually exclusive with `valid` and `warning` props */
|
|
304
|
+
error: sharedPropTypes.statusPropType,
|
|
305
|
+
/** The input grabs initial focus on the page */
|
|
306
|
+
initialFocus: PropTypes.bool,
|
|
307
|
+
/** Adds a loading indicator beside the input */
|
|
308
|
+
loading: PropTypes.bool,
|
|
309
|
+
/** The [native `max` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-max), for use when `type` is `'number'` */
|
|
310
|
+
max: PropTypes.string,
|
|
311
|
+
/** The [native `min` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-min), for use when `type` is `'number'` */
|
|
312
|
+
min: PropTypes.string,
|
|
313
|
+
/** Name associated with the input. Passed to event handler callbacks in object */
|
|
314
|
+
name: PropTypes.string,
|
|
315
|
+
/** Placeholder text for the input */
|
|
316
|
+
placeholder: PropTypes.string,
|
|
317
|
+
/** Add prefix icon */
|
|
318
|
+
prefixIcon: PropTypes.element,
|
|
319
|
+
/** Makes the input read-only */
|
|
320
|
+
readOnly: PropTypes.bool,
|
|
321
|
+
/** Sets a role attribute on the input */
|
|
322
|
+
role: PropTypes.string,
|
|
323
|
+
/** The [native `step` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-step), for use when `type` is `'number'` */
|
|
324
|
+
step: PropTypes.string,
|
|
325
|
+
tabIndex: PropTypes.string,
|
|
326
|
+
/** The native input `type` attribute */
|
|
327
|
+
type: PropTypes.oneOf(inputTypes),
|
|
328
|
+
/** Applies 'valid' appearance for validation feedback. Mutually exclusive with `error` and `warning` props */
|
|
329
|
+
valid: sharedPropTypes.statusPropType,
|
|
330
|
+
/** Value in the input. Can be used to control the component (recommended). Passed to event handler callbacks in object */
|
|
331
|
+
value: PropTypes.string,
|
|
332
|
+
/** Applies 'warning' appearance for validation feedback. Mutually exclusive with `valid` and `error` props */
|
|
333
|
+
warning: sharedPropTypes.statusPropType,
|
|
334
|
+
/** Defines the width of the input. Can be any valid CSS measurement */
|
|
335
|
+
width: PropTypes.string,
|
|
336
|
+
/** Called with signature `({ name: string, value: string }, event)` */
|
|
337
|
+
onBlur: PropTypes.func,
|
|
338
|
+
/** Called with signature `({ name: string, value: string }, event)` */
|
|
339
|
+
onChange: PropTypes.func,
|
|
340
|
+
/** Called with signature `({ name: string, value: string }, event)` */
|
|
341
|
+
onFocus: PropTypes.func,
|
|
342
|
+
/** Called with signature `({ name: string, value: string }, event)` */
|
|
343
|
+
onKeyDown: PropTypes.func,
|
|
344
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { sharedPropTypes } from '@dhis2/ui-constants'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { Input } from './index.js'
|
|
4
|
+
|
|
5
|
+
const description = `
|
|
6
|
+
An input allows a user to enter data, usually text.
|
|
7
|
+
|
|
8
|
+
Inputs are used wherever a user needs to input standard text information. Inputs are often used as part of forms. An input can also be used to capture information outside of a form, perhaps as a 'Filter' or 'Search' field.
|
|
9
|
+
|
|
10
|
+
To use a label and validation text, consider the \`InputField\` component.
|
|
11
|
+
|
|
12
|
+
Read more about Inputs and InputFields at [Design System: Inputs](https://github.com/dhis2/design-system/blob/master/atoms/inputfield.md).
|
|
13
|
+
|
|
14
|
+
\`\`\`js
|
|
15
|
+
import { Input } from '@dhis/ui'
|
|
16
|
+
\`\`\`
|
|
17
|
+
`
|
|
18
|
+
|
|
19
|
+
const inputTypeArgType = {
|
|
20
|
+
table: { type: { summary: 'string' } },
|
|
21
|
+
control: {
|
|
22
|
+
type: 'select',
|
|
23
|
+
options: [
|
|
24
|
+
'text',
|
|
25
|
+
'number',
|
|
26
|
+
'password',
|
|
27
|
+
'email',
|
|
28
|
+
'url',
|
|
29
|
+
'tel',
|
|
30
|
+
'date',
|
|
31
|
+
'datetime',
|
|
32
|
+
'datetime-local',
|
|
33
|
+
'month',
|
|
34
|
+
'week',
|
|
35
|
+
'time',
|
|
36
|
+
'search',
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const logger = ({ name, value }) =>
|
|
42
|
+
console.log(`Name: ${name}, value: ${value}`)
|
|
43
|
+
|
|
44
|
+
export default {
|
|
45
|
+
title: 'Input',
|
|
46
|
+
component: Input,
|
|
47
|
+
parameters: {
|
|
48
|
+
docs: { description: { component: description } },
|
|
49
|
+
},
|
|
50
|
+
args: {
|
|
51
|
+
name: 'defaultName',
|
|
52
|
+
onChange: logger,
|
|
53
|
+
},
|
|
54
|
+
argTypes: {
|
|
55
|
+
type: { ...inputTypeArgType },
|
|
56
|
+
valid: { ...sharedPropTypes.statusArgType },
|
|
57
|
+
warning: { ...sharedPropTypes.statusArgType },
|
|
58
|
+
error: { ...sharedPropTypes.statusArgType },
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const Template = (args) => <Input {...args} />
|
|
63
|
+
|
|
64
|
+
export const Default = Template.bind({})
|
|
65
|
+
|
|
66
|
+
export const NoPlaceholderNoValue = Template.bind({})
|
|
67
|
+
NoPlaceholderNoValue.storyName = 'No placeholder, no value'
|
|
68
|
+
|
|
69
|
+
export const PlaceholderNoValue = Template.bind({})
|
|
70
|
+
PlaceholderNoValue.args = { placeholder: 'Hold the place' }
|
|
71
|
+
PlaceholderNoValue.storyName = 'Placeholder, no value'
|
|
72
|
+
|
|
73
|
+
export const WithValue = Template.bind({})
|
|
74
|
+
WithValue.args = {
|
|
75
|
+
value: 'This is set through the value prop, which means the component is controlled.',
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const NumberMaxMinStep = Template.bind({})
|
|
79
|
+
NumberMaxMinStep.args = {
|
|
80
|
+
type: 'number',
|
|
81
|
+
max: '3',
|
|
82
|
+
min: '0',
|
|
83
|
+
step: '0.5',
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const Focus = Template.bind({})
|
|
87
|
+
Focus.args = { initialFocus: true }
|
|
88
|
+
// Disabled initial focus stories on docs page
|
|
89
|
+
Focus.parameters = { docs: { disable: true } }
|
|
90
|
+
|
|
91
|
+
export const StatusValid = Template.bind({})
|
|
92
|
+
StatusValid.args = { valid: true, value: 'This value is valid' }
|
|
93
|
+
StatusValid.storyName = 'Status: Valid'
|
|
94
|
+
|
|
95
|
+
export const StatusWarning = Template.bind({})
|
|
96
|
+
StatusWarning.args = { warning: true, value: 'This value produces a warning' }
|
|
97
|
+
StatusWarning.storyName = 'Status: Warning'
|
|
98
|
+
|
|
99
|
+
export const StatusError = Template.bind({})
|
|
100
|
+
StatusError.args = { error: true, value: 'This value produces an error' }
|
|
101
|
+
StatusError.storyName = 'Status: Error'
|
|
102
|
+
|
|
103
|
+
export const StatusLoading = Template.bind({})
|
|
104
|
+
StatusLoading.args = {
|
|
105
|
+
loading: true,
|
|
106
|
+
value: 'This value produces a loading state',
|
|
107
|
+
}
|
|
108
|
+
StatusLoading.storyName = 'Status: Loading'
|
|
109
|
+
|
|
110
|
+
export const Disabled = Template.bind({})
|
|
111
|
+
Disabled.args = { disabled: true, value: 'This field is disabled' }
|
|
112
|
+
|
|
113
|
+
export const ReadOnly = Template.bind({})
|
|
114
|
+
ReadOnly.args = { readOnly: true, value: 'This field is read-only' }
|
|
115
|
+
|
|
116
|
+
export const Dense = Template.bind({})
|
|
117
|
+
Dense.args = { dense: true, value: 'This field is dense' }
|
|
118
|
+
|
|
119
|
+
export const ValueTextOverflow = Template.bind({})
|
|
120
|
+
ValueTextOverflow.args = {
|
|
121
|
+
value: "This value is too long in order to show on a single line of the input field. It should stay on one line, not in an extra line and which wouldn't look like a standard input",
|
|
122
|
+
dense: true,
|
|
123
|
+
warning: true,
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export const RTLErrorPlaceholder = (args) => (
|
|
127
|
+
<div dir="rtl">
|
|
128
|
+
<Input {...args} />
|
|
129
|
+
</div>
|
|
130
|
+
)
|
|
131
|
+
RTLErrorPlaceholder.args = {
|
|
132
|
+
error: true,
|
|
133
|
+
placeholder: 'RTL placeholder',
|
|
134
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { render, fireEvent, screen } from '@testing-library/react'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { InputField } from '../input-field.js'
|
|
4
|
+
|
|
5
|
+
describe('<Input>', () => {
|
|
6
|
+
it('should call the onKeyDown callback when provided', () => {
|
|
7
|
+
const onKeyDown = jest.fn()
|
|
8
|
+
|
|
9
|
+
render(
|
|
10
|
+
<InputField
|
|
11
|
+
label="label"
|
|
12
|
+
name="foo"
|
|
13
|
+
value="bar"
|
|
14
|
+
onKeyDown={onKeyDown}
|
|
15
|
+
/>
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
fireEvent.keyDown(screen.getByRole('textbox'), {})
|
|
19
|
+
|
|
20
|
+
expect(onKeyDown).toHaveBeenCalledWith(
|
|
21
|
+
{ name: 'foo', value: 'bar' },
|
|
22
|
+
expect.objectContaining({})
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
expect(onKeyDown).toHaveBeenCalledTimes(1)
|
|
26
|
+
})
|
|
27
|
+
})
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Given, Then } from '@badeball/cypress-cucumber-preprocessor'
|
|
2
|
+
|
|
3
|
+
Given('a InputField with label and a required flag is rendered', () => {
|
|
4
|
+
cy.visitStory('InputField', 'With label and required')
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
Then('the required indicator is visible', () => {
|
|
8
|
+
cy.get('[data-test="dhis2-uiwidgets-inputfield-label-required"]').should(
|
|
9
|
+
'be.visible'
|
|
10
|
+
)
|
|
11
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { InputField, InputFieldProps } from './input-field.js'
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { sharedPropTypes } from '@dhis2/ui-constants'
|
|
2
|
+
import { Box } from '@dhis2-ui/box'
|
|
3
|
+
import { Field } from '@dhis2-ui/field'
|
|
4
|
+
import PropTypes from 'prop-types'
|
|
5
|
+
import React from 'react'
|
|
6
|
+
import { Input, inputTypes } from '../input/index.js'
|
|
7
|
+
|
|
8
|
+
class InputField extends React.Component {
|
|
9
|
+
static defaultProps = {
|
|
10
|
+
dataTest: 'dhis2-uiwidgets-inputfield',
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
render() {
|
|
14
|
+
const {
|
|
15
|
+
className,
|
|
16
|
+
onChange,
|
|
17
|
+
onFocus,
|
|
18
|
+
onKeyDown,
|
|
19
|
+
onBlur,
|
|
20
|
+
initialFocus,
|
|
21
|
+
type,
|
|
22
|
+
dense,
|
|
23
|
+
required,
|
|
24
|
+
label,
|
|
25
|
+
disabled,
|
|
26
|
+
readOnly,
|
|
27
|
+
placeholder,
|
|
28
|
+
name,
|
|
29
|
+
max,
|
|
30
|
+
min,
|
|
31
|
+
step,
|
|
32
|
+
valid,
|
|
33
|
+
error,
|
|
34
|
+
warning,
|
|
35
|
+
loading,
|
|
36
|
+
value,
|
|
37
|
+
tabIndex,
|
|
38
|
+
helpText,
|
|
39
|
+
validationText,
|
|
40
|
+
inputWidth,
|
|
41
|
+
autoComplete,
|
|
42
|
+
clearable,
|
|
43
|
+
prefixIcon,
|
|
44
|
+
dataTest = 'dhis2-uiwidgets-inputfield',
|
|
45
|
+
} = this.props
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<Field
|
|
49
|
+
className={className}
|
|
50
|
+
dataTest={dataTest}
|
|
51
|
+
error={error}
|
|
52
|
+
warning={warning}
|
|
53
|
+
valid={valid}
|
|
54
|
+
helpText={helpText}
|
|
55
|
+
validationText={validationText}
|
|
56
|
+
label={label}
|
|
57
|
+
name={name}
|
|
58
|
+
disabled={disabled}
|
|
59
|
+
required={required}
|
|
60
|
+
>
|
|
61
|
+
<Box width={inputWidth} minWidth="72px">
|
|
62
|
+
<Input
|
|
63
|
+
onFocus={onFocus}
|
|
64
|
+
onKeyDown={onKeyDown}
|
|
65
|
+
onBlur={onBlur}
|
|
66
|
+
onChange={onChange}
|
|
67
|
+
name={name}
|
|
68
|
+
type={type}
|
|
69
|
+
value={value || ''}
|
|
70
|
+
placeholder={placeholder}
|
|
71
|
+
disabled={disabled}
|
|
72
|
+
max={max}
|
|
73
|
+
min={min}
|
|
74
|
+
step={step}
|
|
75
|
+
valid={valid}
|
|
76
|
+
warning={warning}
|
|
77
|
+
error={error}
|
|
78
|
+
loading={loading}
|
|
79
|
+
dense={dense}
|
|
80
|
+
tabIndex={tabIndex}
|
|
81
|
+
initialFocus={initialFocus}
|
|
82
|
+
readOnly={readOnly}
|
|
83
|
+
autoComplete={autoComplete}
|
|
84
|
+
clearable={clearable}
|
|
85
|
+
prefixIcon={prefixIcon}
|
|
86
|
+
width={inputWidth}
|
|
87
|
+
/>
|
|
88
|
+
</Box>
|
|
89
|
+
</Field>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const InputFieldProps = {
|
|
95
|
+
/** The [native `autocomplete` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-autocomplete) */
|
|
96
|
+
autoComplete: PropTypes.string,
|
|
97
|
+
className: PropTypes.string,
|
|
98
|
+
/** Makes the input field clearable */
|
|
99
|
+
clearable: PropTypes.bool,
|
|
100
|
+
dataTest: PropTypes.string,
|
|
101
|
+
/** Makes the input smaller */
|
|
102
|
+
dense: PropTypes.bool,
|
|
103
|
+
/** Disables the input */
|
|
104
|
+
disabled: PropTypes.bool,
|
|
105
|
+
/** Applies 'error' appearance for validation feedback. Mutually exclusive with `valid` and `warning` props */
|
|
106
|
+
error: sharedPropTypes.statusPropType,
|
|
107
|
+
/** Guiding text for how to use this input */
|
|
108
|
+
helpText: PropTypes.string,
|
|
109
|
+
/** The input grabs initial focus on the page */
|
|
110
|
+
initialFocus: PropTypes.bool,
|
|
111
|
+
/** Defines the width of the input. Can be any valid CSS measurement */
|
|
112
|
+
inputWidth: PropTypes.string,
|
|
113
|
+
/** Label text for the input */
|
|
114
|
+
label: PropTypes.string,
|
|
115
|
+
/** Adds a loading indicator beside the input */
|
|
116
|
+
loading: PropTypes.bool,
|
|
117
|
+
/** The [native `max` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-max), for use when `type` is `'number'` */
|
|
118
|
+
max: PropTypes.string,
|
|
119
|
+
/** The [native `min` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-min), for use when `type` is `'number'` */
|
|
120
|
+
min: PropTypes.string,
|
|
121
|
+
/** Name associated with the input. Passed to event handler callbacks in object */
|
|
122
|
+
name: PropTypes.string,
|
|
123
|
+
/** Placeholder text for the input */
|
|
124
|
+
placeholder: PropTypes.string,
|
|
125
|
+
/** Add prefix icon */
|
|
126
|
+
prefixIcon: PropTypes.element,
|
|
127
|
+
/** Makes the input read-only */
|
|
128
|
+
readOnly: PropTypes.bool,
|
|
129
|
+
/** Indicates this input is required */
|
|
130
|
+
required: PropTypes.bool,
|
|
131
|
+
/** The [native `step` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-step), for use when `type` is `'number'` */
|
|
132
|
+
step: PropTypes.string,
|
|
133
|
+
tabIndex: PropTypes.string,
|
|
134
|
+
/** Type of input */
|
|
135
|
+
type: PropTypes.oneOf(inputTypes),
|
|
136
|
+
/** Applies 'valid' appearance for validation feedback. Mutually exclusive with `error` and `warning` props */
|
|
137
|
+
valid: sharedPropTypes.statusPropType,
|
|
138
|
+
/** Text below input for validation feedback. Receives styles depending on validation status */
|
|
139
|
+
validationText: PropTypes.string,
|
|
140
|
+
/** Value in the input. Can be used to control the component (recommended). Passed to event handler callbacks in object */
|
|
141
|
+
value: PropTypes.string,
|
|
142
|
+
/** Applies 'warning' appearance for validation feedback. Mutually exclusive with `valid` and `error` props */
|
|
143
|
+
warning: sharedPropTypes.statusPropType,
|
|
144
|
+
/** Called with signature `({ name: string, value: string }, event)` */
|
|
145
|
+
onBlur: PropTypes.func,
|
|
146
|
+
/** Called with signature `({ name: string, value: string }, event)` */
|
|
147
|
+
onChange: PropTypes.func,
|
|
148
|
+
/** Called with signature `({ name: string, value: string }, event)` */
|
|
149
|
+
onFocus: PropTypes.func,
|
|
150
|
+
/** Called with signature `({ name: string, value: string }, event)` */
|
|
151
|
+
onKeyDown: PropTypes.func,
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
InputField.propTypes = InputFieldProps
|
|
155
|
+
|
|
156
|
+
export { InputField, InputFieldProps }
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { sharedPropTypes } from '@dhis2/ui-constants'
|
|
2
|
+
import { IconLocation16, IconSearch16 } from '@dhis2/ui-icons'
|
|
3
|
+
import React, { useState } from 'react'
|
|
4
|
+
import { InputField } from './index.js'
|
|
5
|
+
|
|
6
|
+
const subtitle = 'Allows a user to enter data, usually text'
|
|
7
|
+
|
|
8
|
+
const description = `
|
|
9
|
+
Inputs are used wherever a user needs to input standard text information. Inputs are often used as part of forms. An input can also be used to capture information outside of a form, perhaps as a 'Filter' or 'Search' field.
|
|
10
|
+
|
|
11
|
+
InputField wraps an Input component with a label, help text, validation text, and some other features.
|
|
12
|
+
|
|
13
|
+
Please see more about options and features of inputs at [Design System: Input Field](https://github.com/dhis2/design-system/blob/master/atoms/inputfield.md#input).
|
|
14
|
+
|
|
15
|
+
\`\`\`js
|
|
16
|
+
import { InputField } from '@dhis2/ui'
|
|
17
|
+
\`\`\`
|
|
18
|
+
`
|
|
19
|
+
|
|
20
|
+
const logger = ({ name, value }) =>
|
|
21
|
+
console.log(`Name: ${name}, value: ${value}`)
|
|
22
|
+
|
|
23
|
+
const inputTypeArgType = {
|
|
24
|
+
table: { type: { summary: 'string' } },
|
|
25
|
+
control: {
|
|
26
|
+
type: 'select',
|
|
27
|
+
options: [
|
|
28
|
+
'text',
|
|
29
|
+
'number',
|
|
30
|
+
'password',
|
|
31
|
+
'email',
|
|
32
|
+
'url',
|
|
33
|
+
'tel',
|
|
34
|
+
'date',
|
|
35
|
+
'datetime',
|
|
36
|
+
'datetime-local',
|
|
37
|
+
'month',
|
|
38
|
+
'week',
|
|
39
|
+
'time',
|
|
40
|
+
'search',
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default {
|
|
46
|
+
title: 'Input Field',
|
|
47
|
+
component: InputField,
|
|
48
|
+
parameters: {
|
|
49
|
+
componentSubtitle: subtitle,
|
|
50
|
+
docs: { description: { component: description } },
|
|
51
|
+
},
|
|
52
|
+
// Default args
|
|
53
|
+
args: {
|
|
54
|
+
label: 'Default label',
|
|
55
|
+
name: 'defaultName',
|
|
56
|
+
onChange: logger,
|
|
57
|
+
},
|
|
58
|
+
argTypes: {
|
|
59
|
+
type: { ...inputTypeArgType },
|
|
60
|
+
valid: { ...sharedPropTypes.statusArgType },
|
|
61
|
+
warning: { ...sharedPropTypes.statusArgType },
|
|
62
|
+
error: { ...sharedPropTypes.statusArgType },
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const Template = (args) => <InputField {...args} />
|
|
67
|
+
|
|
68
|
+
export const Default = Template.bind({})
|
|
69
|
+
|
|
70
|
+
export const NoPlaceholderNoValue = Template.bind({})
|
|
71
|
+
NoPlaceholderNoValue.storyName = 'No placeholder, no value'
|
|
72
|
+
|
|
73
|
+
export const PlaceholderNoValue = Template.bind({})
|
|
74
|
+
PlaceholderNoValue.args = { placeholder: 'Hold the place' }
|
|
75
|
+
PlaceholderNoValue.storyName = 'Placeholder, no value'
|
|
76
|
+
|
|
77
|
+
export const WithHelpText = Template.bind({})
|
|
78
|
+
WithHelpText.args = {
|
|
79
|
+
...PlaceholderNoValue.args,
|
|
80
|
+
helpText: 'With some helping text to guide the user along',
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const WithValue = Template.bind({})
|
|
84
|
+
WithValue.args = {
|
|
85
|
+
value: 'This is set through the value prop, which means the component is controlled.',
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const Focus = Template.bind({})
|
|
89
|
+
Focus.args = { initialFocus: true }
|
|
90
|
+
// Disabled initial focus stories on docs page
|
|
91
|
+
Focus.parameters = { docs: { disable: true } }
|
|
92
|
+
|
|
93
|
+
export const StatusValid = Template.bind({})
|
|
94
|
+
StatusValid.args = { valid: true, value: 'This value is valid' }
|
|
95
|
+
StatusValid.storyName = 'Status: Valid'
|
|
96
|
+
|
|
97
|
+
export const StatusWarning = Template.bind({})
|
|
98
|
+
StatusWarning.args = { warning: true, value: 'This value produces a warning' }
|
|
99
|
+
StatusWarning.storyName = 'Status: Warning'
|
|
100
|
+
|
|
101
|
+
export const StatusError = Template.bind({})
|
|
102
|
+
StatusError.args = {
|
|
103
|
+
error: true,
|
|
104
|
+
value: 'This value produces an error',
|
|
105
|
+
helpText: 'This is some help text to advise what this input actually is.',
|
|
106
|
+
validationText:
|
|
107
|
+
'This validation text describes the error, if a message is supplied.',
|
|
108
|
+
}
|
|
109
|
+
StatusError.storyName = 'Status: Error'
|
|
110
|
+
|
|
111
|
+
export const StatusLoading = Template.bind({})
|
|
112
|
+
StatusLoading.args = {
|
|
113
|
+
loading: true,
|
|
114
|
+
value: 'This value produces a loading state',
|
|
115
|
+
}
|
|
116
|
+
StatusLoading.storyName = 'Status: Loading'
|
|
117
|
+
|
|
118
|
+
export const Disabled = Template.bind({})
|
|
119
|
+
Disabled.args = { disabled: true, value: 'This field is disabled' }
|
|
120
|
+
|
|
121
|
+
export const ReadOnly = Template.bind({})
|
|
122
|
+
ReadOnly.args = { readOnly: true, value: 'This field is read-only' }
|
|
123
|
+
|
|
124
|
+
export const Dense = Template.bind({})
|
|
125
|
+
Dense.args = { dense: true, value: 'This field is dense' }
|
|
126
|
+
|
|
127
|
+
export const InputWidth = (args) => (
|
|
128
|
+
<>
|
|
129
|
+
<InputField
|
|
130
|
+
{...args}
|
|
131
|
+
name="input1"
|
|
132
|
+
label="My inputField has a width of 100px"
|
|
133
|
+
inputWidth="100px"
|
|
134
|
+
/>
|
|
135
|
+
<InputField
|
|
136
|
+
{...args}
|
|
137
|
+
name="input2"
|
|
138
|
+
label="My inputField has a width of 220px"
|
|
139
|
+
inputWidth="220px"
|
|
140
|
+
/>
|
|
141
|
+
</>
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
export const LabelTextOverflow = Template.bind({})
|
|
145
|
+
LabelTextOverflow.args = {
|
|
146
|
+
dense: true,
|
|
147
|
+
warning: true,
|
|
148
|
+
label: "This label is too long to show on a single line of the input field's label. We just let it flow to the next line so the user can still read it. However, we should always aim to keep it shorter than this!",
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export const ValueTextOverflow = Template.bind({})
|
|
152
|
+
ValueTextOverflow.args = {
|
|
153
|
+
value: "This value is too long in order to show on a single line of the input field. It should stay on one line, not in an extra line and which wouldn't look like a standard input",
|
|
154
|
+
dense: true,
|
|
155
|
+
warning: true,
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export const Required = Template.bind({})
|
|
159
|
+
Required.args = { required: true }
|
|
160
|
+
|
|
161
|
+
export const InputWithPrefixIcon = (args) => (
|
|
162
|
+
<>
|
|
163
|
+
<InputField
|
|
164
|
+
{...args}
|
|
165
|
+
name="prefix-icon-input"
|
|
166
|
+
label="Search"
|
|
167
|
+
placeholder={'Search'}
|
|
168
|
+
prefixIcon={<IconSearch16 />}
|
|
169
|
+
/>
|
|
170
|
+
<InputField
|
|
171
|
+
{...args}
|
|
172
|
+
name="prefix-icon-input"
|
|
173
|
+
label="Location"
|
|
174
|
+
placeholder={'Enter Location'}
|
|
175
|
+
prefixIcon={<IconLocation16 />}
|
|
176
|
+
inputWidth={'200px'}
|
|
177
|
+
/>
|
|
178
|
+
</>
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
export const ClearableInput = (args) => {
|
|
182
|
+
const [value, setValue] = useState('value')
|
|
183
|
+
return (
|
|
184
|
+
<InputField
|
|
185
|
+
{...args}
|
|
186
|
+
name="clearable-input"
|
|
187
|
+
label="This field can be cleared"
|
|
188
|
+
placeholder={''}
|
|
189
|
+
onChange={(e) => setValue(e.value)}
|
|
190
|
+
clearable
|
|
191
|
+
clearText={() => setValue('')}
|
|
192
|
+
value={value}
|
|
193
|
+
/>
|
|
194
|
+
)
|
|
195
|
+
}
|