@pareto-engineering/design-system 4.2.1 → 4.3.0

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 (58) hide show
  1. package/dist/cjs/f/FormInput/FormInput.js +6 -0
  2. package/dist/cjs/f/common/utils/index.js +8 -1
  3. package/dist/cjs/f/common/utils/validators.js +17 -0
  4. package/dist/cjs/f/fields/ChoicesInput/common/Choice/Choice.js +3 -2
  5. package/dist/cjs/f/fields/FileUpload/FileUpload.js +234 -0
  6. package/dist/cjs/f/fields/FileUpload/common/FilePreview/FilePreview.js +118 -0
  7. package/dist/cjs/f/fields/FileUpload/common/FilePreview/index.js +13 -0
  8. package/dist/cjs/f/fields/FileUpload/common/FilePreview/styles.scss +93 -0
  9. package/dist/cjs/f/fields/FileUpload/common/index.js +12 -0
  10. package/dist/cjs/f/fields/FileUpload/index.js +32 -0
  11. package/dist/cjs/f/fields/FileUpload/styles.scss +73 -0
  12. package/dist/cjs/f/fields/FileUpload/utils.js +45 -0
  13. package/dist/cjs/f/fields/index.js +26 -1
  14. package/dist/cjs/g/FormBuilder/FormBuilder.js +11 -3
  15. package/dist/cjs/g/FormBuilder/common/Builder/common/InputBuilder/InputBuilder.js +34 -7
  16. package/dist/cjs/g/FormBuilder/common/Builder/common/InputBuilder/styles.scss +11 -0
  17. package/dist/cjs/g/FormBuilder/common/Renderer/Renderer.js +14 -4
  18. package/dist/cjs/g/FormBuilder/common/Renderer/common/Section/Section.js +8 -4
  19. package/dist/es/f/FormInput/FormInput.js +7 -1
  20. package/dist/es/f/common/utils/index.js +2 -1
  21. package/dist/es/f/common/utils/validators.js +10 -0
  22. package/dist/es/f/fields/ChoicesInput/common/Choice/Choice.js +3 -2
  23. package/dist/es/f/fields/FileUpload/FileUpload.js +223 -0
  24. package/dist/es/f/fields/FileUpload/common/FilePreview/FilePreview.js +108 -0
  25. package/dist/es/f/fields/FileUpload/common/FilePreview/index.js +2 -0
  26. package/dist/es/f/fields/FileUpload/common/FilePreview/styles.scss +93 -0
  27. package/dist/es/f/fields/FileUpload/common/index.js +2 -0
  28. package/dist/es/f/fields/FileUpload/index.js +3 -0
  29. package/dist/es/f/fields/FileUpload/styles.scss +73 -0
  30. package/dist/es/f/fields/FileUpload/utils.js +37 -0
  31. package/dist/es/f/fields/index.js +2 -1
  32. package/dist/es/g/FormBuilder/FormBuilder.js +11 -3
  33. package/dist/es/g/FormBuilder/common/Builder/common/InputBuilder/InputBuilder.js +35 -8
  34. package/dist/es/g/FormBuilder/common/Builder/common/InputBuilder/styles.scss +11 -0
  35. package/dist/es/g/FormBuilder/common/Renderer/Renderer.js +14 -4
  36. package/dist/es/g/FormBuilder/common/Renderer/common/Section/Section.js +8 -3
  37. package/package.json +3 -3
  38. package/src/stories/f/FileUpload.stories.jsx +55 -0
  39. package/src/stories/g/FormBuilder.stories.jsx +19 -97
  40. package/src/ui/f/FormInput/FormInput.jsx +11 -0
  41. package/src/ui/f/common/utils/index.js +1 -0
  42. package/src/ui/f/common/utils/validators.js +10 -0
  43. package/src/ui/f/fields/ChoicesInput/common/Choice/Choice.jsx +2 -0
  44. package/src/ui/f/fields/FileUpload/FileUpload.jsx +279 -0
  45. package/src/ui/f/fields/FileUpload/common/FilePreview/FilePreview.jsx +160 -0
  46. package/src/ui/f/fields/FileUpload/common/FilePreview/index.js +2 -0
  47. package/src/ui/f/fields/FileUpload/common/FilePreview/styles.scss +93 -0
  48. package/src/ui/f/fields/FileUpload/common/index.js +2 -0
  49. package/src/ui/f/fields/FileUpload/index.js +3 -0
  50. package/src/ui/f/fields/FileUpload/styles.scss +73 -0
  51. package/src/ui/f/fields/FileUpload/utils.js +49 -0
  52. package/src/ui/f/fields/index.js +6 -0
  53. package/src/ui/g/FormBuilder/FormBuilder.jsx +9 -0
  54. package/src/ui/g/FormBuilder/common/Builder/common/InputBuilder/InputBuilder.jsx +53 -11
  55. package/src/ui/g/FormBuilder/common/Builder/common/InputBuilder/styles.scss +11 -0
  56. package/src/ui/g/FormBuilder/common/Renderer/Renderer.jsx +17 -3
  57. package/src/ui/g/FormBuilder/common/Renderer/common/Section/Section.jsx +11 -2
  58. package/tests/__snapshots__/Storyshots.test.js.snap +180 -154
@@ -0,0 +1,93 @@
1
+ /* @pareto-engineering/generator-front 1.1.1-alpha.2 */
2
+
3
+ @use "@pareto-engineering/bem";
4
+
5
+ $default-padding: 1rem;
6
+ $default-margin: 1rem;
7
+ $files-per-row: 3;
8
+ $total-gaps: $files-per-row - 1;
9
+ $combined-gap: calc(var(--gap) * $total-gaps);
10
+ $width-without-gaps: calc(100% - $combined-gap);
11
+ $default-file-width: calc($width-without-gaps / $files-per-row);
12
+
13
+ .#{bem.$base}.file-preview {
14
+ border: var(--theme-default-border-style) var(--ui-lines);
15
+ border-radius: calc(var(--theme-default-border-radius) / 2);
16
+ display: flex;
17
+ flex-direction: column;
18
+ gap: calc(var(--gap) / 4);
19
+ padding: $default-padding;
20
+ width: $default-file-width;
21
+
22
+ > .identity {
23
+ align-items: center;
24
+ display: flex;
25
+ gap: calc(var(--gap) / 2);
26
+
27
+ > .type {
28
+ padding-block: calc($default-padding / 4);
29
+ padding-inline: calc($default-padding / 2);
30
+
31
+ // TODO: update the colors to use variables. These are colors from the new DS
32
+ &.pdf {
33
+ background-color: #fdead7;
34
+ color: #b93814;
35
+ }
36
+
37
+ &.file {
38
+ background-color: #eef2f6;
39
+ color: #364151;
40
+ }
41
+
42
+ &.txt {
43
+ background-color: #d1dfff;
44
+ color: #004eeb;
45
+ }
46
+
47
+
48
+ &.vid {
49
+ background-color: #ebe9fe;
50
+ color: #5a25dc;
51
+ }
52
+
53
+ &.img {
54
+ background-color: #cbfbef;
55
+ color: #107569;
56
+ }
57
+
58
+ &.aud {
59
+ background-color: #fef7c3;
60
+ color: #a15c08;
61
+ }
62
+ }
63
+
64
+ > .name {
65
+ overflow: hidden;
66
+ text-overflow: ellipsis;
67
+ white-space: nowrap;
68
+ }
69
+
70
+ > .#{bem.$base}.button {
71
+ cursor: pointer;
72
+ margin-left: auto;
73
+ }
74
+ }
75
+
76
+ > .progress-status {
77
+ > .status {
78
+ align-items: center;
79
+ color: var(--x);
80
+ display: flex;
81
+ gap: calc(var(--gap) / 2);
82
+ margin-bottom: calc($default-margin / 4);
83
+
84
+ > .icon {
85
+ background-color: var(--x);
86
+ border-radius: 50%;
87
+ color: var(--on-x);
88
+ font-size: calc(var(--s-2) * 1rem);
89
+ padding: calc($default-padding / 4);
90
+ }
91
+ }
92
+ }
93
+ }
@@ -0,0 +1,2 @@
1
+ /* @pareto-engineering/generator-front 1.1.1-alpha.2 */
2
+ export { FilePreview } from './FilePreview'
@@ -0,0 +1,3 @@
1
+ /* @pareto-engineering/generator-front 1.1.1-alpha.2 */
2
+ export { default as FileUpload } from './FileUpload'
3
+ export { fileUploadOptions, getFileType, getFileTypeFromUrl } from './utils'
@@ -0,0 +1,73 @@
1
+ /* @pareto-engineering/generator-front 1.1.1-alpha.2 */
2
+
3
+ @use "@pareto-engineering/bem";
4
+
5
+ $default-padding: 1rem;
6
+ $default-margin: 1rem;
7
+
8
+ .#{bem.$base}.file-upload {
9
+ display: flex;
10
+ flex-direction: column;
11
+ gap: var(--gap);
12
+
13
+
14
+ > p {
15
+ color: var(--y);
16
+ margin: 0;
17
+ }
18
+
19
+ > .#{bem.$base}.description {
20
+ height: auto;
21
+ }
22
+
23
+ > .#{bem.$base}.form-label {
24
+ align-items: center;
25
+ border: var(--theme-default-border-style) var(--ui-lines);
26
+ border-radius: calc(var(--theme-default-border-radius) / 2);
27
+ color: var(--heading);
28
+ cursor: pointer;
29
+ display: flex;
30
+ gap: calc(var(--gap) / 2);
31
+ padding: $default-padding;
32
+ width: fit-content;
33
+
34
+ &:has(>input:disabled) {
35
+ --icon-color: var(--hard-disabled);
36
+ color: var(--hard-disabled);
37
+ cursor: not-allowed;
38
+ }
39
+
40
+ &:has(>input:not(:disabled)) {
41
+ &:hover,
42
+ &:active {
43
+ border: var(--theme-hover-input-border);
44
+ }
45
+ }
46
+
47
+ > .ai-icon {
48
+ color: var(--icon-color, var(--interactive));
49
+ font-size: calc(var(--s2) * 1rem);
50
+ }
51
+
52
+ > input[type="file"] {
53
+ display: none;
54
+ }
55
+ }
56
+
57
+ > .attached-files {
58
+ display: flex;
59
+ gap: var(--gap);
60
+
61
+ > p {
62
+ margin: 0;
63
+ }
64
+
65
+ > .files {
66
+ align-items: center;
67
+ display: flex;
68
+ flex-wrap: wrap;
69
+ gap: var(--gap);
70
+ width: 100%;
71
+ }
72
+ }
73
+ }
@@ -0,0 +1,49 @@
1
+ export const fileUploadOptions = [
2
+ {
3
+ key :'FILE',
4
+ value:'.doc,.docx,.odt,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.oasis.opendocument.text',
5
+ label:'Documents',
6
+ },
7
+ {
8
+ key :'TXT',
9
+ value:'.xls,.xlsx,.ods,.csv,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.oasis.opendocument.spreadsheet,text/csv',
10
+ label:'Spreadsheets',
11
+ },
12
+ {
13
+ key :'PDF',
14
+ value:'.pdf,application/pdf',
15
+ label:'PDF',
16
+ },
17
+ {
18
+ key :'VID',
19
+ value:'.mp4,.ogg,.webm,video/mp4,video/ogg,video/webm',
20
+ label:'Videos',
21
+ },
22
+ {
23
+ key :'IMG',
24
+ value:'.png,.jpeg,.jpg,.gif,.webp,image/png,image/jpeg,image/gif,image/webp',
25
+ label:'Images',
26
+ },
27
+ {
28
+ key :'AUD',
29
+ value:'.mp3,.ogg,.wav,audio/mpeg,audio/ogg,audio/wav',
30
+ label:'Audio',
31
+ },
32
+ ]
33
+
34
+ export const getFileType = (file) => {
35
+ const mimeType = file?.type
36
+ const extension = file?.name?.split('.').pop().toLowerCase()
37
+ const fileType = fileUploadOptions.find(
38
+ (option) => option.value.includes(mimeType) || option.value.includes(extension))
39
+
40
+ return fileType ? fileType.key : 'unknown'
41
+ }
42
+
43
+ export const getFileTypeFromUrl = (url) => {
44
+ const urlWithoutParams = url.split('?')[0]
45
+ const extension = urlWithoutParams.split('.').pop().toLowerCase()
46
+ const fileType = fileUploadOptions.find((option) => option.value.includes(extension))
47
+
48
+ return fileType ? fileType.key : 'unknown'
49
+ }
@@ -9,3 +9,9 @@ export { Checkbox } from './Checkbox'
9
9
  export { QueryChoices } from './QueryChoices'
10
10
  export { LinkInput } from './LinkInput'
11
11
  export { EditorInput } from './EditorInput'
12
+ export {
13
+ FileUpload,
14
+ fileUploadOptions,
15
+ getFileType,
16
+ getFileTypeFromUrl,
17
+ } from './FileUpload'
@@ -33,6 +33,8 @@ const FormBuilder = ({
33
33
  onRendererFormSave,
34
34
  onBuilderValidate,
35
35
  initialBuilderValues,
36
+ fileUploadStatus,
37
+ handleFileDelete,
36
38
  // ...otherProps
37
39
  }) => (
38
40
  <div
@@ -65,6 +67,8 @@ const FormBuilder = ({
65
67
  readOnly={readOnly}
66
68
  shouldSubmit={shouldSubmit}
67
69
  onError={onRendererError}
70
+ fileUploadStatus={fileUploadStatus}
71
+ handleFileDelete={handleFileDelete}
68
72
  />
69
73
  )}
70
74
  </div>
@@ -119,6 +123,11 @@ FormBuilder.propTypes = {
119
123
  * Whether the form builder in renderer mode should submit the form values itself
120
124
  */
121
125
  shouldSubmit:PropTypes.bool,
126
+
127
+ /**
128
+ * The upload status of files if any
129
+ */
130
+ fileUploadStatus:PropTypes.objectOf(PropTypes.string),
122
131
  }
123
132
 
124
133
  FormBuilder.defaultProps = {
@@ -9,7 +9,9 @@ import { FieldArray, useFormikContext } from 'formik'
9
9
 
10
10
  import styleNames from '@pareto-engineering/bem/exports'
11
11
 
12
- import { SelectInput, TextInput } from 'ui/f'
12
+ import {
13
+ ChoicesInput, SelectInput, TextInput, integerAndGreaterThanZero, fileUploadOptions,
14
+ } from 'ui/f'
13
15
 
14
16
  import { ToggleSwitch } from 'ui/a'
15
17
 
@@ -23,6 +25,12 @@ const baseClassName = styleNames.base
23
25
 
24
26
  const componentClassName = 'input-builder'
25
27
 
28
+ const getToggleSwitchStyles = (condition) => (
29
+ condition
30
+ ? { '--slider-background-color': 'var(--disabled)' }
31
+ : {}
32
+ )
33
+
26
34
  /**
27
35
  * This is the component description.
28
36
  */
@@ -43,15 +51,6 @@ const InputBuilder = ({
43
51
  setFieldValue(`sections.${sectionIndex}.inputs.${inputIndex}.required`, !input?.required)
44
52
  }
45
53
 
46
- const toggleSwitchStyles = {
47
- ...(!input?.required
48
- ? {
49
- '--slider-background-color':'var(--disabled)',
50
- }
51
- : {}
52
- ),
53
- }
54
-
55
54
  const [hasDescription, setHasDescription] = useState(false)
56
55
 
57
56
  // TODO: handle nav logic
@@ -72,6 +71,12 @@ const InputBuilder = ({
72
71
  setFieldValue(`sections.${sectionIndex}.inputs.${inputIndex}.options.${optionIndex}.label`, value)
73
72
  }
74
73
 
74
+ const isFile = input?.type === 'file'
75
+
76
+ const handleToggleShowSpecificFileTypes = () => {
77
+ setFieldValue(`sections.${sectionIndex}.inputs.${inputIndex}.showSpecificFileTypes`, !input.showSpecificFileTypes)
78
+ }
79
+
75
80
  return (
76
81
  <div
77
82
  id={id}
@@ -121,6 +126,10 @@ const InputBuilder = ({
121
126
  value:'choices',
122
127
  label:'Radio buttons',
123
128
  },
129
+ {
130
+ value:'file',
131
+ label:'File upload',
132
+ },
124
133
  ]}
125
134
  />
126
135
  <div className="controls">
@@ -138,7 +147,7 @@ const InputBuilder = ({
138
147
  <ToggleSwitch
139
148
  handleOnChange={handleToggleRequired}
140
149
  checked={input?.required}
141
- style={toggleSwitchStyles}
150
+ style={getToggleSwitchStyles(!input?.required)}
142
151
  inputId={`sections_${sectionIndex}_inputs.${inputIndex}_toggle`}
143
152
  />
144
153
  </div>
@@ -231,6 +240,39 @@ const InputBuilder = ({
231
240
  )}
232
241
  </FieldArray>
233
242
  )}
243
+
244
+ {isFile && (
245
+ <div className="input-file-options">
246
+ <TextInput
247
+ label="Maximum number of files"
248
+ name={`sections.${sectionIndex}.inputs.${inputIndex}.maxCount`}
249
+ placeholder="1"
250
+ type="number"
251
+ min="0"
252
+ validate={integerAndGreaterThanZero}
253
+ />
254
+ <div className="specific-file-types">
255
+ <ToggleSwitch
256
+ handleOnChange={handleToggleShowSpecificFileTypes}
257
+ checked={input?.showSpecificFileTypes}
258
+ style={getToggleSwitchStyles(!input?.showSpecificFileTypes)}
259
+ inputId={`sections_${sectionIndex}_inputs.${inputIndex}_show_specific_file_types`}
260
+ />
261
+ <span className="s0">
262
+ Allow only specific file types
263
+ </span>
264
+ </div>
265
+ {input?.showSpecificFileTypes && (
266
+ <>
267
+ <ChoicesInput
268
+ name={`sections.${sectionIndex}.inputs.${inputIndex}.accept`}
269
+ options={fileUploadOptions}
270
+ multiple
271
+ />
272
+ </>
273
+ )}
274
+ </div>
275
+ )}
234
276
  </div>
235
277
  </div>
236
278
  )
@@ -137,6 +137,17 @@ $default-list-width: var(--action-button-width, 18rem);
137
137
  }
138
138
  }
139
139
  }
140
+
141
+ > .input-file-options {
142
+ display: flex;
143
+ flex-direction: column;
144
+ gap: var(--gap);
145
+ margin-bottom: $default-margin;
146
+
147
+ > .specific-file-types {
148
+ display: flex;
149
+ }
150
+ }
140
151
  }
141
152
  }
142
153
 
@@ -23,9 +23,14 @@ const componentClassName = 'renderer'
23
23
 
24
24
  const reconstructFormDataWithValues = (formData, values) => {
25
25
  const newData = { ...formData }
26
- Object.keys(values).forEach((key) => {
26
+ Object.keys(values).forEach(async (key) => {
27
27
  const [sectionIdx, inputIdx] = key.match(/\d+/g).map(Number)
28
- newData.sections[sectionIdx].inputs[inputIdx].value = values[key]
28
+ if (key.includes('files')) {
29
+ const files = values[key].map((file) => URL.createObjectURL(file))
30
+ newData.sections[sectionIdx].inputs[inputIdx].files = files
31
+ } else {
32
+ newData.sections[sectionIdx].inputs[inputIdx].value = values[key]
33
+ }
29
34
  })
30
35
  return newData
31
36
  }
@@ -57,6 +62,8 @@ const Renderer = ({
57
62
  onSave,
58
63
  onError,
59
64
  shouldSubmit,
65
+ fileUploadStatus,
66
+ handleFileDelete,
60
67
  // ...otherProps
61
68
  }) => {
62
69
  const [currentSectionIndex, setCurrentSectionIndex] = useState(0)
@@ -144,7 +151,14 @@ const Renderer = ({
144
151
  <Form>
145
152
  {updatedFormData.sections.map((section, sectionIndex) => (
146
153
  sectionIndex === currentSectionIndex && (
147
- <Section key={`${section.title}`} {...section} readOnly={readOnly} />
154
+ <Section
155
+ key={`${section.title}`}
156
+ {...section}
157
+ readOnly={readOnly}
158
+ setUpdatedFormData={setUpdatedFormData}
159
+ fileUploadStatus={fileUploadStatus}
160
+ handleFileDelete={handleFileDelete}
161
+ />
148
162
  )
149
163
  ))}
150
164
  <div className="navigator-container">
@@ -1,3 +1,4 @@
1
+ /* eslint-disable no-alert */
1
2
  /* @pareto-engineering/generator-front 1.1.1-alpha.2 */
2
3
  import * as React from 'react'
3
4
 
@@ -26,6 +27,8 @@ const Section = ({
26
27
  description,
27
28
  inputs,
28
29
  readOnly,
30
+ fileUploadStatus,
31
+ handleFileDelete,
29
32
  // ...otherProps
30
33
  }) => (
31
34
  <div
@@ -46,8 +49,14 @@ const Section = ({
46
49
  nodes={description}
47
50
  name="instructions"
48
51
  />
49
- {inputs.map((input) => (
50
- <FormInput key={input.name} {...input} disabled={readOnly} />
52
+ {inputs?.map((input) => (
53
+ <FormInput
54
+ key={input.name}
55
+ {...input}
56
+ disabled={readOnly}
57
+ uploadStatus={fileUploadStatus}
58
+ handleFileDelete={handleFileDelete}
59
+ />
51
60
  ))}
52
61
  </div>
53
62
  )