@popsure/dirty-swan 0.26.10 → 0.26.13

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 (240) hide show
  1. package/dist/index.css +62 -0
  2. package/dist/index.css.map +1 -1
  3. package/dist/lib/scss/private/base/_border_radius.scss +15 -0
  4. package/dist/lib/scss/private/base/_grid.scss +8 -0
  5. package/dist/lib/scss/private/base/_index.scss +1 -0
  6. package/dist/lib/scss/private/base/style.module.scss +10 -0
  7. package/dist/lib/scss/public/colors/default.scss +5 -0
  8. package/dist/lib/scss/public/demo.tsx +13 -1
  9. package/package.json +2 -1
  10. package/src/App.tsx +50 -0
  11. package/src/bin/index.ts +71 -0
  12. package/src/bin/tsconfig.json +13 -0
  13. package/src/bin/util/index.test.ts +85 -0
  14. package/src/bin/util/index.ts +132 -0
  15. package/src/bin/util/test/data.json +13 -0
  16. package/src/colors.scss +1 -0
  17. package/src/font-weight.scss +1 -0
  18. package/src/grid.scss +1 -0
  19. package/src/index.tsx +37 -0
  20. package/src/intro.stories.mdx +41 -0
  21. package/src/lib/components/autocompleteAddress/demo.tsx +38 -0
  22. package/src/lib/components/autocompleteAddress/index.stories.mdx +44 -0
  23. package/src/lib/components/autocompleteAddress/index.tsx +316 -0
  24. package/src/lib/components/autocompleteAddress/mapStyle.ts +187 -0
  25. package/src/lib/components/autocompleteAddress/style.module.scss +82 -0
  26. package/src/lib/components/autocompleteAddress/util/index.test.ts +51 -0
  27. package/src/lib/components/autocompleteAddress/util/index.ts +55 -0
  28. package/src/lib/components/button/icons/index.ts +14 -0
  29. package/src/lib/components/button/icons/send-purple.svg +4 -0
  30. package/src/lib/components/button/icons/send-white.svg +4 -0
  31. package/src/lib/components/button/index.stories.mdx +121 -0
  32. package/src/lib/components/button/index.tsx +64 -0
  33. package/src/lib/components/button/styles.module.scss +5 -0
  34. package/src/lib/components/cards/a.stories.mdx +44 -0
  35. package/src/lib/components/cards/cardButton/index.stories.mdx +47 -0
  36. package/src/lib/components/cards/cardButton/index.tsx +61 -0
  37. package/src/lib/components/cards/cardButton/style.module.scss +33 -0
  38. package/src/lib/components/cards/cardWithLeftIcon/index.stories.mdx +103 -0
  39. package/src/lib/components/cards/cardWithLeftIcon/index.tsx +87 -0
  40. package/src/lib/components/cards/cardWithLeftIcon/style.module.scss +23 -0
  41. package/src/lib/components/cards/cardWithTopIcon/index.stories.mdx +105 -0
  42. package/src/lib/components/cards/cardWithTopIcon/index.tsx +60 -0
  43. package/src/lib/components/cards/cardWithTopIcon/style.module.scss +10 -0
  44. package/src/lib/components/cards/cardWithTopLeftIcon/index.stories.mdx +101 -0
  45. package/src/lib/components/cards/cardWithTopLeftIcon/index.tsx +72 -0
  46. package/src/lib/components/cards/cardWithTopLeftIcon/style.module.scss +21 -0
  47. package/src/lib/components/cards/icons/arrow-right.svg +4 -0
  48. package/src/lib/components/cards/icons/chevron-right.svg +3 -0
  49. package/src/lib/components/cards/icons/feather-logo.svg +10 -0
  50. package/src/lib/components/cards/icons/index.ts +36 -0
  51. package/src/lib/components/cards/icons/info.svg +12 -0
  52. package/src/lib/components/cards/index.test.ts +37 -0
  53. package/src/lib/components/cards/index.tsx +57 -0
  54. package/src/lib/components/cards/infoCard/index.stories.mdx +61 -0
  55. package/src/lib/components/cards/infoCard/index.tsx +47 -0
  56. package/src/lib/components/cards/infoCard/style.module.scss +53 -0
  57. package/src/lib/components/chip/icons/remove-button-highlighted.svg +4 -0
  58. package/src/lib/components/chip/icons/remove-button.svg +4 -0
  59. package/src/lib/components/chip/index.stories.mdx +101 -0
  60. package/src/lib/components/chip/index.tsx +38 -0
  61. package/src/lib/components/chip/style.module.scss +54 -0
  62. package/src/lib/components/comparisonTable/components/Chevron.tsx +19 -0
  63. package/src/lib/components/comparisonTable/components/Row/index.tsx +68 -0
  64. package/src/lib/components/comparisonTable/components/Row/style.module.scss +114 -0
  65. package/src/lib/components/comparisonTable/components/TableArrows/Arrow.tsx +19 -0
  66. package/src/lib/components/comparisonTable/components/TableArrows/index.tsx +52 -0
  67. package/src/lib/components/comparisonTable/components/TableArrows/style.module.scss +53 -0
  68. package/src/lib/components/comparisonTable/components/TableInfoButton/index.tsx +37 -0
  69. package/src/lib/components/comparisonTable/components/TableInfoButton/style.module.scss +30 -0
  70. package/src/lib/components/comparisonTable/components/TableRating/StarIcon.tsx +13 -0
  71. package/src/lib/components/comparisonTable/components/TableRating/ZapIcon.tsx +17 -0
  72. package/src/lib/components/comparisonTable/components/TableRating/index.tsx +45 -0
  73. package/src/lib/components/comparisonTable/components/TableRating/style.module.scss +13 -0
  74. package/src/lib/components/comparisonTable/components/TableRowHeader/index.tsx +35 -0
  75. package/src/lib/components/comparisonTable/components/TableRowHeader/style.module.scss +3 -0
  76. package/src/lib/components/comparisonTable/components/TableTrueFalse.tsx +40 -0
  77. package/src/lib/components/comparisonTable/hooks/useActiveTableArrows.ts +63 -0
  78. package/src/lib/components/comparisonTable/index.stories.mdx +254 -0
  79. package/src/lib/components/comparisonTable/index.tsx +211 -0
  80. package/src/lib/components/comparisonTable/style.module.scss +104 -0
  81. package/src/lib/components/dateSelector/datepicker.scss +406 -0
  82. package/src/lib/components/dateSelector/icons/calendar.svg +6 -0
  83. package/src/lib/components/dateSelector/icons/chevron-left.svg +3 -0
  84. package/src/lib/components/dateSelector/icons/chevron-right.svg +3 -0
  85. package/src/lib/components/dateSelector/index.stories.mdx +62 -0
  86. package/src/lib/components/dateSelector/index.test.ts +33 -0
  87. package/src/lib/components/dateSelector/index.tsx +247 -0
  88. package/src/lib/components/dateSelector/style.module.scss +77 -0
  89. package/src/lib/components/downloadButton/icons/check.svg +3 -0
  90. package/src/lib/components/downloadButton/icons/download.svg +5 -0
  91. package/src/lib/components/downloadButton/index.stories.mdx +59 -0
  92. package/src/lib/components/downloadButton/index.tsx +67 -0
  93. package/src/lib/components/downloadButton/style.module.scss +19 -0
  94. package/src/lib/components/downloadRing/icons/check-outside-circle.tsx +26 -0
  95. package/src/lib/components/downloadRing/icons/download-cloud.tsx +18 -0
  96. package/src/lib/components/downloadRing/icons/style.module.scss +7 -0
  97. package/src/lib/components/downloadRing/index.stories.mdx +35 -0
  98. package/src/lib/components/downloadRing/index.tsx +79 -0
  99. package/src/lib/components/downloadRing/style.module.scss +66 -0
  100. package/src/lib/components/dropzone/images/error.tsx +18 -0
  101. package/src/lib/components/dropzone/images/file.tsx +26 -0
  102. package/src/lib/components/dropzone/images/style.module.scss +7 -0
  103. package/src/lib/components/dropzone/images/upload-complete.tsx +24 -0
  104. package/src/lib/components/dropzone/images/upload.tsx +18 -0
  105. package/src/lib/components/dropzone/index.stories.mdx +44 -0
  106. package/src/lib/components/dropzone/index.tsx +152 -0
  107. package/src/lib/components/dropzone/style.module.scss +90 -0
  108. package/src/lib/components/input/a.stories.mdx +28 -0
  109. package/src/lib/components/input/autoSuggestInput/index.stories.mdx +137 -0
  110. package/src/lib/components/input/autoSuggestInput/index.tsx +81 -0
  111. package/src/lib/components/input/autoSuggestInput/style.module.scss +71 -0
  112. package/src/lib/components/input/autoSuggestMultiSelect/index.stories.mdx +115 -0
  113. package/src/lib/components/input/autoSuggestMultiSelect/index.tsx +75 -0
  114. package/src/lib/components/input/autoSuggestMultiSelect/style.module.scss +4 -0
  115. package/src/lib/components/input/currency/format/index.test.ts +49 -0
  116. package/src/lib/components/input/currency/format/index.ts +15 -0
  117. package/src/lib/components/input/currency/index.stories.mdx +25 -0
  118. package/src/lib/components/input/currency/index.test.tsx +56 -0
  119. package/src/lib/components/input/currency/index.tsx +53 -0
  120. package/src/lib/components/input/iban/formatIban/index.test.ts +11 -0
  121. package/src/lib/components/input/iban/formatIban/index.ts +22 -0
  122. package/src/lib/components/input/iban/index.stories.mdx +21 -0
  123. package/src/lib/components/input/iban/index.tsx +20 -0
  124. package/src/lib/components/input/index.stories.mdx +62 -0
  125. package/src/lib/components/input/index.tsx +51 -0
  126. package/src/lib/components/input/style.module.scss +94 -0
  127. package/src/lib/components/modal/bottomModal/img/close.svg +4 -0
  128. package/src/lib/components/modal/bottomModal/index.tsx +68 -0
  129. package/src/lib/components/modal/bottomModal/style.module.scss +104 -0
  130. package/src/lib/components/modal/bottomOrRegularModal/index.tsx +43 -0
  131. package/src/lib/components/modal/bottomOrRegularModal/style.module.scss +16 -0
  132. package/src/lib/components/modal/hooks/useOnClose.ts +51 -0
  133. package/src/lib/components/modal/index.stories.mdx +316 -0
  134. package/src/lib/components/modal/index.ts +14 -0
  135. package/src/lib/components/modal/regularModal/img/close.svg +4 -0
  136. package/src/lib/components/modal/regularModal/index.tsx +55 -0
  137. package/src/lib/components/modal/regularModal/style.module.scss +106 -0
  138. package/src/lib/components/multiDropzone/UploadFileCell/index.tsx +138 -0
  139. package/src/lib/components/multiDropzone/UploadFileCell/style.module.scss +101 -0
  140. package/src/lib/components/multiDropzone/icons/bmp-complete.svg +10 -0
  141. package/src/lib/components/multiDropzone/icons/bmp.svg +10 -0
  142. package/src/lib/components/multiDropzone/icons/doc-complete.svg +11 -0
  143. package/src/lib/components/multiDropzone/icons/doc.svg +11 -0
  144. package/src/lib/components/multiDropzone/icons/docx-complete.svg +12 -0
  145. package/src/lib/components/multiDropzone/icons/docx.svg +12 -0
  146. package/src/lib/components/multiDropzone/icons/eye.svg +4 -0
  147. package/src/lib/components/multiDropzone/icons/generic-complete.svg +4 -0
  148. package/src/lib/components/multiDropzone/icons/generic-error.svg +7 -0
  149. package/src/lib/components/multiDropzone/icons/generic.svg +4 -0
  150. package/src/lib/components/multiDropzone/icons/heic-complete.svg +11 -0
  151. package/src/lib/components/multiDropzone/icons/heic.svg +11 -0
  152. package/src/lib/components/multiDropzone/icons/index.ts +51 -0
  153. package/src/lib/components/multiDropzone/icons/jpeg-complete.svg +11 -0
  154. package/src/lib/components/multiDropzone/icons/jpeg.svg +11 -0
  155. package/src/lib/components/multiDropzone/icons/jpg-complete.svg +10 -0
  156. package/src/lib/components/multiDropzone/icons/jpg.svg +10 -0
  157. package/src/lib/components/multiDropzone/icons/pdf-complete.svg +8 -0
  158. package/src/lib/components/multiDropzone/icons/pdf.svg +8 -0
  159. package/src/lib/components/multiDropzone/icons/png-complete.svg +10 -0
  160. package/src/lib/components/multiDropzone/icons/png.svg +10 -0
  161. package/src/lib/components/multiDropzone/icons/trash.svg +6 -0
  162. package/src/lib/components/multiDropzone/icons/upload.svg +5 -0
  163. package/src/lib/components/multiDropzone/index.stories.mdx +91 -0
  164. package/src/lib/components/multiDropzone/index.tsx +99 -0
  165. package/src/lib/components/multiDropzone/style.module.scss +32 -0
  166. package/src/lib/components/segmentedControl/index.stories.mdx +47 -0
  167. package/src/lib/components/segmentedControl/index.tsx +105 -0
  168. package/src/lib/components/segmentedControl/style.module.scss +36 -0
  169. package/src/lib/components/signaturePad/img/reset.svg +4 -0
  170. package/src/lib/components/signaturePad/img/sign.svg +3 -0
  171. package/src/lib/components/signaturePad/index.stories.mdx +17 -0
  172. package/src/lib/components/signaturePad/index.tsx +96 -0
  173. package/src/lib/components/signaturePad/style.module.scss +90 -0
  174. package/src/lib/index.tsx +71 -0
  175. package/src/lib/models/autoSuggestInput/index.ts +4 -0
  176. package/src/lib/models/download.ts +1 -0
  177. package/src/lib/models/downloadRing/index.ts +6 -0
  178. package/src/lib/scss/index.scss +22 -0
  179. package/src/lib/scss/private/_reset.scss +149 -0
  180. package/src/lib/scss/private/base/_border_radius.scss +15 -0
  181. package/src/lib/scss/private/base/_colors.scss +19 -0
  182. package/src/lib/scss/private/base/_cursors.scss +31 -0
  183. package/src/lib/scss/private/base/_display.scss +35 -0
  184. package/src/lib/scss/private/base/_grid.scss +60 -0
  185. package/src/lib/scss/private/base/_index.scss +10 -0
  186. package/src/lib/scss/private/base/_shadows.scss +2 -0
  187. package/src/lib/scss/private/base/_spacing.scss +89 -0
  188. package/src/lib/scss/private/base/_typography.scss +128 -0
  189. package/src/lib/scss/private/base/_width_and_height.scss +25 -0
  190. package/src/lib/scss/private/base/border_radius.stories.mdx +43 -0
  191. package/src/lib/scss/private/base/cursors.stories.mdx +18 -0
  192. package/src/lib/scss/private/base/demo.tsx +119 -0
  193. package/src/lib/scss/private/base/display.stories.mdx +19 -0
  194. package/src/lib/scss/private/base/flex/_flex.scss +63 -0
  195. package/src/lib/scss/private/base/flex/flex.stories.mdx +139 -0
  196. package/src/lib/scss/private/base/flex/style.module.scss +24 -0
  197. package/src/lib/scss/private/base/spacing.stories.mdx +185 -0
  198. package/src/lib/scss/private/base/style.module.scss +52 -0
  199. package/src/lib/scss/private/base/typography.stories.mdx +71 -0
  200. package/src/lib/scss/private/base/width_and_height.stories.mdx +172 -0
  201. package/src/lib/scss/private/components/_badge.scss +41 -0
  202. package/src/lib/scss/private/components/_buttons.scss +193 -0
  203. package/src/lib/scss/private/components/_cards.scss +32 -0
  204. package/src/lib/scss/private/components/_index.scss +6 -0
  205. package/src/lib/scss/private/components/_input.scss +241 -0
  206. package/src/lib/scss/private/components/_notices.scss +39 -0
  207. package/src/lib/scss/private/components/_spinner.scss +60 -0
  208. package/src/lib/scss/private/components/assets/checkmark.svg +3 -0
  209. package/src/lib/scss/private/components/assets/icon-form-dropdown.svg +3 -0
  210. package/src/lib/scss/private/components/badge.stories.mdx +37 -0
  211. package/src/lib/scss/private/components/button.stories.mdx +107 -0
  212. package/src/lib/scss/private/components/cards.stories.mdx +35 -0
  213. package/src/lib/scss/private/components/checkbox.stories.mdx +47 -0
  214. package/src/lib/scss/private/components/input.stories.mdx +33 -0
  215. package/src/lib/scss/private/components/notices.stories.mdx +37 -0
  216. package/src/lib/scss/private/components/radio.stories.mdx +47 -0
  217. package/src/lib/scss/private/components/select.stories.mdx +17 -0
  218. package/src/lib/scss/private/components/spinner.stories.mdx +25 -0
  219. package/src/lib/scss/public/colors/_index.scss +2 -0
  220. package/src/lib/scss/public/colors/default.scss +130 -0
  221. package/src/lib/scss/public/colors/overrides.scss +0 -0
  222. package/src/lib/scss/public/colors.stories.mdx +27 -0
  223. package/src/lib/scss/public/demo.tsx +297 -0
  224. package/src/lib/scss/public/font/_index.scss +2 -0
  225. package/src/lib/scss/public/font/default.scss +3 -0
  226. package/src/lib/scss/public/font/overrides.scss +0 -0
  227. package/src/lib/scss/public/font-weight.scss +9 -0
  228. package/src/lib/scss/public/font-weight.stories.mdx +32 -0
  229. package/src/lib/scss/public/grid.scss +21 -0
  230. package/src/lib/scss/public/grid.stories.mdx +41 -0
  231. package/src/lib/scss/third-party/_google_places.scss +62 -0
  232. package/src/lib/scss/third-party/_index.scss +1 -0
  233. package/src/lib/scss/utils/_index.scss +3 -0
  234. package/src/lib/util/calendarDate/index.test.ts +32 -0
  235. package/src/lib/util/calendarDate/index.ts +30 -0
  236. package/src/lib/util/zeroFill.test.ts +15 -0
  237. package/src/lib/util/zeroFill.ts +7 -0
  238. package/src/react-app-env.d.ts +1 -0
  239. package/src/setupTests.js +8 -0
  240. package/src/theme.stories.mdx +54 -0
@@ -0,0 +1,137 @@
1
+ import { useState } from 'react';
2
+ import { Meta } from '@storybook/addon-docs/blocks';
3
+
4
+ import AutoSuggestInput from '.';
5
+ import featherLogo from '../../cards/icons/feather-logo.svg';
6
+
7
+ <Meta title="JSX/Inputs/AutoSuggestInput" />
8
+
9
+ # AutoSuggestInput
10
+
11
+ This component allows quick search via the input field to find an option for selection.
12
+
13
+ ## Types
14
+
15
+ ```typescript
16
+ export interface Option {
17
+ value: string; // value of option to be stored and displayed on UI
18
+ leftIcon?: string; // image of the provided option to be displayed on UI
19
+ }
20
+ ```
21
+
22
+ ## Arguments
23
+
24
+ | attribute | unit | description | default value | required |
25
+ | ---------------------------- | -------- | ----------------------------------------------------------------------------------------- | ------------- | -------- |
26
+ | currentOption | string | Current input of the component by user | n/a | true |
27
+ | suggestions | Option | List of suggestions that should be displayed to the respective input | n/a | true |
28
+ | onChange | function | Function that is called when value of current input changes | n/a | true |
29
+ | handleSuggestionSelected | function | Functon that runs when a suggestion is selected from the dropdown | n/a | true |
30
+ | handleSuggestionFetchRequest | function | Function that allows control of which suggestions should be displayed | n/a | true |
31
+ | handleSuggestionClearRequest | function | Function that runs when suggestions are cleared (eg. input removal, selecting suggestion) | n/a | true |
32
+ | placeholder | string | Placeholder for DirtySwan Input component | n/a | true |
33
+ | className | string | Class name for the most parent element | undefined | false |
34
+ | wrapText | boolean | Wether or not wrap the entries in the dropdown or hide overflown text | false | false |
35
+
36
+ ## Example
37
+
38
+ Following component has five options to search from: `feather`, `feather2`, `feather3`,`dirtyswan`, `test value`
39
+
40
+ export const AutoSuggestInputStory = () => {
41
+ const [selectedValues, setSelectedValues] = useState([]);
42
+ const [currentOption, setCurrentOption] = useState('');
43
+ const [suggestions, setSuggestions] = useState([]);
44
+ const options = [
45
+ { value: 'feather', leftIcon: featherLogo },
46
+ { value: 'feather2', leftIcon: featherLogo },
47
+ {
48
+ value: 'feather3',
49
+ leftIcon: featherLogo,
50
+ },
51
+ {
52
+ value: 'dirtyswan',
53
+ leftIcon: featherLogo,
54
+ },
55
+ {
56
+ value: 'test value',
57
+ leftIcon: featherLogo,
58
+ },
59
+ ];
60
+ return (
61
+ <AutoSuggestInput
62
+ className="wmx5"
63
+ placeholder="Placeholder"
64
+ currentOption={currentOption}
65
+ suggestions={suggestions}
66
+ onChange={(value) => {
67
+ setCurrentOption(value);
68
+ }}
69
+ handleSuggestionSelected={(value) => {
70
+ const newSelectedOptions = [...selectedValues];
71
+ newSelectedOptions.push(value);
72
+ setSelectedValues(newSelectedOptions);
73
+ setCurrentOption(value.value);
74
+ }}
75
+ handleSuggestionFetchRequest={({ value }) => {
76
+ const filteredOptions = options.filter((option) =>
77
+ option.value.toLowerCase().startsWith(value.toLowerCase())
78
+ );
79
+ setSuggestions(filteredOptions);
80
+ }}
81
+ handleSuggestionClearRequest={() => setSuggestions([])}
82
+ />
83
+ );
84
+ };
85
+
86
+ <AutoSuggestInputStory />
87
+
88
+ ```typescript
89
+ import React, { useState } from 'react';
90
+ import { AutoSuggestInput } from '@popsure/dirty-swan';
91
+
92
+ export default () => {
93
+ const [selectedValues, setSelectedValues] = useState([]);
94
+ const [currentOption, setCurrentOption] = useState('');
95
+ const [suggestions, setSuggestions] = useState([]);
96
+ const options = [
97
+ { value: 'feather', leftIcon: featherLogo },
98
+ { value: 'feather2', leftIcon: featherLogo },
99
+ {
100
+ value: 'feather3',
101
+ leftIcon: featherLogo,
102
+ },
103
+ {
104
+ value: 'dirtyswan',
105
+ leftIcon: featherLogo,
106
+ },
107
+ {
108
+ value: 'test value',
109
+ leftIcon: featherLogo,
110
+ },
111
+ ];
112
+ return (
113
+ <AutoSuggestInput
114
+ className="wmx5"
115
+ placeholder="Placeholder"
116
+ currentOption={currentOption}
117
+ suggestions={suggestions}
118
+ onChange={(value) => {
119
+ setCurrentOption(value);
120
+ }}
121
+ handleSuggestionSelected={(value) => {
122
+ const newSelectedOptions = [...selectedValues];
123
+ newSelectedOptions.push(value);
124
+ setSelectedValues(newSelectedOptions);
125
+ setCurrentOption(value.value);
126
+ }}
127
+ handleSuggestionFetchRequest={({ value }) => {
128
+ const filteredOptions = options.filter((option) =>
129
+ option.value.toLowerCase().startsWith(value.toLowerCase())
130
+ );
131
+ setSuggestions(filteredOptions);
132
+ }}
133
+ handleSuggestionClearRequest={() => setSuggestions([])}
134
+ />
135
+ );
136
+ };
137
+ ```
@@ -0,0 +1,81 @@
1
+ import classNames from 'classnames';
2
+ import Autosuggest from 'react-autosuggest';
3
+
4
+ import styles from './style.module.scss';
5
+ import { Option } from '../../../models/autoSuggestInput';
6
+ import Input, { InputProps } from '../index';
7
+
8
+ export default ({
9
+ currentOption,
10
+ suggestions,
11
+ handleSuggestionSelected,
12
+ onChange,
13
+ handleSuggestionFetchRequest,
14
+ handleSuggestionClearRequest,
15
+ placeholder,
16
+ className,
17
+ wrapText,
18
+ }: {
19
+ currentOption: string;
20
+ suggestions: Option[];
21
+ handleSuggestionSelected: (value: Option) => void;
22
+ onChange: (value: string) => void;
23
+ handleSuggestionFetchRequest: (value: Option) => void;
24
+ handleSuggestionClearRequest: () => void;
25
+ placeholder: string;
26
+ className?: string;
27
+ wrapText?: boolean;
28
+ }) => {
29
+ const renderSuggestion = (suggestion: Option) => (
30
+ <div className={`${styles['suggestion-option']}`}>
31
+ {suggestion.leftIcon && (
32
+ <img
33
+ className={`mr16 ${styles['suggestion-img']}`}
34
+ src={suggestion.leftIcon}
35
+ alt={suggestion.value}
36
+ />
37
+ )}
38
+ <div
39
+ className={classNames(styles['suggestion-text'], {
40
+ [styles.nowrap]: !wrapText,
41
+ })}
42
+ >
43
+ {suggestion.value}
44
+ </div>
45
+ </div>
46
+ );
47
+
48
+ const getSuggestionValue = (suggestion: Option) => suggestion.value;
49
+
50
+ const renderInputComponent = (inputProps: Omit<InputProps, 'ref'>) => (
51
+ <Input
52
+ {...inputProps}
53
+ placeholder={placeholder}
54
+ data-cy="suggest-multi-select-input"
55
+ />
56
+ );
57
+
58
+ return (
59
+ <div className={className}>
60
+ <Autosuggest
61
+ theme={styles}
62
+ suggestions={suggestions}
63
+ onSuggestionsFetchRequested={handleSuggestionFetchRequest}
64
+ onSuggestionsClearRequested={handleSuggestionClearRequest}
65
+ getSuggestionValue={getSuggestionValue}
66
+ renderSuggestion={renderSuggestion}
67
+ highlightFirstSuggestion={true}
68
+ inputProps={{
69
+ value: currentOption,
70
+ onChange: (_, { newValue }) => {
71
+ onChange(newValue);
72
+ },
73
+ }}
74
+ onSuggestionSelected={(_, { suggestion }) => {
75
+ handleSuggestionSelected(suggestion);
76
+ }}
77
+ renderInputComponent={renderInputComponent}
78
+ />
79
+ </div>
80
+ );
81
+ };
@@ -0,0 +1,71 @@
1
+ @keyframes appearInAnimation {
2
+ from {
3
+ opacity: 0;
4
+ transform: translateY(16px);
5
+ }
6
+
7
+ to {
8
+ opacity: 1;
9
+ }
10
+ }
11
+
12
+ .suggestionsList {
13
+ position: absolute;
14
+
15
+ z-index: 100;
16
+ overflow: hidden;
17
+ border-radius: 8px;
18
+ border: 1px solid var(--ds-primary-500);
19
+
20
+ width: 100%;
21
+ max-width: 364px;
22
+ height: fit-content;
23
+ max-height: 216px;
24
+
25
+ overflow-y: scroll;
26
+
27
+ background-color: white;
28
+
29
+ animation-name: appearInAnimation;
30
+ animation-duration: 0.3s;
31
+ animation-fill-mode: both;
32
+
33
+ transform: translateY(8px);
34
+ }
35
+
36
+ .suggestion-option {
37
+ display: flex;
38
+ align-items: center;
39
+
40
+ cursor: pointer;
41
+ position: relative;
42
+
43
+ margin: 0;
44
+ padding: 12px 16px;
45
+
46
+ color: var(--ds-grey-900);
47
+
48
+ min-height: 48px;
49
+ line-height: 24px;
50
+ }
51
+
52
+ .suggestion-img {
53
+ width: 32px;
54
+ height: 24px;
55
+
56
+ border-radius: 2px;
57
+ }
58
+
59
+ .suggestion-text {
60
+ flex: 1;
61
+ }
62
+
63
+ .nowrap {
64
+ white-space: nowrap;
65
+ overflow: hidden;
66
+ text-overflow: ellipsis;
67
+ }
68
+
69
+ .suggestionHighlighted {
70
+ background-color: var(--ds-primary-100);
71
+ }
@@ -0,0 +1,115 @@
1
+ import { useState } from 'react';
2
+ import { Meta, Preview } from '@storybook/addon-docs/blocks';
3
+
4
+ import AutoSuggestMultiSelect from '.';
5
+ import featherLogo from '../../cards/icons/feather-logo.svg';
6
+
7
+ <Meta title="JSX/Inputs/AutoSuggestMultiSelect" />
8
+
9
+ ## AutoSuggestMultiSelect
10
+
11
+ AutoSuggestMultiSelect is a combination of the AutoSuggestInput and Chip components.
12
+
13
+ This component allows quick search via the input field to find an option for selection.
14
+
15
+ Upon selecting an option, the option is displayed above the input field as a chip.
16
+
17
+ The chip can be removed by clicking on the X button. Multi options can be selected as well.
18
+
19
+ Prop `setValues` function must be provided to keep track of removed and added option selections.
20
+
21
+ ## Types
22
+
23
+ ```typescript
24
+ export interface Option {
25
+ value: string; // value of option to be stored and displayed on UI
26
+ leftIcon?: string; // image of the provided option to be displayed on UI
27
+ }
28
+ ```
29
+
30
+ ## Arguments
31
+
32
+ | attribute | unit | description | default value | required |
33
+ | -------------------- | -------- | ------------------------------------------------------------------------ | ------------- | -------- |
34
+ | options | Option[] | List of all options available to search from | n/a | true |
35
+ | selectedValues | Option[] | List of all selected values | n/a | true |
36
+ | setValues | function | Function that runs when selecting values | n/a | true |
37
+ | placeholder | string | Placeholder for DirtySwan Input component | n/a | true |
38
+ | chipsListClassName | string | Class name for the most parent element of the Chip component | undefined | false |
39
+ | multiSelectClassName | string | Class name for the most parent element of the AutoSuggestInput component | undefined | false |
40
+ | wrapText | boolean | Wether or not wrap the entries in the dropdown or hide overflown text | false | false |
41
+
42
+ ## Example
43
+
44
+ Following component has five options to search from: `feather`, `feather2`, `feather3`,`dirtyswan`. `test value`
45
+
46
+ export const AutoSuggestMultiSelectStory = () => {
47
+ const [selectedValues, setSelectedValues] = useState([]);
48
+ const [suggestions, setSuggestions] = useState([]);
49
+ const [currentOption, setCurrentOption] = useState('');
50
+ const options = [
51
+ { value: 'feather', leftIcon: featherLogo },
52
+ { value: 'feather2', leftIcon: featherLogo },
53
+ {
54
+ value: 'feather3',
55
+ leftIcon: featherLogo,
56
+ },
57
+ {
58
+ value: 'dirtyswan',
59
+ leftIcon: featherLogo,
60
+ },
61
+ {
62
+ value: 'test value',
63
+ leftIcon: featherLogo,
64
+ },
65
+ ];
66
+ return (
67
+ <AutoSuggestMultiSelect
68
+ options={options}
69
+ selectedValues={selectedValues}
70
+ setValues={setSelectedValues}
71
+ placeholder="Placeholder"
72
+ chipsListClassName="wmx7"
73
+ multiSelectClassName="wmx5"
74
+ />
75
+ );
76
+ };
77
+
78
+ <AutoSuggestMultiSelectStory />
79
+
80
+ ```typescript
81
+ import React, { useState } from 'react';
82
+ import { AutoSuggestMultiSelect } from '@popsure/dirty-swan';
83
+
84
+ export default () => {
85
+ const [selectedValues, setSelectedValues] = useState([]);
86
+ const [suggestions, setSuggestions] = useState([]);
87
+ const [currentOption, setCurrentOption] = useState('');
88
+ const options = [
89
+ { value: 'feather', leftIcon: featherLogo },
90
+ { value: 'feather2', leftIcon: featherLogo },
91
+ {
92
+ value: 'feather3',
93
+ leftIcon: featherLogo,
94
+ },
95
+ {
96
+ value: 'dirtyswan',
97
+ leftIcon: featherLogo,
98
+ },
99
+ {
100
+ value: 'test value',
101
+ leftIcon: featherLogo,
102
+ },
103
+ ];
104
+ return (
105
+ <AutoSuggestMultiSelect
106
+ options={options}
107
+ selectedValues={selectedValues}
108
+ setValues={setSelectedValues}
109
+ placeholder="Placeholder"
110
+ chipsListClassName="wmx7"
111
+ multiSelectClassName="wmx5"
112
+ />
113
+ );
114
+ };
115
+ ```
@@ -0,0 +1,75 @@
1
+ import { useState } from 'react';
2
+
3
+ import { Option } from '../../../models/autoSuggestInput';
4
+ import Chip from '../../chip';
5
+ import AutoSuggestInput from '../autoSuggestInput';
6
+ import styles from './style.module.scss';
7
+
8
+ export default ({
9
+ options,
10
+ selectedValues,
11
+ setValues,
12
+ placeholder,
13
+ chipsListClassName,
14
+ multiSelectClassName,
15
+ wrapText,
16
+ }: {
17
+ options: Option[];
18
+ selectedValues?: Option[];
19
+ setValues: (values: Option[]) => void;
20
+ placeholder: string;
21
+ chipsListClassName?: string;
22
+ multiSelectClassName?: string;
23
+ wrapText?: boolean;
24
+ }) => {
25
+ const [suggestions, setSuggestions] = useState<Option[]>([]);
26
+ const [currentOption, setCurrentOption] = useState('');
27
+
28
+ return (
29
+ <>
30
+ {selectedValues && selectedValues.length > 0 && (
31
+ <div
32
+ className={`mb8 ${styles['chip-container']} ${chipsListClassName}`}
33
+ >
34
+ {selectedValues.map((value, index) => (
35
+ <Chip
36
+ key={`${value.value}-${index}`}
37
+ value={value}
38
+ onRemove={(value: Option) => {
39
+ const newValues = [...selectedValues].filter(
40
+ (selectedValue) => selectedValue.value !== value.value
41
+ );
42
+ setValues(newValues);
43
+ }}
44
+ />
45
+ ))}
46
+ </div>
47
+ )}
48
+ <AutoSuggestInput
49
+ className={multiSelectClassName}
50
+ placeholder={placeholder}
51
+ onChange={setCurrentOption}
52
+ handleSuggestionSelected={(value) => {
53
+ const newSelectedOptions = selectedValues ?? [];
54
+ newSelectedOptions?.push(value);
55
+ setValues(newSelectedOptions);
56
+ setCurrentOption('');
57
+ }}
58
+ handleSuggestionFetchRequest={({ value }) => {
59
+ const filteredOptions = options.filter(
60
+ (option) =>
61
+ option.value.toLowerCase().startsWith(value.toLowerCase()) &&
62
+ selectedValues?.find(
63
+ (selectedValue) => selectedValue.value === option.value
64
+ ) === undefined
65
+ );
66
+ setSuggestions(filteredOptions);
67
+ }}
68
+ currentOption={currentOption}
69
+ suggestions={suggestions}
70
+ handleSuggestionClearRequest={() => setSuggestions([])}
71
+ wrapText={wrapText}
72
+ />
73
+ </>
74
+ );
75
+ };
@@ -0,0 +1,4 @@
1
+ .chip-container {
2
+ display: flex;
3
+ flex-wrap: wrap;
4
+ }
@@ -0,0 +1,49 @@
1
+ import { formatInput, reverseFormatInput } from './';
2
+
3
+ describe('Format input', () => {
4
+ it('Should add space for thousands separator', () => {
5
+ expect(formatInput('1000')).toEqual('1 000');
6
+ });
7
+
8
+ it('Should add space for hundred thousands separator', () => {
9
+ expect(formatInput('172888')).toEqual('172 888');
10
+ });
11
+
12
+ it('Should add space for million separator', () => {
13
+ expect(formatInput('1728281')).toEqual('1 728 281');
14
+ });
15
+
16
+ it('Should add dot for decimal separator', () => {
17
+ expect(formatInput('1728281.23')).toEqual('1 728 281.23');
18
+ });
19
+
20
+ it('Should replace comma with dot', () => {
21
+ expect(formatInput('1728281,23')).toEqual('1 728 281.23');
22
+ });
23
+
24
+ it('Should truncate 2 digits after decimal separator', () => {
25
+ expect(formatInput('1728281.23392')).toEqual('1 728 281.23');
26
+ });
27
+ });
28
+
29
+ describe('Reverse format input', () => {
30
+ it('Should reverse format 1 728 281.23', () => {
31
+ expect(reverseFormatInput('1 728 281.23')).toEqual(1728281.23);
32
+ });
33
+
34
+ it('Should reverse format 1 728 281,23', () => {
35
+ expect(reverseFormatInput('1 728 281,23')).toEqual(1728281.23);
36
+ });
37
+
38
+ it('Should reverse format 1 728 281', () => {
39
+ expect(reverseFormatInput('1 728 281')).toEqual(1728281);
40
+ });
41
+
42
+ it('Should reverse format 172 888', () => {
43
+ expect(reverseFormatInput('172 888')).toEqual(172888);
44
+ });
45
+
46
+ it('Should reverse format 1 000', () => {
47
+ expect(reverseFormatInput('1 000')).toEqual(1000);
48
+ });
49
+ });
@@ -0,0 +1,15 @@
1
+ export const formatInput = (input: string): string => {
2
+ const decimalSeparator = input.includes(',') ? ',' : '.';
3
+ const parts = input.split(decimalSeparator);
4
+ const floor = parts[0];
5
+ const ceiling = parts[1];
6
+ parts[0] = floor.replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
7
+ if (ceiling) {
8
+ parts[1] = ceiling.slice(0, Math.min(ceiling.length, 2));
9
+ }
10
+ return parts.join('.');
11
+ };
12
+
13
+ export function reverseFormatInput(input: string): number {
14
+ return Number(input.replace(/,/, '.').replace(/\s/g, ''));
15
+ }
@@ -0,0 +1,25 @@
1
+ import { Meta, Preview } from '@storybook/addon-docs/blocks';
2
+
3
+ import CurrencyInput from '.';
4
+
5
+ <Meta title="JSX/Inputs/Currency Input" />
6
+
7
+ # Currency Input
8
+
9
+ Currency input is a component built on top of the [Input](?path=/story/jsx-inputs-intro--page) component that give you a better formatting when numerical currency values.
10
+
11
+ The following will be done:
12
+
13
+ - Adding a Euro sign (€) prefix.
14
+ - Formatting thousand separators with a space as user type in.
15
+ - Setting decimal reparator as a dot (.)
16
+
17
+ <Preview>
18
+ <>
19
+ <h1 className="p-h1">Currency Input</h1>
20
+ <h4 className="p-h4 mt24">Empty</h4>
21
+ <CurrencyInput className="wmx5 mt8" />
22
+ <h4 className="p-h4 mt24">Filled</h4>
23
+ <CurrencyInput className="wmx5 mt8" value={1234567.32} />
24
+ </>
25
+ </Preview>
@@ -0,0 +1,56 @@
1
+ import { fireEvent, render } from '@testing-library/react';
2
+
3
+ import CurrencyInput from '.';
4
+
5
+ const setup = () => {
6
+ const utils = render(<CurrencyInput />);
7
+ const input = utils.getByTestId('ds-input-input') as HTMLInputElement;
8
+ return {
9
+ input,
10
+ ...utils,
11
+ };
12
+ };
13
+
14
+ describe('Currency input component', () => {
15
+ it('Should correctly space thousands separators', () => {
16
+ const { input } = setup();
17
+ fireEvent.change(input, { target: { value: '1234567' } });
18
+ expect(input.value).toBe('1 234 567');
19
+ });
20
+
21
+ it('Should remove non numerical values', () => {
22
+ const { input } = setup();
23
+ fireEvent.change(input, { target: { value: '123asdf4567' } });
24
+ expect(input.value).toBe('1 234 567');
25
+ });
26
+
27
+ it('Should remove non numerical values', () => {
28
+ const { input } = setup();
29
+ fireEvent.change(input, { target: { value: '123asdf4567' } });
30
+ expect(input.value).toBe('1 234 567');
31
+ });
32
+
33
+ it('Should allow decimal separator', () => {
34
+ const { input } = setup();
35
+ fireEvent.change(input, { target: { value: '1234567.34' } });
36
+ expect(input.value).toBe('1 234 567.34');
37
+ });
38
+
39
+ it('Should replace comma decimal seprator with a dot', () => {
40
+ const { input } = setup();
41
+ fireEvent.change(input, { target: { value: '1234567,34' } });
42
+ expect(input.value).toBe('1 234 567.34');
43
+ });
44
+
45
+ it('Should only allow one decimal separator', () => {
46
+ const { input } = setup();
47
+ fireEvent.change(input, { target: { value: '1234567..34' } });
48
+ expect(input.value).toBe('1 234 567.34');
49
+ });
50
+
51
+ it('Should only allow one decimal separator after a sequence of number', () => {
52
+ const { input } = setup();
53
+ fireEvent.change(input, { target: { value: '1234567..34.4' } });
54
+ expect(input.value).toBe('1 234 567.34');
55
+ });
56
+ });
@@ -0,0 +1,53 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ import { formatInput, reverseFormatInput } from './format';
4
+ import Input, { InputProps } from '..';
5
+
6
+ const CurrencyInput = ({
7
+ value,
8
+ onChange,
9
+ type,
10
+ ...props
11
+ }: {
12
+ value?: number;
13
+ placeholder?: string;
14
+ onChange?: (value: number) => void;
15
+ } & Omit<InputProps, 'onChange' | 'value' | 'ref'>) => {
16
+ const [shadowValue, setShadowValue] = useState('');
17
+
18
+ const formattedShadowValue = formatInput(
19
+ shadowValue
20
+ .replace(/ /g, '') // remove all whitespace
21
+ .replace(',', '.') // change commas to dot for decimal separator
22
+ .replace('.', 'DECIMAL_SEPARATOR') // Gymnastic to remove all the but the first decimal separators 🤸
23
+ .replace(/\./g, '')
24
+ .replace('DECIMAL_SEPARATOR', '.') // End of the Gymnastic 🤸
25
+ .replace(/[^\d\\.]/g, '') // remove all non decimal and dot
26
+ );
27
+
28
+ useEffect(() => {
29
+ if (value && value !== reverseFormatInput(shadowValue)) {
30
+ setShadowValue(formatInput(value.toString()));
31
+ }
32
+ // eslint-disable-next-line
33
+ }, [value]);
34
+
35
+ useEffect(() => {
36
+ onChange?.(reverseFormatInput(shadowValue));
37
+ // eslint-disable-next-line
38
+ }, [shadowValue]);
39
+
40
+ return (
41
+ <Input
42
+ prefix="€"
43
+ type="string"
44
+ value={formattedShadowValue}
45
+ onChange={(e) => {
46
+ setShadowValue(e.target.value);
47
+ }}
48
+ {...props}
49
+ />
50
+ );
51
+ };
52
+
53
+ export default CurrencyInput;