@dhis2-ui/file-input 10.16.1 → 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.
Files changed (104) hide show
  1. package/package.json +10 -9
  2. package/src/file-input/__tests__/file-input.test.js +28 -0
  3. package/src/file-input/features/accepts_multiple_files/index.js +32 -0
  4. package/src/file-input/features/accepts_multiple_files.feature +8 -0
  5. package/src/file-input/features/can_be_blurred/index.js +23 -0
  6. package/src/file-input/features/can_be_blurred.feature +6 -0
  7. package/src/file-input/features/can_be_changed/index.js +26 -0
  8. package/src/file-input/features/can_be_changed.feature +8 -0
  9. package/src/file-input/features/can_be_focused/index.js +22 -0
  10. package/src/file-input/features/can_be_focused.feature +6 -0
  11. package/src/file-input/features/common/index.js +21 -0
  12. package/src/file-input/file-input.e2e.stories.js +46 -0
  13. package/src/file-input/file-input.js +159 -0
  14. package/src/file-input/file-input.prod.stories.js +82 -0
  15. package/src/file-input/index.js +1 -0
  16. package/src/file-input-field/__tests__/file-input-field.test.js +29 -0
  17. package/src/file-input-field/features/can_be_required/index.js +9 -0
  18. package/src/file-input-field/features/can_be_required.feature +5 -0
  19. package/src/file-input-field/features/has_default_button_label/index.js +9 -0
  20. package/src/file-input-field/features/has_default_button_label.feature +5 -0
  21. package/src/file-input-field/features/has_default_placeholder/index.js +9 -0
  22. package/src/file-input-field/features/has_default_placeholder.feature +5 -0
  23. package/src/file-input-field/file-input-field.e2e.stories.js +15 -0
  24. package/src/file-input-field/file-input-field.js +139 -0
  25. package/src/file-input-field/file-input-field.prod.stories.js +167 -0
  26. package/src/file-input-field/index.js +1 -0
  27. package/src/file-input-field-with-list/__tests__/file-input-field-with-list.test.js +30 -0
  28. package/src/file-input-field-with-list/features/common/index.js +18 -0
  29. package/src/file-input-field-with-list/features/deduplicates_the_file_list/index.js +43 -0
  30. package/src/file-input-field-with-list/features/deduplicates_the_file_list.feature +8 -0
  31. package/src/file-input-field-with-list/features/disables_button_when_full/index.js +13 -0
  32. package/src/file-input-field-with-list/features/disables_button_when_full.feature +6 -0
  33. package/src/file-input-field-with-list/features/displays_files_in_a_list/index.js +7 -0
  34. package/src/file-input-field-with-list/features/displays_files_in_a_list.feature +5 -0
  35. package/src/file-input-field-with-list/features/files_can_be_removed/index.js +24 -0
  36. package/src/file-input-field-with-list/features/files_can_be_removed.feature +7 -0
  37. package/src/file-input-field-with-list/features/has_default_button_label/index.js +9 -0
  38. package/src/file-input-field-with-list/features/has_default_button_label.feature +5 -0
  39. package/src/file-input-field-with-list/features/has_default_placeholder/index.js +9 -0
  40. package/src/file-input-field-with-list/features/has_default_placeholder.feature +5 -0
  41. package/src/file-input-field-with-list/features/has_default_remove_text/index.js +9 -0
  42. package/src/file-input-field-with-list/features/has_default_remove_text.feature +5 -0
  43. package/src/file-input-field-with-list/file-input-field-with-list.e2e.stories.js +73 -0
  44. package/src/file-input-field-with-list/file-input-field-with-list.js +188 -0
  45. package/src/file-input-field-with-list/file-input-field-with-list.prod.stories.js +67 -0
  46. package/src/file-input-field-with-list/file-list-item-with-remove.js +34 -0
  47. package/src/file-input-field-with-list/index.js +1 -0
  48. package/src/file-list/features/accepts_cancel_text/index.js +14 -0
  49. package/src/file-list/features/accepts_cancel_text.feature +5 -0
  50. package/src/file-list/features/accepts_label/index.js +11 -0
  51. package/src/file-list/features/accepts_label.feature +5 -0
  52. package/src/file-list/features/accepts_remove_text/index.js +11 -0
  53. package/src/file-list/features/accepts_remove_text.feature +5 -0
  54. package/src/file-list/features/can_be_removed/index.js +15 -0
  55. package/src/file-list/features/can_be_removed.feature +6 -0
  56. package/src/file-list/features/file-list-item-accepts_children/index.js +10 -0
  57. package/src/file-list/features/file-list-item-accepts_children.feature +5 -0
  58. package/src/file-list/features/file-list-placeholder-accepts_children/index.js +12 -0
  59. package/src/file-list/features/file-list-placeholder-accepts_children.feature +5 -0
  60. package/src/file-list/features/loading_can_be_cancelled/index.js +15 -0
  61. package/src/file-list/features/loading_can_be_cancelled.feature +6 -0
  62. package/src/file-list/file-list-item.e2e.stories.js +39 -0
  63. package/src/file-list/file-list-item.js +131 -0
  64. package/src/file-list/file-list-placeholder.e2e.stories.js +7 -0
  65. package/src/file-list/file-list-placeholder.js +27 -0
  66. package/src/file-list/file-list.e2e.stories.js +5 -0
  67. package/src/file-list/file-list.js +28 -0
  68. package/src/file-list/index.js +3 -0
  69. package/src/index.js +9 -0
  70. package/src/locales/ar/translations.json +5 -0
  71. package/src/locales/ar_IQ/translations.json +5 -0
  72. package/src/locales/ckb/translations.json +5 -0
  73. package/src/locales/cs/translations.json +5 -0
  74. package/src/locales/da/translations.json +5 -0
  75. package/src/locales/en/translations.json +5 -0
  76. package/src/locales/es/translations.json +5 -0
  77. package/src/locales/es_419/translations.json +5 -0
  78. package/src/locales/fr/translations.json +5 -0
  79. package/src/locales/hi_IN/translations.json +5 -0
  80. package/src/locales/id/translations.json +5 -0
  81. package/src/locales/index.js +82 -0
  82. package/src/locales/km/translations.json +5 -0
  83. package/src/locales/ko_KR/translations.json +5 -0
  84. package/src/locales/lo/translations.json +5 -0
  85. package/src/locales/my/translations.json +5 -0
  86. package/src/locales/nb/translations.json +5 -0
  87. package/src/locales/nl/translations.json +5 -0
  88. package/src/locales/prs/translations.json +5 -0
  89. package/src/locales/ps/translations.json +5 -0
  90. package/src/locales/pt/translations.json +5 -0
  91. package/src/locales/pt_BR/translations.json +5 -0
  92. package/src/locales/ru/translations.json +5 -0
  93. package/src/locales/si/translations.json +5 -0
  94. package/src/locales/sv/translations.json +5 -0
  95. package/src/locales/tet/translations.json +5 -0
  96. package/src/locales/tg/translations.json +5 -0
  97. package/src/locales/uk/translations.json +5 -0
  98. package/src/locales/ur/translations.json +5 -0
  99. package/src/locales/uz_Latn/translations.json +5 -0
  100. package/src/locales/uz_UZ_Cyrl/translations.json +5 -0
  101. package/src/locales/uz_UZ_Latn/translations.json +5 -0
  102. package/src/locales/vi/translations.json +5 -0
  103. package/src/locales/zh/translations.json +5 -0
  104. package/src/locales/zh_CN/translations.json +5 -0
@@ -0,0 +1,139 @@
1
+ import { sharedPropTypes } from '@dhis2/ui-constants'
2
+ import { Field } from '@dhis2-ui/field'
3
+ import { Label } from '@dhis2-ui/label'
4
+ import PropTypes from 'prop-types'
5
+ import React from 'react'
6
+ import { FileInput } from '../file-input/index.js'
7
+ import { FileList, FileListPlaceholder } from '../file-list/index.js'
8
+ import i18n from '../locales/index.js'
9
+
10
+ // TODO: i18n
11
+ const translate = (prop, interpolationObject) => {
12
+ if (typeof prop === 'function') {
13
+ return prop(interpolationObject)
14
+ }
15
+
16
+ return prop
17
+ }
18
+
19
+ const defaultButtonLabel = () => i18n.t('Upload a file')
20
+ const defaultPlaceholder = () => i18n.t('No file uploaded yet')
21
+ const FileInputField = ({
22
+ accept = '*',
23
+ buttonLabel = defaultButtonLabel,
24
+ children,
25
+ className,
26
+ dataTest = 'dhis2-uiwidgets-fileinputfield',
27
+ disabled,
28
+ error,
29
+ helpText,
30
+ initialFocus,
31
+ label,
32
+ large,
33
+ multiple,
34
+ name,
35
+ onBlur,
36
+ onChange,
37
+ onFocus,
38
+ onKeyDown,
39
+ placeholder = defaultPlaceholder,
40
+ required,
41
+ small,
42
+ tabIndex,
43
+ valid,
44
+ validationText,
45
+ warning,
46
+ }) => (
47
+ <Field
48
+ className={className}
49
+ dataTest={dataTest}
50
+ helpText={helpText}
51
+ validationText={validationText}
52
+ error={error}
53
+ warning={warning}
54
+ valid={valid}
55
+ >
56
+ {label && (
57
+ <Label required={required} disabled={disabled} htmlFor={name}>
58
+ {label}
59
+ </Label>
60
+ )}
61
+
62
+ <FileInput
63
+ accept={accept}
64
+ buttonLabel={translate(buttonLabel)}
65
+ className={className}
66
+ disabled={disabled}
67
+ error={error}
68
+ initialFocus={initialFocus}
69
+ large={large}
70
+ multiple={multiple}
71
+ name={name}
72
+ onBlur={onBlur}
73
+ onChange={onChange}
74
+ onFocus={onFocus}
75
+ onKeyDown={onKeyDown}
76
+ small={small}
77
+ tabIndex={tabIndex}
78
+ valid={valid}
79
+ warning={warning}
80
+ />
81
+
82
+ <FileList>
83
+ {children ? (
84
+ children
85
+ ) : (
86
+ <FileListPlaceholder>
87
+ {translate(placeholder)}
88
+ </FileListPlaceholder>
89
+ )}
90
+ </FileList>
91
+ </Field>
92
+ )
93
+
94
+ FileInputField.propTypes = {
95
+ /** The `accept` attribute of the [native file input](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept) */
96
+ accept: PropTypes.string,
97
+ /** Text on the button */
98
+ buttonLabel: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
99
+ children: PropTypes.node,
100
+ className: PropTypes.string,
101
+ dataTest: PropTypes.string,
102
+ /** Disables the button */
103
+ disabled: PropTypes.bool,
104
+ /** Applies 'error' styling to the validation text. Mutually exclusive with `warning` and `valid` props */
105
+ error: sharedPropTypes.statusPropType,
106
+ /** Useful guiding text for the user */
107
+ helpText: PropTypes.string,
108
+ initialFocus: PropTypes.bool,
109
+ /** A descriptive label above the button */
110
+ label: PropTypes.string,
111
+ /** Size of the button. Mutually exclusive with the `small` prop */
112
+ large: sharedPropTypes.sizePropType,
113
+ /** The `multiple` attribute of the [native file input](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#multiple) */
114
+ multiple: PropTypes.bool,
115
+ /** Name associated with input. Passed to event handler callbacks */
116
+ name: PropTypes.string,
117
+ /** Placeholder below the button */
118
+ placeholder: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
119
+ /** Adds an asterisk to indicate this field is required */
120
+ required: PropTypes.bool,
121
+ /** Size of the button. Mutually exclusive with the `large` prop */
122
+ small: sharedPropTypes.sizePropType,
123
+ tabIndex: PropTypes.string,
124
+ /** Applies 'valid' styling to the validation text. Mutually exclusive with `warning` and `error` props */
125
+ valid: sharedPropTypes.statusPropType,
126
+ /** Text below the button that provides validation feedback */
127
+ validationText: PropTypes.string,
128
+ /** Applies 'warning' styling to the validation text. Mutually exclusive with `valid` and `error` props */
129
+ warning: sharedPropTypes.statusPropType,
130
+ onBlur: PropTypes.func,
131
+ /** Called with signature `({ name: string, files: [] }, event)` */
132
+ onChange: PropTypes.func,
133
+ /** Called with signature `({ name: string, files: [] }, event)` */
134
+ onFocus: PropTypes.func,
135
+ /** Called with signature `({ name: string, files: [] }, event)` */
136
+ onKeyDown: PropTypes.func,
137
+ }
138
+
139
+ export { FileInputField }
@@ -0,0 +1,167 @@
1
+ import { sharedPropTypes } from '@dhis2/ui-constants'
2
+ import React from 'react'
3
+ import { FileListItem } from '../index.js'
4
+ import { FileInputField } from './file-input-field.js'
5
+
6
+ const description = `
7
+ The \`FileInputField\` component wraps the \`FileInput\` component in a \`Field\` wrapper to add labels, help text, and validation text.
8
+
9
+ \`\`\`js
10
+ import { FileInputField, FileListItem } from '@dhis2/ui'
11
+ \`\`\`
12
+ `
13
+
14
+ const onChange = (obj) => console.log('onChange', obj)
15
+ const onRemove = () => console.log('onRemove')
16
+ const onCancel = () => console.log('onCancel')
17
+
18
+ export default {
19
+ title: 'File Input Field',
20
+ component: FileInputField,
21
+ parameters: { docs: { description: { component: description } } },
22
+ // Default args:
23
+ args: {
24
+ // Handle default values (see comment in Transfer.js)
25
+ accept: '*',
26
+ dataTest: 'dhis2-uiwidgets-fileinputfield',
27
+ placeholder: 'No file uploaded yet',
28
+ onChange: onChange,
29
+ name: 'uploadName',
30
+ label: 'Upload something',
31
+ buttonLabel: 'Upload a file',
32
+ },
33
+ argTypes: {
34
+ small: { ...sharedPropTypes.sizeArgType },
35
+ large: { ...sharedPropTypes.sizeArgType },
36
+ valid: { ...sharedPropTypes.statusArgType },
37
+ warning: { ...sharedPropTypes.statusArgType },
38
+ error: { ...sharedPropTypes.statusArgType },
39
+ },
40
+ }
41
+
42
+ const Template = (args) => <FileInputField {...args} />
43
+
44
+ export const Default = Template.bind({})
45
+ Default.args = { label: null }
46
+
47
+ export const WithLabel = Template.bind({})
48
+
49
+ export const Required = Template.bind({})
50
+ Required.args = { required: true }
51
+
52
+ export const Multiple = Template.bind({})
53
+ Multiple.args = {
54
+ multiple: true,
55
+ label: 'Upload multiple things',
56
+ buttonLabel: 'Upload files',
57
+ }
58
+
59
+ export const Disabled = Template.bind({})
60
+ Disabled.args = { disabled: true }
61
+
62
+ export const Sizes = (args) => (
63
+ <>
64
+ <FileInputField
65
+ {...args}
66
+ buttonLabel="Default size"
67
+ name="defaultName"
68
+ />
69
+ <FileInputField {...args} buttonLabel="Small" small name="smallName" />
70
+ <FileInputField {...args} buttonLabel="Large" large name="largeName" />
71
+ </>
72
+ )
73
+
74
+ export const Statuses = (args) => (
75
+ <>
76
+ <FileInputField {...args} buttonLabel="Default" name="defaultName" />
77
+ <FileInputField {...args} buttonLabel="Valid" name="validName" valid />
78
+ <FileInputField
79
+ {...args}
80
+ buttonLabel="Warning"
81
+ name="warningName"
82
+ warning
83
+ />
84
+ <FileInputField
85
+ {...args}
86
+ buttonLabel="Error"
87
+ name="errorName"
88
+ error
89
+ validationText="Something went wrong"
90
+ />
91
+ </>
92
+ )
93
+
94
+ export const FileList = (args) => (
95
+ <div style={{ width: 250 }}>
96
+ <FileInputField {...args}>
97
+ <FileListItem
98
+ label="picture1.jpg"
99
+ onRemove={onRemove}
100
+ onCancel={onCancel}
101
+ cancelText="Cancel"
102
+ removeText="Remove"
103
+ />
104
+ <FileListItem
105
+ label="image_that_is_uploading.jpg"
106
+ onRemove={onRemove}
107
+ onCancel={onCancel}
108
+ cancelText="Cancel"
109
+ removeText="Remove"
110
+ loading
111
+ />
112
+ <FileListItem
113
+ label="image_file_name_is_to_long_to_display_on_one_line.jpg"
114
+ onRemove={onRemove}
115
+ onCancel={onCancel}
116
+ cancelText="Cancel"
117
+ removeText="Remove"
118
+ />
119
+ </FileInputField>
120
+ <br />
121
+ <p style={{ color: 'grey' }}>
122
+ <em>Bounding box is 250px wide</em>
123
+ </p>
124
+ </div>
125
+ )
126
+
127
+ export const PlaceholderText = Template.bind({})
128
+ PlaceholderText.args = { placeholder: 'No file(s) selected yet' }
129
+
130
+ export const HelpText = Template.bind({})
131
+ HelpText.args = { helpText: 'Please select any file type' }
132
+
133
+ export const DesignSystemStackingOrder = (args) => (
134
+ <FileInputField {...args}>
135
+ <FileListItem
136
+ label="TestFile.txt"
137
+ onRemove={onRemove}
138
+ removeText="remove"
139
+ />
140
+ <FileListItem
141
+ label="BusyFile.txt"
142
+ onRemove={onRemove}
143
+ onCancel={onCancel}
144
+ cancelText="cancel"
145
+ removeText="remove"
146
+ loading
147
+ />
148
+ </FileInputField>
149
+ )
150
+ DesignSystemStackingOrder.args = {
151
+ error: true,
152
+ validationText: 'Oops!',
153
+ placeholder: 'Select a file',
154
+ helpText: 'Please upload something',
155
+ }
156
+
157
+ export const DesignSystemStackingOrderEmptyFileList = Template.bind({})
158
+ DesignSystemStackingOrderEmptyFileList.args = {
159
+ ...DesignSystemStackingOrder.args,
160
+ }
161
+ DesignSystemStackingOrderEmptyFileList.storyName =
162
+ 'Design system stacking order - empty file list'
163
+
164
+ export const DefaultButtonLabelAndPlaceholder = Template.bind({})
165
+ DefaultButtonLabelAndPlaceholder.args = { label: null }
166
+ DefaultButtonLabelAndPlaceholder.storyName =
167
+ 'Default: buttonLabel and placeholder'
@@ -0,0 +1 @@
1
+ export { FileInputField } from './file-input-field.js'
@@ -0,0 +1,30 @@
1
+ import { render, fireEvent, screen } from '@testing-library/react'
2
+ import React from 'react'
3
+ import { FileInputFieldWithList } from '../file-input-field-with-list.js'
4
+
5
+ describe('<FileInputFieldWithList />', () => {
6
+ it('should call the onKeyDown callback when provided', () => {
7
+ const onKeyDown = jest.fn()
8
+
9
+ render(
10
+ <FileInputFieldWithList
11
+ label="Label"
12
+ name="foo"
13
+ value="bar"
14
+ checked={false}
15
+ onKeyDown={onKeyDown}
16
+ onChange={jest.fn()}
17
+ />
18
+ )
19
+
20
+ fireEvent.keyDown(screen.getByRole('button'), {})
21
+
22
+ expect(onKeyDown).toHaveBeenCalledTimes(1)
23
+
24
+ const input = screen.getByTestId('dhis2-uicore-fileinput-input')
25
+ expect(onKeyDown).toHaveBeenCalledWith(
26
+ { name: 'foo', files: input.files },
27
+ expect.objectContaining({})
28
+ )
29
+ })
30
+ })
@@ -0,0 +1,18 @@
1
+ import { Given, Then } from '@badeball/cypress-cucumber-preprocessor'
2
+
3
+ Given('a FileInputFieldWithList with multiple files is rendered', () => {
4
+ cy.visitStory('FileInputFieldWithList', 'Multiple files - with files')
5
+ })
6
+
7
+ Then('the onChange handler is called', () => {
8
+ cy.window().should((win) => {
9
+ const calls = win.onChange.getCalls()
10
+ expect(calls).to.have.lengthOf(1)
11
+
12
+ const callArgs = calls[0].args
13
+ expect(callArgs).to.have.lengthOf(2)
14
+
15
+ const payload = callArgs[0]
16
+ expect(Object.keys(payload)).to.include.members(['files', 'name'])
17
+ })
18
+ })
@@ -0,0 +1,43 @@
1
+ import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'
2
+
3
+ Given('the list contains the file duplicate.md', () => {
4
+ cy.contains('duplicate.md')
5
+ .should('have.lengthOf', 1)
6
+ .should('have.class', 'label')
7
+ .closest('[data-test="dhis2-uicore-filelistitem"]')
8
+ .should('have.lengthOf', 1)
9
+ })
10
+
11
+ When('the file duplicate.md is selected', () => {
12
+ cy.window().then((win) => {
13
+ cy.get('[data-test="dhis2-uicore-fileinput"] input')
14
+ .then(($input) => {
15
+ // name, lastModified, size and type are equal to the file created in the story
16
+ const duplicate = new File(...win.duplicateFileConstructorArgs)
17
+ const dataTransfer = new DataTransfer()
18
+
19
+ dataTransfer.items.add(duplicate)
20
+ $input[0].files = dataTransfer.files
21
+ })
22
+ .trigger('change', { force: true })
23
+ })
24
+ })
25
+
26
+ Then(
27
+ "the onChange handler's payload contains a single entry for file.md",
28
+ () => {
29
+ cy.window().should((win) => {
30
+ const calls = win.onChange.getCalls()
31
+ const callArgs = calls[0].args
32
+ const payload = callArgs[0]
33
+ const files = payload.files
34
+
35
+ expect(files).to.have.lengthOf(3)
36
+
37
+ const filesWithNameFileMd = files.filter(
38
+ (f) => f.name === 'duplicate.md'
39
+ )
40
+ expect(filesWithNameFileMd).to.have.lengthOf(1)
41
+ })
42
+ }
43
+ )
@@ -0,0 +1,8 @@
1
+ Feature: The FileInputFieldWithList deduplicates the file list
2
+
3
+ Scenario: The file list is deduplicated
4
+ Given a FileInputFieldWithList with multiple files is rendered
5
+ And the list contains the file duplicate.md
6
+ When the file duplicate.md is selected
7
+ Then the onChange handler is called
8
+ Then the onChange handler's payload contains a single entry for file.md
@@ -0,0 +1,13 @@
1
+ import { Given, Then } from '@badeball/cypress-cucumber-preprocessor'
2
+
3
+ Given('a FileInputFieldWithList without multiple is rendered', () => {
4
+ cy.visitStory('FileInputFieldWithList', 'Single file - with file')
5
+ })
6
+
7
+ Given('the FileInputFieldWithList holds a file', () => {
8
+ cy.get('[data-test="dhis2-uicore-filelistitem"]').should('have.lengthOf', 1)
9
+ })
10
+
11
+ Then('the button is disabled', () => {
12
+ cy.get('[data-test="dhis2-uicore-button"]').should('have.attr', 'disabled')
13
+ })
@@ -0,0 +1,6 @@
1
+ Feature: The FileInputFieldWithList disables its button when full
2
+
3
+ Scenario: The button is disabled when the fileInput is full
4
+ Given a FileInputFieldWithList without multiple is rendered
5
+ And the FileInputFieldWithList holds a file
6
+ Then the button is disabled
@@ -0,0 +1,7 @@
1
+ import { Then } from '@badeball/cypress-cucumber-preprocessor'
2
+
3
+ Then('the files are displayed in a list', () => {
4
+ cy.get('[data-test="dhis2-uicore-filelistitem"] .label')
5
+ .then(($labels) => $labels.map((_, el) => el.innerHTML).toArray())
6
+ .should('deep.equal', ['test1.md', 'duplicate.md', 'test2.md'])
7
+ })
@@ -0,0 +1,5 @@
1
+ Feature: The FileInputFieldWithList displays files in a list
2
+
3
+ Scenario: Files are displayed in a list
4
+ Given a FileInputFieldWithList with multiple files is rendered
5
+ Then the files are displayed in a list
@@ -0,0 +1,24 @@
1
+ import { When, Then } from '@badeball/cypress-cucumber-preprocessor'
2
+
3
+ When('the remove handle behind a file is clicked', () => {
4
+ cy.contains('test1.md')
5
+ .siblings('[data-test="dhis2-uicore-filelistitem-remove"]')
6
+ .click()
7
+ })
8
+
9
+ Then(
10
+ "the onChange handler's payload does not contain an entry for that file",
11
+ () => {
12
+ cy.window().should((win) => {
13
+ const calls = win.onChange.getCalls()
14
+ const callArgs = calls[0].args
15
+
16
+ const payload = callArgs[0]
17
+ const files = payload.files
18
+ expect(files).to.have.lengthOf(2)
19
+
20
+ const filtered = files.filter((file) => file.name === 'test1.md')
21
+ expect(filtered).to.have.lengthOf(0)
22
+ })
23
+ }
24
+ )
@@ -0,0 +1,7 @@
1
+ Feature: Files can be removed from FileInputFieldWithList
2
+
3
+ Scenario: Files can be removed from the file list
4
+ Given a FileInputFieldWithList with multiple files is rendered
5
+ When the remove handle behind a file is clicked
6
+ Then the onChange handler is called
7
+ Then the onChange handler's payload does not contain an entry for that file
@@ -0,0 +1,9 @@
1
+ import { Given, Then } from '@badeball/cypress-cucumber-preprocessor'
2
+
3
+ Given('a default FileInputFieldWithList is rendered', () => {
4
+ cy.visitStory('FileInputFieldWithList', 'With default texts')
5
+ })
6
+
7
+ Then('the default button label is visible', () => {
8
+ cy.contains('Upload a file').should('be.visible')
9
+ })
@@ -0,0 +1,5 @@
1
+ Feature: Button label for the FileInputFieldWithList
2
+
3
+ Scenario: Rendering a FileInputFieldWithList
4
+ Given a default FileInputFieldWithList is rendered
5
+ Then the default button label is visible
@@ -0,0 +1,9 @@
1
+ import { Given, Then } from '@badeball/cypress-cucumber-preprocessor'
2
+
3
+ Given('a default FileInputFieldWithList is rendered', () => {
4
+ cy.visitStory('FileInputFieldWithList', 'With default texts')
5
+ })
6
+
7
+ Then('the default placeholder is visible', () => {
8
+ cy.contains('No file uploaded yet').should('be.visible')
9
+ })
@@ -0,0 +1,5 @@
1
+ Feature: Placeholder for the FileInputFieldWithList
2
+
3
+ Scenario: Rendering a FileInputFieldWithList
4
+ Given a default FileInputFieldWithList is rendered
5
+ Then the default placeholder is visible
@@ -0,0 +1,9 @@
1
+ import { Given, Then } from '@badeball/cypress-cucumber-preprocessor'
2
+
3
+ Given('a default FileInputFieldWithList is rendered', () => {
4
+ cy.visitStory('FileInputFieldWithList', 'With file and default texts')
5
+ })
6
+
7
+ Then('the default remove text is visible', () => {
8
+ cy.contains('Remove').should('be.visible')
9
+ })
@@ -0,0 +1,5 @@
1
+ Feature: Placeholder for the FileInputFieldWithList
2
+
3
+ Scenario: Rendering a FileInputFieldWithList
4
+ Given a default FileInputFieldWithList is rendered
5
+ Then the default remove text is visible
@@ -0,0 +1,73 @@
1
+ import React from 'react'
2
+ import { FileInputFieldWithList } from './index.js'
3
+
4
+ window.onChange = window.Cypress && window.Cypress.cy.stub()
5
+
6
+ const singleFileArray = [new File([], 'dummy.txt')]
7
+
8
+ window.duplicateFileConstructorArgs = [
9
+ [],
10
+ 'duplicate.md',
11
+ {
12
+ lastModified: 1581348175357,
13
+ type: 'text/markdown',
14
+ },
15
+ ]
16
+
17
+ const multipleFileArray = [
18
+ new File([], 'test1.md'),
19
+ new File(...window.duplicateFileConstructorArgs),
20
+ new File([], 'test2.md'),
21
+ ]
22
+
23
+ const onChange = (payload, event) => {
24
+ // NOTE: if files is not transformed into an array,
25
+ // cypress will get an empty file list!
26
+ window.onChange(
27
+ {
28
+ ...payload,
29
+ files: [...payload.files],
30
+ },
31
+ event
32
+ )
33
+ }
34
+
35
+ export default { title: 'FileInputFieldWithList' }
36
+ export const SingleFileWithFile = () => (
37
+ <FileInputFieldWithList
38
+ buttonLabel="Upload file"
39
+ name="upload"
40
+ files={singleFileArray}
41
+ removeText="remove"
42
+ onChange={onChange}
43
+ />
44
+ )
45
+ export const MultipleFilesEmpty = () => (
46
+ <FileInputFieldWithList
47
+ buttonLabel="Upload file"
48
+ multiple
49
+ name="upload"
50
+ removeText="remove"
51
+ onChange={onChange}
52
+ />
53
+ )
54
+ export const MultipleFilesWithFiles = () => (
55
+ <FileInputFieldWithList
56
+ buttonLabel="Upload file"
57
+ multiple
58
+ name="upload"
59
+ files={multipleFileArray}
60
+ removeText="remove"
61
+ onChange={onChange}
62
+ />
63
+ )
64
+ export const WithFileAndDefaultTexts = () => (
65
+ <FileInputFieldWithList
66
+ name="upload"
67
+ files={singleFileArray}
68
+ onChange={onChange}
69
+ />
70
+ )
71
+ export const WithDefaultTexts = () => (
72
+ <FileInputFieldWithList name="upload" onChange={onChange} />
73
+ )