@dhis2-ui/text-area 10.16.2 → 10.16.3
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 +9 -8
- package/src/index.js +2 -0
- package/src/text-area/__tests__/text-area.test.js +20 -0
- package/src/text-area/features/accepts_initial_focus/index.js +9 -0
- package/src/text-area/features/accepts_initial_focus.feature +5 -0
- package/src/text-area/features/can_be_blurred/index.js +18 -0
- package/src/text-area/features/can_be_blurred.feature +6 -0
- package/src/text-area/features/can_be_changed/index.js +18 -0
- package/src/text-area/features/can_be_changed.feature +6 -0
- package/src/text-area/features/can_be_disabled/index.js +15 -0
- package/src/text-area/features/can_be_disabled.feature +6 -0
- package/src/text-area/features/can_be_focused/index.js +18 -0
- package/src/text-area/features/can_be_focused.feature +6 -0
- package/src/text-area/features/common/index.js +5 -0
- package/src/text-area/index.js +1 -0
- package/src/text-area/text-area.e2e.stories.js +19 -0
- package/src/text-area/text-area.js +224 -0
- package/src/text-area/text-area.prod.stories.js +219 -0
- package/src/text-area/text-area.styles.js +60 -0
- package/src/text-area-field/__tests__/text-area-field.test.js +20 -0
- package/src/text-area-field/features/can_be_required/index.js +11 -0
- package/src/text-area-field/features/can_be_required.feature +5 -0
- package/src/text-area-field/index.js +1 -0
- package/src/text-area-field/text-area-field.e2e.stories.js +11 -0
- package/src/text-area-field/text-area-field.js +127 -0
- package/src/text-area-field/text-area-field.prod.stories.js +221 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dhis2-ui/text-area",
|
|
3
|
-
"version": "10.16.
|
|
3
|
+
"version": "10.16.3",
|
|
4
4
|
"description": "UI TextArea",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -33,18 +33,19 @@
|
|
|
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/loader": "10.16.
|
|
39
|
-
"@dhis2-ui/status-icon": "10.16.
|
|
40
|
-
"@dhis2/ui-constants": "10.16.
|
|
41
|
-
"@dhis2/ui-icons": "10.16.
|
|
36
|
+
"@dhis2-ui/box": "10.16.3",
|
|
37
|
+
"@dhis2-ui/field": "10.16.3",
|
|
38
|
+
"@dhis2-ui/loader": "10.16.3",
|
|
39
|
+
"@dhis2-ui/status-icon": "10.16.3",
|
|
40
|
+
"@dhis2/ui-constants": "10.16.3",
|
|
41
|
+
"@dhis2/ui-icons": "10.16.3",
|
|
42
42
|
"classnames": "^2.3.1",
|
|
43
43
|
"prop-types": "^15.7.2"
|
|
44
44
|
},
|
|
45
45
|
"files": [
|
|
46
46
|
"build",
|
|
47
|
-
"types"
|
|
47
|
+
"types",
|
|
48
|
+
"src"
|
|
48
49
|
],
|
|
49
50
|
"devDependencies": {
|
|
50
51
|
"react": "^18.3.1",
|
package/src/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { render, fireEvent, screen } from '@testing-library/react'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { TextArea } from '../text-area.js'
|
|
4
|
+
|
|
5
|
+
describe('<TextArea>', () => {
|
|
6
|
+
it('should call the onKeyDown callback when provided', () => {
|
|
7
|
+
const onKeyDown = jest.fn()
|
|
8
|
+
|
|
9
|
+
render(<TextArea name="foo" value="bar" onKeyDown={onKeyDown} />)
|
|
10
|
+
|
|
11
|
+
fireEvent.keyDown(screen.getByRole('textbox'), {})
|
|
12
|
+
|
|
13
|
+
expect(onKeyDown).toHaveBeenCalledWith(
|
|
14
|
+
{ name: 'foo', value: 'bar' },
|
|
15
|
+
expect.objectContaining({})
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
expect(onKeyDown).toHaveBeenCalledTimes(1)
|
|
19
|
+
})
|
|
20
|
+
})
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Given, Then } from '@badeball/cypress-cucumber-preprocessor'
|
|
2
|
+
|
|
3
|
+
Given('a TextArea with initialFocus is rendered', () => {
|
|
4
|
+
cy.visitStory('TextArea', 'With initial focus')
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
Then('the TextArea is focused', () => {
|
|
8
|
+
cy.focused().parent('[data-test="dhis2-uicore-textarea"]').should('exist')
|
|
9
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'
|
|
2
|
+
|
|
3
|
+
Given('a TextArea with initialFocus and onBlur handler is rendered', () => {
|
|
4
|
+
cy.visitStory('TextArea', 'With initial focus and on blur')
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
When('the TextArea is blurred', () => {
|
|
8
|
+
cy.get('[data-test="dhis2-uicore-textarea"] textarea').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: 'textarea',
|
|
16
|
+
})
|
|
17
|
+
})
|
|
18
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'
|
|
2
|
+
|
|
3
|
+
Given('a TextArea with onChange handler is rendered', () => {
|
|
4
|
+
cy.visitStory('TextArea', 'With on change')
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
When('the TextArea is filled with a character', () => {
|
|
8
|
+
cy.get('[data-test="dhis2-uicore-textarea"]').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: 'textarea',
|
|
16
|
+
})
|
|
17
|
+
})
|
|
18
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'
|
|
2
|
+
|
|
3
|
+
Given('a disabled TextArea is rendered', () => {
|
|
4
|
+
cy.visitStory('TextArea', 'With disabled')
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
When('the user clicks the TextArea', () => {
|
|
8
|
+
cy.get('[data-test="dhis2-uicore-textarea"] textarea').click({
|
|
9
|
+
force: true,
|
|
10
|
+
})
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
Then('the TextArea is not focused', () => {
|
|
14
|
+
cy.focused().should('not.exist')
|
|
15
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'
|
|
2
|
+
|
|
3
|
+
Given('a TextArea with onFocus handler is rendered', () => {
|
|
4
|
+
cy.visitStory('TextArea', 'With on focus')
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
When('the TextArea is focused', () => {
|
|
8
|
+
cy.get('[data-test="dhis2-uicore-textarea"] textarea').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: 'textarea',
|
|
16
|
+
})
|
|
17
|
+
})
|
|
18
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { TextArea } from './text-area.js'
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { TextArea } 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: 'TextArea' }
|
|
9
|
+
export const WithOnChange = () => (
|
|
10
|
+
<TextArea onChange={window.onChange} name="textarea" />
|
|
11
|
+
)
|
|
12
|
+
export const WithInitialFocusAndOnBlur = () => (
|
|
13
|
+
<TextArea initialFocus name="textarea" onBlur={window.onBlur} />
|
|
14
|
+
)
|
|
15
|
+
export const WithOnFocus = () => (
|
|
16
|
+
<TextArea name="textarea" onFocus={window.onFocus} />
|
|
17
|
+
)
|
|
18
|
+
export const WithInitialFocus = () => <TextArea name="textarea" initialFocus />
|
|
19
|
+
export const WithDisabled = () => <TextArea name="textarea" disabled />
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { sharedPropTypes } from '@dhis2/ui-constants'
|
|
2
|
+
import { StatusIcon } from '@dhis2-ui/status-icon'
|
|
3
|
+
import cx from 'classnames'
|
|
4
|
+
import PropTypes from 'prop-types'
|
|
5
|
+
import React, { Component } from 'react'
|
|
6
|
+
import { styles } from './text-area.styles.js'
|
|
7
|
+
|
|
8
|
+
export class TextArea extends Component {
|
|
9
|
+
textareaRef = React.createRef()
|
|
10
|
+
state = {
|
|
11
|
+
height: 'auto',
|
|
12
|
+
}
|
|
13
|
+
textareaDimensions = { width: 0, height: 0 }
|
|
14
|
+
userHasResized = false
|
|
15
|
+
|
|
16
|
+
componentDidMount() {
|
|
17
|
+
this.attachResizeListener()
|
|
18
|
+
|
|
19
|
+
if (this.props.initialFocus) {
|
|
20
|
+
this.textareaRef.current.focus()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (this.shouldDoAutoGrow()) {
|
|
24
|
+
this.setHeight()
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
componentDidUpdate(prevProps) {
|
|
29
|
+
if (this.shouldDoAutoGrow() && this.props.value !== prevProps.value) {
|
|
30
|
+
this.setHeight()
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
attachResizeListener() {
|
|
35
|
+
const textarea = this.textareaRef.current
|
|
36
|
+
textarea.addEventListener('mousedown', this.setTextareaDimensions)
|
|
37
|
+
textarea.addEventListener('mouseup', this.hasUserResized)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
removeResizeListener() {
|
|
41
|
+
const textarea = this.textareaRef.current
|
|
42
|
+
textarea.removeEventListener('mousedown', this.setTextareaDimensions)
|
|
43
|
+
textarea.removeEventListener('mouseup', this.hasUserResized)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
setHeight() {
|
|
47
|
+
const textarea = this.textareaRef.current
|
|
48
|
+
const offset = textarea.offsetHeight - textarea.clientHeight
|
|
49
|
+
const height = textarea.scrollHeight + offset + 'px'
|
|
50
|
+
this.setState({ height })
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
setTextareaDimensions = () => {
|
|
54
|
+
const textarea = this.textareaRef.current
|
|
55
|
+
this.textareaDimensions = {
|
|
56
|
+
width: textarea.clientWidth,
|
|
57
|
+
height: textarea.clientHeight,
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
shouldDoAutoGrow() {
|
|
62
|
+
return this.props.autoGrow && !this.userHasResized
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
hasUserResized = () => {
|
|
66
|
+
const { width: oldWidth, height: oldHeight } = this.textareaDimensions
|
|
67
|
+
|
|
68
|
+
this.setTextareaDimensions()
|
|
69
|
+
|
|
70
|
+
const { width: newWidth, height: newHeight } = this.textareaDimensions
|
|
71
|
+
const userHasResized = newWidth !== oldWidth || newHeight !== oldHeight
|
|
72
|
+
|
|
73
|
+
if (userHasResized) {
|
|
74
|
+
this.userHasResized = true
|
|
75
|
+
this.removeResizeListener()
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
handleChange = (e) => {
|
|
80
|
+
if (this.props.onChange) {
|
|
81
|
+
this.props.onChange(this.createHandlerPayload(e), e)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
handleBlur = (e) => {
|
|
86
|
+
if (this.props.onBlur) {
|
|
87
|
+
this.props.onBlur(this.createHandlerPayload(e), e)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
handleFocus = (e) => {
|
|
92
|
+
if (this.props.onFocus) {
|
|
93
|
+
this.props.onFocus(this.createHandlerPayload(e), e)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
handleKeyDown = (e) => {
|
|
98
|
+
if (this.props.onKeyDown) {
|
|
99
|
+
this.props.onKeyDown(this.createHandlerPayload(e), e)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
createHandlerPayload(e) {
|
|
104
|
+
return {
|
|
105
|
+
value: e.target.value,
|
|
106
|
+
name: this.props.name,
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
static defaultProps = {
|
|
110
|
+
rows: 4,
|
|
111
|
+
width: '100%',
|
|
112
|
+
resize: 'vertical',
|
|
113
|
+
dataTest: 'dhis2-uicore-textarea',
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
render() {
|
|
117
|
+
const {
|
|
118
|
+
className,
|
|
119
|
+
dense,
|
|
120
|
+
disabled,
|
|
121
|
+
readOnly,
|
|
122
|
+
placeholder,
|
|
123
|
+
name,
|
|
124
|
+
valid,
|
|
125
|
+
error,
|
|
126
|
+
warning,
|
|
127
|
+
loading,
|
|
128
|
+
value,
|
|
129
|
+
tabIndex,
|
|
130
|
+
resize = 'vertical',
|
|
131
|
+
rows = 4,
|
|
132
|
+
width = '100%',
|
|
133
|
+
dataTest = 'dhis2-uicore-textarea',
|
|
134
|
+
} = this.props
|
|
135
|
+
const { height } = this.state
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<div className={cx('textarea', className)} data-test={dataTest}>
|
|
139
|
+
<textarea
|
|
140
|
+
id={name}
|
|
141
|
+
name={name}
|
|
142
|
+
placeholder={placeholder}
|
|
143
|
+
ref={this.textareaRef}
|
|
144
|
+
value={value}
|
|
145
|
+
disabled={disabled}
|
|
146
|
+
readOnly={readOnly}
|
|
147
|
+
tabIndex={tabIndex}
|
|
148
|
+
onFocus={this.handleFocus}
|
|
149
|
+
onKeyDown={this.handleKeyDown}
|
|
150
|
+
onBlur={this.handleBlur}
|
|
151
|
+
onChange={this.handleChange}
|
|
152
|
+
rows={rows}
|
|
153
|
+
className={cx({
|
|
154
|
+
dense,
|
|
155
|
+
disabled,
|
|
156
|
+
error,
|
|
157
|
+
valid,
|
|
158
|
+
warning,
|
|
159
|
+
'read-only': readOnly,
|
|
160
|
+
})}
|
|
161
|
+
/>
|
|
162
|
+
<StatusIcon
|
|
163
|
+
error={error}
|
|
164
|
+
valid={valid}
|
|
165
|
+
loading={loading}
|
|
166
|
+
warning={warning}
|
|
167
|
+
/>
|
|
168
|
+
|
|
169
|
+
<style jsx>{styles}</style>
|
|
170
|
+
<style jsx>{`
|
|
171
|
+
textarea {
|
|
172
|
+
width: ${width};
|
|
173
|
+
height: ${height};
|
|
174
|
+
resize: ${resize};
|
|
175
|
+
}
|
|
176
|
+
`}</style>
|
|
177
|
+
</div>
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
TextArea.propTypes = {
|
|
183
|
+
/** Grow the text area in response to overflow instead of adding a scroll bar */
|
|
184
|
+
autoGrow: PropTypes.bool,
|
|
185
|
+
className: PropTypes.string,
|
|
186
|
+
dataTest: PropTypes.string,
|
|
187
|
+
/** Compact mode */
|
|
188
|
+
dense: PropTypes.bool,
|
|
189
|
+
/** Disables the textarea and makes in non-interactive */
|
|
190
|
+
disabled: PropTypes.bool,
|
|
191
|
+
/** Applies 'error' styles for validation feedback. Mutually exclusive with `valid` and `warning` props */
|
|
192
|
+
error: sharedPropTypes.statusPropType,
|
|
193
|
+
/** Grabs initial focus on the page */
|
|
194
|
+
initialFocus: PropTypes.bool,
|
|
195
|
+
/** Adds a loading spinner */
|
|
196
|
+
loading: PropTypes.bool,
|
|
197
|
+
/** Name associated with the text area. Passed in object argument to event handlers. */
|
|
198
|
+
name: PropTypes.string,
|
|
199
|
+
/** Placeholder text for an empty textarea */
|
|
200
|
+
placeholder: PropTypes.string,
|
|
201
|
+
/** Makes the textarea read-only */
|
|
202
|
+
readOnly: PropTypes.bool,
|
|
203
|
+
/** [Resize property](https://developer.mozilla.org/en-US/docs/Web/CSS/resize) for the textarea element */
|
|
204
|
+
resize: PropTypes.oneOf(['none', 'both', 'horizontal', 'vertical']),
|
|
205
|
+
/** Initial height of the textarea, in lines of text */
|
|
206
|
+
rows: PropTypes.number,
|
|
207
|
+
tabIndex: PropTypes.string,
|
|
208
|
+
/** Applies 'valid' styles for validation feedback. Mutually exclusive with `warning` and `error` props */
|
|
209
|
+
valid: sharedPropTypes.statusPropType,
|
|
210
|
+
/** Value in the textarea. Can be used to control component (recommended). Passed in object argument to event handlers. */
|
|
211
|
+
value: PropTypes.string,
|
|
212
|
+
/** Applies 'warning' styles for validation feedback. Mutually exclusive with `valid` and `error` props */
|
|
213
|
+
warning: sharedPropTypes.statusPropType,
|
|
214
|
+
/** Width of the text area. Can be any valid CSS measurement */
|
|
215
|
+
width: PropTypes.string,
|
|
216
|
+
/** Called with signature `({ name: string, value: string }, event)` */
|
|
217
|
+
onBlur: PropTypes.func,
|
|
218
|
+
/** Called with signature `({ name: string, value: string }, event)` */
|
|
219
|
+
onChange: PropTypes.func,
|
|
220
|
+
/** Called with signature `({ name: string, value: string }, event)` */
|
|
221
|
+
onFocus: PropTypes.func,
|
|
222
|
+
/** Called with signature `({ name: string, value: string }, event)` */
|
|
223
|
+
onKeyDown: PropTypes.func,
|
|
224
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { sharedPropTypes } from '@dhis2/ui-constants'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { TextArea } from './index.js'
|
|
4
|
+
|
|
5
|
+
const description = `
|
|
6
|
+
A textarea allows multiple lines of text input. Use a textarea wherever a user needs to input a lot of information. Do not use a textarea if a short, single line of content is expected.
|
|
7
|
+
|
|
8
|
+
Options for textarea inputs are:
|
|
9
|
+
|
|
10
|
+
- Rows: the height of the input, defined by the number of rows of text
|
|
11
|
+
- Resizable: whether the textarea can be resized by the user or not. Can be set for both width and height.
|
|
12
|
+
- Autoheight: if enabled, the texarea will grow in height to adapt to the content.
|
|
13
|
+
|
|
14
|
+
\`\`\`js
|
|
15
|
+
import { TextArea } from '@dhis2/ui'
|
|
16
|
+
\`\`\`
|
|
17
|
+
`
|
|
18
|
+
|
|
19
|
+
window.onChange = (payload, event) => {
|
|
20
|
+
console.log('onChange payload', payload)
|
|
21
|
+
console.log('onChange event', event)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
window.onFocus = (payload, event) => {
|
|
25
|
+
console.log('onFocus payload', payload)
|
|
26
|
+
console.log('onFocus event', event)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
window.onBlur = (payload, event) => {
|
|
30
|
+
console.log('onBlur payload', payload)
|
|
31
|
+
console.log('onBlur event', event)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const onChange = (...args) => window.onChange(...args)
|
|
35
|
+
const onFocus = (...args) => window.onFocus(...args)
|
|
36
|
+
const onBlur = (...args) => window.onBlur(...args)
|
|
37
|
+
|
|
38
|
+
export default {
|
|
39
|
+
title: 'Text Area',
|
|
40
|
+
component: TextArea,
|
|
41
|
+
parameters: { docs: { description: { component: description } } },
|
|
42
|
+
argTypes: {
|
|
43
|
+
valid: { ...sharedPropTypes.statusArgType },
|
|
44
|
+
error: { ...sharedPropTypes.statusArgType },
|
|
45
|
+
warning: { ...sharedPropTypes.statusArgType },
|
|
46
|
+
},
|
|
47
|
+
args: {
|
|
48
|
+
name: 'textAreaName',
|
|
49
|
+
onChange,
|
|
50
|
+
onFocus,
|
|
51
|
+
onBlur,
|
|
52
|
+
},
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const Template = (args) => <TextArea {...args} />
|
|
56
|
+
|
|
57
|
+
export const Default = Template.bind({})
|
|
58
|
+
|
|
59
|
+
export const PlaceholderNoValue = Template.bind({})
|
|
60
|
+
PlaceholderNoValue.args = { placeholder: 'Hold the place' }
|
|
61
|
+
PlaceholderNoValue.storyName = 'Placeholder, no value'
|
|
62
|
+
|
|
63
|
+
export const WithValue = Template.bind({})
|
|
64
|
+
WithValue.args = {
|
|
65
|
+
value: 'This is set through the value prop, which means the component is controlled.',
|
|
66
|
+
}
|
|
67
|
+
WithValue.storyName = 'With value'
|
|
68
|
+
|
|
69
|
+
export const Focus = (args) => (
|
|
70
|
+
<>
|
|
71
|
+
<TextArea {...args} initialFocus className="initially-focused" />
|
|
72
|
+
<TextArea {...args} className="initially-unfocused" />
|
|
73
|
+
</>
|
|
74
|
+
)
|
|
75
|
+
Focus.parameters = { docs: { disable: true } }
|
|
76
|
+
|
|
77
|
+
export const StatusValid = Template.bind({})
|
|
78
|
+
StatusValid.args = { valid: true, value: 'This value is valid' }
|
|
79
|
+
StatusValid.storyName = 'Status: Valid'
|
|
80
|
+
|
|
81
|
+
export const StatusWarning = Template.bind({})
|
|
82
|
+
StatusWarning.args = { warning: true, value: 'This value produces a warning' }
|
|
83
|
+
StatusWarning.storyName = 'Status: Warning'
|
|
84
|
+
|
|
85
|
+
export const StatusError = Template.bind({})
|
|
86
|
+
StatusError.args = {
|
|
87
|
+
error: true,
|
|
88
|
+
value: 'This value produces an error',
|
|
89
|
+
helpText: 'This is some help text to advise what this input actually is.',
|
|
90
|
+
validationText: 'This describes the error, if a message is supplied.',
|
|
91
|
+
}
|
|
92
|
+
StatusError.storyName = 'Status: Error'
|
|
93
|
+
|
|
94
|
+
export const StatusLoading = Template.bind({})
|
|
95
|
+
StatusLoading.args = {
|
|
96
|
+
loading: true,
|
|
97
|
+
value: 'This value produces a loadingn state',
|
|
98
|
+
}
|
|
99
|
+
StatusLoading.storyName = 'Status: Loading'
|
|
100
|
+
|
|
101
|
+
export const Disabled = Template.bind({})
|
|
102
|
+
Disabled.args = { disabled: true, value: 'This field is disabled' }
|
|
103
|
+
|
|
104
|
+
export const ReadOnly = Template.bind({})
|
|
105
|
+
ReadOnly.args = { readOnly: true, value: 'This field is readOnly' }
|
|
106
|
+
|
|
107
|
+
export const Dense = Template.bind({})
|
|
108
|
+
Dense.args = { dense: true, value: 'This field is dense' }
|
|
109
|
+
|
|
110
|
+
export const TextareaTextOverflow = Template.bind({})
|
|
111
|
+
TextareaTextOverflow.args = {
|
|
112
|
+
label: 'I have a scrollbar',
|
|
113
|
+
value: [
|
|
114
|
+
'A line of text',
|
|
115
|
+
'A line of text',
|
|
116
|
+
'A line of text',
|
|
117
|
+
'A line of text',
|
|
118
|
+
'A line of text',
|
|
119
|
+
'A line of text',
|
|
120
|
+
'A line of text',
|
|
121
|
+
'A line of text',
|
|
122
|
+
'A line of text',
|
|
123
|
+
'A line of text',
|
|
124
|
+
'A line of text',
|
|
125
|
+
'A line of text',
|
|
126
|
+
'A line of text',
|
|
127
|
+
'A line of text',
|
|
128
|
+
'A line of text',
|
|
129
|
+
].join('\n'),
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export const Rows = Template.bind({})
|
|
133
|
+
Rows.args = {
|
|
134
|
+
rows: 8,
|
|
135
|
+
label: 'You can set the height with the rows prop. I have 8',
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export const Resize = (args) => (
|
|
139
|
+
<>
|
|
140
|
+
<TextArea
|
|
141
|
+
{...args}
|
|
142
|
+
name="textarea1"
|
|
143
|
+
label="Resize: vertical (default)"
|
|
144
|
+
/>
|
|
145
|
+
<TextArea
|
|
146
|
+
{...args}
|
|
147
|
+
name="textarea2"
|
|
148
|
+
label="Resize: none"
|
|
149
|
+
resize="none"
|
|
150
|
+
/>
|
|
151
|
+
<TextArea
|
|
152
|
+
{...args}
|
|
153
|
+
name="textarea3"
|
|
154
|
+
label="Resize: both"
|
|
155
|
+
resize="both"
|
|
156
|
+
/>
|
|
157
|
+
<TextArea
|
|
158
|
+
{...args}
|
|
159
|
+
name="textarea4"
|
|
160
|
+
label="Resize: horizontal"
|
|
161
|
+
resize="horizontal"
|
|
162
|
+
/>
|
|
163
|
+
</>
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
export const Autogrow = (args) => (
|
|
167
|
+
<>
|
|
168
|
+
<TextArea
|
|
169
|
+
{...args}
|
|
170
|
+
name="textarea1"
|
|
171
|
+
label="Autogrow step 1"
|
|
172
|
+
autoGrow
|
|
173
|
+
rows={2}
|
|
174
|
+
value="This TextArea has a height of 2 rows"
|
|
175
|
+
/>
|
|
176
|
+
<TextArea
|
|
177
|
+
{...args}
|
|
178
|
+
name="textarea2"
|
|
179
|
+
label="Autogrow step 2"
|
|
180
|
+
autoGrow
|
|
181
|
+
rows={2}
|
|
182
|
+
value={[
|
|
183
|
+
'This TextArea has a height of two rows',
|
|
184
|
+
'it also has autoGrow set to true so it will grow with the content',
|
|
185
|
+
].join('\n')}
|
|
186
|
+
/>
|
|
187
|
+
<TextArea
|
|
188
|
+
{...args}
|
|
189
|
+
name="textarea3"
|
|
190
|
+
label="Autogrow step 3"
|
|
191
|
+
autoGrow
|
|
192
|
+
rows={2}
|
|
193
|
+
value={[
|
|
194
|
+
'This TextArea has a height of two rows',
|
|
195
|
+
'it also has autoGrow set to true so it will grow with the content.',
|
|
196
|
+
'See: rows is still 2, but I now have 3 lines.',
|
|
197
|
+
].join('\n')}
|
|
198
|
+
/>
|
|
199
|
+
<TextArea
|
|
200
|
+
{...args}
|
|
201
|
+
name="textarea4"
|
|
202
|
+
label="Autogrow step 4"
|
|
203
|
+
value={[
|
|
204
|
+
'This TextArea has a height of two rows',
|
|
205
|
+
'it also has autoGrow set to true so it will grow with the content.',
|
|
206
|
+
'See: rows is still 2...',
|
|
207
|
+
'And now I have 4 lines and still no scroll bar in sight.',
|
|
208
|
+
].join('\n')}
|
|
209
|
+
/>
|
|
210
|
+
</>
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
export const RTL = (args) => (
|
|
214
|
+
<div dir="rtl">
|
|
215
|
+
<Template {...args} />
|
|
216
|
+
</div>
|
|
217
|
+
)
|
|
218
|
+
RTL.args = { valid: true, value: 'This RTL text is valid' }
|
|
219
|
+
RTL.storyName = 'RTL: Valid'
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { colors, theme, spacers } from '@dhis2/ui-constants'
|
|
2
|
+
import css from 'styled-jsx/css'
|
|
3
|
+
|
|
4
|
+
export const styles = css`
|
|
5
|
+
.textarea {
|
|
6
|
+
display: flex;
|
|
7
|
+
gap: ${spacers.dp8};
|
|
8
|
+
}
|
|
9
|
+
textarea {
|
|
10
|
+
box-sizing: border-box;
|
|
11
|
+
padding: 8px 12px;
|
|
12
|
+
|
|
13
|
+
color: ${colors.grey900};
|
|
14
|
+
background-color: white;
|
|
15
|
+
|
|
16
|
+
border: 1px solid ${colors.grey500};
|
|
17
|
+
border-radius: 3px;
|
|
18
|
+
box-shadow: inset 0 0 1px 0 rgba(48, 54, 60, 0.1);
|
|
19
|
+
outline: 0;
|
|
20
|
+
|
|
21
|
+
font-size: 14px;
|
|
22
|
+
line-height: 17px;
|
|
23
|
+
user-select: text;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
textarea.dense {
|
|
27
|
+
padding: 6px 8px;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
textarea:focus {
|
|
31
|
+
outline: none;
|
|
32
|
+
box-shadow: inset 0 0 0 2px ${theme.focus};
|
|
33
|
+
border-color: ${theme.focus};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
textarea.valid {
|
|
37
|
+
border-color: ${theme.valid};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
textarea.warning {
|
|
41
|
+
border-color: ${theme.warning};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
textarea.error {
|
|
45
|
+
border-color: ${theme.error};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
textarea.read-only {
|
|
49
|
+
background-color: ${colors.grey100};
|
|
50
|
+
border-color: ${colors.grey500};
|
|
51
|
+
cursor: text;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
textarea.disabled {
|
|
55
|
+
background-color: ${colors.grey100};
|
|
56
|
+
border-color: ${colors.grey500};
|
|
57
|
+
color: ${theme.disabled};
|
|
58
|
+
cursor: not-allowed;
|
|
59
|
+
}
|
|
60
|
+
`
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { render, fireEvent, screen } from '@testing-library/react'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { TextAreaField } from '../text-area-field.js'
|
|
4
|
+
|
|
5
|
+
describe('<TextArea>', () => {
|
|
6
|
+
it('should call the onKeyDown callback when provided', () => {
|
|
7
|
+
const onKeyDown = jest.fn()
|
|
8
|
+
|
|
9
|
+
render(<TextAreaField name="foo" value="bar" onKeyDown={onKeyDown} />)
|
|
10
|
+
|
|
11
|
+
fireEvent.keyDown(screen.getByRole('textbox'), {})
|
|
12
|
+
|
|
13
|
+
expect(onKeyDown).toHaveBeenCalledWith(
|
|
14
|
+
{ name: 'foo', value: 'bar' },
|
|
15
|
+
expect.objectContaining({})
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
expect(onKeyDown).toHaveBeenCalledTimes(1)
|
|
19
|
+
})
|
|
20
|
+
})
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Given, Then } from '@badeball/cypress-cucumber-preprocessor'
|
|
2
|
+
|
|
3
|
+
Given('a TextAreaField with label and a required flag is rendered', () => {
|
|
4
|
+
cy.visitStory('TextAreaField', 'With label and required')
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
Then('the required indicator is visible', () => {
|
|
8
|
+
cy.get('[data-test="dhis2-uiwidgets-textareafield-label-required"]').should(
|
|
9
|
+
'be.visible'
|
|
10
|
+
)
|
|
11
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { TextAreaField } from './text-area-field.js'
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { TextAreaField } from './index.js'
|
|
3
|
+
|
|
4
|
+
export default { title: 'TextAreaField' }
|
|
5
|
+
export const WithLabelAndRequired = () => (
|
|
6
|
+
<TextAreaField
|
|
7
|
+
name="textarea"
|
|
8
|
+
label="I am required and have an asterisk"
|
|
9
|
+
required
|
|
10
|
+
/>
|
|
11
|
+
)
|
|
@@ -0,0 +1,127 @@
|
|
|
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 { TextArea } from '../text-area/index.js'
|
|
7
|
+
|
|
8
|
+
const TextAreaField = ({
|
|
9
|
+
className,
|
|
10
|
+
onChange,
|
|
11
|
+
onFocus,
|
|
12
|
+
onKeyDown,
|
|
13
|
+
onBlur,
|
|
14
|
+
initialFocus,
|
|
15
|
+
dense,
|
|
16
|
+
required,
|
|
17
|
+
label,
|
|
18
|
+
disabled,
|
|
19
|
+
placeholder,
|
|
20
|
+
name,
|
|
21
|
+
valid,
|
|
22
|
+
error,
|
|
23
|
+
warning,
|
|
24
|
+
loading,
|
|
25
|
+
value,
|
|
26
|
+
tabIndex,
|
|
27
|
+
helpText,
|
|
28
|
+
validationText,
|
|
29
|
+
autoGrow,
|
|
30
|
+
readOnly,
|
|
31
|
+
resize = 'vertical',
|
|
32
|
+
rows = 4,
|
|
33
|
+
inputWidth,
|
|
34
|
+
dataTest = 'dhis2-uiwidgets-textareafield',
|
|
35
|
+
}) => (
|
|
36
|
+
<Field
|
|
37
|
+
className={className}
|
|
38
|
+
dataTest={dataTest}
|
|
39
|
+
disabled={disabled}
|
|
40
|
+
required={required}
|
|
41
|
+
name={name}
|
|
42
|
+
helpText={helpText}
|
|
43
|
+
validationText={validationText}
|
|
44
|
+
error={error}
|
|
45
|
+
warning={warning}
|
|
46
|
+
valid={valid}
|
|
47
|
+
label={label}
|
|
48
|
+
>
|
|
49
|
+
<Box width={inputWidth} minWidth="220px">
|
|
50
|
+
<TextArea
|
|
51
|
+
onFocus={onFocus}
|
|
52
|
+
onKeyDown={onKeyDown}
|
|
53
|
+
onBlur={onBlur}
|
|
54
|
+
onChange={onChange}
|
|
55
|
+
name={name}
|
|
56
|
+
value={value || ''}
|
|
57
|
+
placeholder={placeholder}
|
|
58
|
+
disabled={disabled}
|
|
59
|
+
valid={valid}
|
|
60
|
+
warning={warning}
|
|
61
|
+
error={error}
|
|
62
|
+
loading={loading}
|
|
63
|
+
dense={dense}
|
|
64
|
+
tabIndex={tabIndex}
|
|
65
|
+
initialFocus={initialFocus}
|
|
66
|
+
autoGrow={autoGrow}
|
|
67
|
+
readOnly={readOnly}
|
|
68
|
+
resize={resize}
|
|
69
|
+
rows={rows}
|
|
70
|
+
/>
|
|
71
|
+
</Box>
|
|
72
|
+
</Field>
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
TextAreaField.propTypes = {
|
|
76
|
+
/** Grow the text area in response to overflow instead of adding a scroll bar */
|
|
77
|
+
autoGrow: PropTypes.bool,
|
|
78
|
+
className: PropTypes.string,
|
|
79
|
+
dataTest: PropTypes.string,
|
|
80
|
+
/** Compact mode */
|
|
81
|
+
dense: PropTypes.bool,
|
|
82
|
+
/** Disables the textarea and makes in non-interactive */
|
|
83
|
+
disabled: PropTypes.bool,
|
|
84
|
+
/** Applies 'error' styles for validation feedback. Mutually exclusive with `valid` and `warning` props */
|
|
85
|
+
error: sharedPropTypes.statusPropType,
|
|
86
|
+
/** Adds useful help text below the textarea */
|
|
87
|
+
helpText: PropTypes.string,
|
|
88
|
+
/** Grabs initial focus on the page */
|
|
89
|
+
initialFocus: PropTypes.bool,
|
|
90
|
+
/** Sets the width of the textarea. Minimum 220px. Any valid CSS measurement can be used */
|
|
91
|
+
inputWidth: PropTypes.string,
|
|
92
|
+
/** Labels the textarea */
|
|
93
|
+
label: PropTypes.string,
|
|
94
|
+
/** Adds a loading spinner */
|
|
95
|
+
loading: PropTypes.bool,
|
|
96
|
+
/** Name associated with the text area. Passed in object argument to event handlers. */
|
|
97
|
+
name: PropTypes.string,
|
|
98
|
+
/** Placeholder text for an empty textarea */
|
|
99
|
+
placeholder: PropTypes.string,
|
|
100
|
+
/** Makes the textarea read-only */
|
|
101
|
+
readOnly: PropTypes.bool,
|
|
102
|
+
/** Adds an asterisk to the label to indicate this field is required */
|
|
103
|
+
required: PropTypes.bool,
|
|
104
|
+
/** [Resize property](https://developer.mozilla.org/en-US/docs/Web/CSS/resize) for the textarea element */
|
|
105
|
+
resize: PropTypes.oneOf(['none', 'both', 'horizontal', 'vertical']),
|
|
106
|
+
/** Initial height of the textarea, in lines of text */
|
|
107
|
+
rows: PropTypes.number,
|
|
108
|
+
tabIndex: PropTypes.string,
|
|
109
|
+
/** Applies 'valid' styles for validation feedback. Mutually exclusive with `warning` and `error` props */
|
|
110
|
+
valid: sharedPropTypes.statusPropType,
|
|
111
|
+
/** Validation text below the textarea to provide validation feedback. Changes appearance depending on validation status */
|
|
112
|
+
validationText: PropTypes.string,
|
|
113
|
+
/** Value in the textarea. Can be used to control component (recommended). Passed in object argument to event handlers. */
|
|
114
|
+
value: PropTypes.string,
|
|
115
|
+
/** Applies 'warning' styles for validation feedback. Mutually exclusive with `valid` and `error` props */
|
|
116
|
+
warning: sharedPropTypes.statusPropType,
|
|
117
|
+
/** Called with signature `({ name: string, value: string }, event)` */
|
|
118
|
+
onBlur: PropTypes.func,
|
|
119
|
+
/** Called with signature `({ name: string, value: string }, event)` */
|
|
120
|
+
onChange: PropTypes.func,
|
|
121
|
+
/** Called with signature `({ name: string, value: string }, event)` */
|
|
122
|
+
onFocus: PropTypes.func,
|
|
123
|
+
/** Called with signature `({ name: string, value: string }, event)` */
|
|
124
|
+
onKeyDown: PropTypes.func,
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export { TextAreaField }
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { sharedPropTypes } from '@dhis2/ui-constants'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { TextAreaField } from './index.js'
|
|
4
|
+
|
|
5
|
+
const description = `
|
|
6
|
+
\`TextAreaField\` wraps a \`TextArea\` component with a label, help text, validation text, and other functions.
|
|
7
|
+
|
|
8
|
+
See the regular TextArea for usage information and options.
|
|
9
|
+
|
|
10
|
+
\`\`\`js
|
|
11
|
+
import { TextAreaField } from '@dhis2/ui'
|
|
12
|
+
\`\`\`
|
|
13
|
+
`
|
|
14
|
+
|
|
15
|
+
export default {
|
|
16
|
+
title: 'Text Area Field',
|
|
17
|
+
component: TextAreaField,
|
|
18
|
+
parameters: { docs: { description: { component: description } } },
|
|
19
|
+
// Default args:
|
|
20
|
+
args: {
|
|
21
|
+
onChange: console.log,
|
|
22
|
+
name: 'textareaName',
|
|
23
|
+
},
|
|
24
|
+
argTypes: {
|
|
25
|
+
valid: { ...sharedPropTypes.statusArgType },
|
|
26
|
+
warning: { ...sharedPropTypes.statusArgType },
|
|
27
|
+
error: { ...sharedPropTypes.statusArgType },
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const Template = (args) => <TextAreaField {...args} />
|
|
32
|
+
|
|
33
|
+
export const NoPlaceholderNoValue = Template.bind({})
|
|
34
|
+
NoPlaceholderNoValue.storyName = 'No placeholder, no value'
|
|
35
|
+
|
|
36
|
+
export const PlaceholderNoValue = Template.bind({})
|
|
37
|
+
PlaceholderNoValue.args = { placeholder: 'Hold the place' }
|
|
38
|
+
PlaceholderNoValue.storyName = 'Placeholder, no value'
|
|
39
|
+
|
|
40
|
+
export const WithHelpText = Template.bind({})
|
|
41
|
+
WithHelpText.args = {
|
|
42
|
+
helpText: 'With some helping text to guide the user along',
|
|
43
|
+
...PlaceholderNoValue.args,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const WithValue = Template.bind({})
|
|
47
|
+
WithValue.args = {
|
|
48
|
+
value: 'This is set through the value prop, which means the component is controlled.',
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const Focus = Template.bind({})
|
|
52
|
+
Focus.args = { initialFocus: true }
|
|
53
|
+
// Disable stories that manipulate focus on docs page
|
|
54
|
+
Focus.parameters = { docs: { disable: true } }
|
|
55
|
+
|
|
56
|
+
export const StatusValid = Template.bind({})
|
|
57
|
+
StatusValid.args = { valid: true, value: 'This value is valid' }
|
|
58
|
+
StatusValid.storyName = 'Status: Valid'
|
|
59
|
+
|
|
60
|
+
export const StatusWarning = Template.bind({})
|
|
61
|
+
StatusWarning.args = { warning: true, value: 'This value produces a warning' }
|
|
62
|
+
StatusWarning.storyName = 'Status: Warning'
|
|
63
|
+
|
|
64
|
+
export const StatusError = Template.bind({})
|
|
65
|
+
StatusError.args = {
|
|
66
|
+
error: true,
|
|
67
|
+
value: 'This value produces an error',
|
|
68
|
+
helpText: 'This is some help text to advise what this input actually is.',
|
|
69
|
+
validationText: 'This describes the error, if a message is supplied.',
|
|
70
|
+
}
|
|
71
|
+
StatusError.storyName = 'Status: Error'
|
|
72
|
+
|
|
73
|
+
export const StatusLoading = Template.bind({})
|
|
74
|
+
StatusLoading.args = {
|
|
75
|
+
loading: true,
|
|
76
|
+
value: 'This value produces a loadingn state',
|
|
77
|
+
}
|
|
78
|
+
StatusLoading.storyName = 'Status: Loading'
|
|
79
|
+
|
|
80
|
+
export const Disabled = Template.bind({})
|
|
81
|
+
Disabled.args = { disabled: true, value: 'This field is disabled' }
|
|
82
|
+
|
|
83
|
+
export const ReadOnly = Template.bind({})
|
|
84
|
+
ReadOnly.args = { readOnly: true, value: 'This field is readOnly' }
|
|
85
|
+
|
|
86
|
+
export const Dense = Template.bind({})
|
|
87
|
+
Dense.args = { dense: true, value: 'This field is dense' }
|
|
88
|
+
|
|
89
|
+
export const LabelTextOverflow = Template.bind({})
|
|
90
|
+
LabelTextOverflow.args = {
|
|
91
|
+
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!",
|
|
92
|
+
}
|
|
93
|
+
LabelTextOverflow.storyName = 'Label text overflow'
|
|
94
|
+
|
|
95
|
+
export const TextareaTextOverflow = Template.bind({})
|
|
96
|
+
TextareaTextOverflow.args = {
|
|
97
|
+
label: 'I have a scrollbar',
|
|
98
|
+
value: [
|
|
99
|
+
'A line of text',
|
|
100
|
+
'A line of text',
|
|
101
|
+
'A line of text',
|
|
102
|
+
'A line of text',
|
|
103
|
+
'A line of text',
|
|
104
|
+
'A line of text',
|
|
105
|
+
'A line of text',
|
|
106
|
+
'A line of text',
|
|
107
|
+
'A line of text',
|
|
108
|
+
'A line of text',
|
|
109
|
+
'A line of text',
|
|
110
|
+
'A line of text',
|
|
111
|
+
'A line of text',
|
|
112
|
+
'A line of text',
|
|
113
|
+
'A line of text',
|
|
114
|
+
].join('\n'),
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const Required = () => (
|
|
118
|
+
<TextAreaField
|
|
119
|
+
onChange={() => {}}
|
|
120
|
+
name="textarea"
|
|
121
|
+
label="I am required and have an asterisk"
|
|
122
|
+
required
|
|
123
|
+
/>
|
|
124
|
+
)
|
|
125
|
+
Required.args = { required: true, label: 'I am required and have an asterisk' }
|
|
126
|
+
|
|
127
|
+
export const Rows = Template.bind({})
|
|
128
|
+
Rows.args = {
|
|
129
|
+
rows: 8,
|
|
130
|
+
label: 'You can set the height with the rows prop. I have 8',
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export const InputWidth = (args) => (
|
|
134
|
+
<>
|
|
135
|
+
<TextAreaField
|
|
136
|
+
{...args}
|
|
137
|
+
label="My textarea has a width of 220px (the minimum)"
|
|
138
|
+
inputWidth="220px"
|
|
139
|
+
/>
|
|
140
|
+
<TextAreaField
|
|
141
|
+
{...args}
|
|
142
|
+
label="My textarea has a width of 400px"
|
|
143
|
+
inputWidth="400px"
|
|
144
|
+
/>
|
|
145
|
+
</>
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
export const Resize = (args) => (
|
|
149
|
+
<>
|
|
150
|
+
<TextAreaField
|
|
151
|
+
{...args}
|
|
152
|
+
name="textarea1"
|
|
153
|
+
label="Resize: vertical (default)"
|
|
154
|
+
/>
|
|
155
|
+
<TextAreaField
|
|
156
|
+
{...args}
|
|
157
|
+
name="textarea2"
|
|
158
|
+
label="Resize: none"
|
|
159
|
+
resize="none"
|
|
160
|
+
/>
|
|
161
|
+
<TextAreaField
|
|
162
|
+
{...args}
|
|
163
|
+
name="textarea3"
|
|
164
|
+
label="Resize: both"
|
|
165
|
+
resize="both"
|
|
166
|
+
/>
|
|
167
|
+
<TextAreaField
|
|
168
|
+
{...args}
|
|
169
|
+
name="textarea4"
|
|
170
|
+
label="Resize: horizontal"
|
|
171
|
+
resize="horizontal"
|
|
172
|
+
/>
|
|
173
|
+
</>
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
export const Autogrow = (args) => (
|
|
177
|
+
<>
|
|
178
|
+
<TextAreaField
|
|
179
|
+
{...args}
|
|
180
|
+
name="textarea1"
|
|
181
|
+
label="Autogrow step 1"
|
|
182
|
+
autoGrow
|
|
183
|
+
rows={2}
|
|
184
|
+
value="This TextArea has a height of 2 rows"
|
|
185
|
+
/>
|
|
186
|
+
<TextAreaField
|
|
187
|
+
{...args}
|
|
188
|
+
name="textarea2"
|
|
189
|
+
label="Autogrow step 2"
|
|
190
|
+
autoGrow
|
|
191
|
+
rows={2}
|
|
192
|
+
value={[
|
|
193
|
+
'This TextArea has a height of two rows',
|
|
194
|
+
'it also has autoGrow set to true so it will grow with the content',
|
|
195
|
+
].join('\n')}
|
|
196
|
+
/>
|
|
197
|
+
<TextAreaField
|
|
198
|
+
{...args}
|
|
199
|
+
name="textarea3"
|
|
200
|
+
label="Autogrow step 3"
|
|
201
|
+
autoGrow
|
|
202
|
+
rows={2}
|
|
203
|
+
value={[
|
|
204
|
+
'This TextArea has a height of two rows',
|
|
205
|
+
'it also has autoGrow set to true so it will grow with the content.',
|
|
206
|
+
'See: rows is still 2, but I now have 3 lines.',
|
|
207
|
+
].join('\n')}
|
|
208
|
+
/>
|
|
209
|
+
<TextAreaField
|
|
210
|
+
{...args}
|
|
211
|
+
name="textarea4"
|
|
212
|
+
label="Autogrow step 4"
|
|
213
|
+
value={[
|
|
214
|
+
'This TextArea has a height of two rows',
|
|
215
|
+
'it also has autoGrow set to true so it will grow with the content.',
|
|
216
|
+
'See: rows is still 2...',
|
|
217
|
+
'And now I have 4 lines and still no scroll bar in sight.',
|
|
218
|
+
].join('\n')}
|
|
219
|
+
/>
|
|
220
|
+
</>
|
|
221
|
+
)
|