@popsure/dirty-swan 0.26.8 → 0.26.11
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/dist/index.css +1 -0
- package/dist/index.css.map +1 -1
- package/dist/lib/scss/private/components/_buttons.scss +2 -0
- package/package.json +2 -1
- package/src/App.tsx +50 -0
- package/src/bin/index.ts +71 -0
- package/src/bin/tsconfig.json +13 -0
- package/src/bin/util/index.test.ts +85 -0
- package/src/bin/util/index.ts +132 -0
- package/src/bin/util/test/data.json +13 -0
- package/src/colors.scss +1 -0
- package/src/font-weight.scss +1 -0
- package/src/grid.scss +1 -0
- package/src/index.tsx +37 -0
- package/src/intro.stories.mdx +41 -0
- package/src/lib/components/autocompleteAddress/demo.tsx +38 -0
- package/src/lib/components/autocompleteAddress/index.stories.mdx +44 -0
- package/src/lib/components/autocompleteAddress/index.tsx +316 -0
- package/src/lib/components/autocompleteAddress/mapStyle.ts +187 -0
- package/src/lib/components/autocompleteAddress/style.module.scss +82 -0
- package/src/lib/components/autocompleteAddress/util/index.test.ts +51 -0
- package/src/lib/components/autocompleteAddress/util/index.ts +55 -0
- package/src/lib/components/button/icons/index.ts +14 -0
- package/src/lib/components/button/icons/send-purple.svg +4 -0
- package/src/lib/components/button/icons/send-white.svg +4 -0
- package/src/lib/components/button/index.stories.mdx +121 -0
- package/src/lib/components/button/index.tsx +64 -0
- package/src/lib/components/button/styles.module.scss +5 -0
- package/src/lib/components/cards/a.stories.mdx +44 -0
- package/src/lib/components/cards/cardButton/index.stories.mdx +47 -0
- package/src/lib/components/cards/cardButton/index.tsx +61 -0
- package/src/lib/components/cards/cardButton/style.module.scss +33 -0
- package/src/lib/components/cards/cardWithLeftIcon/index.stories.mdx +103 -0
- package/src/lib/components/cards/cardWithLeftIcon/index.tsx +87 -0
- package/src/lib/components/cards/cardWithLeftIcon/style.module.scss +23 -0
- package/src/lib/components/cards/cardWithTopIcon/index.stories.mdx +105 -0
- package/src/lib/components/cards/cardWithTopIcon/index.tsx +60 -0
- package/src/lib/components/cards/cardWithTopIcon/style.module.scss +10 -0
- package/src/lib/components/cards/cardWithTopLeftIcon/index.stories.mdx +101 -0
- package/src/lib/components/cards/cardWithTopLeftIcon/index.tsx +72 -0
- package/src/lib/components/cards/cardWithTopLeftIcon/style.module.scss +21 -0
- package/src/lib/components/cards/icons/arrow-right.svg +4 -0
- package/src/lib/components/cards/icons/chevron-right.svg +3 -0
- package/src/lib/components/cards/icons/feather-logo.svg +10 -0
- package/src/lib/components/cards/icons/index.ts +36 -0
- package/src/lib/components/cards/icons/info.svg +12 -0
- package/src/lib/components/cards/index.test.ts +37 -0
- package/src/lib/components/cards/index.tsx +57 -0
- package/src/lib/components/cards/infoCard/index.stories.mdx +61 -0
- package/src/lib/components/cards/infoCard/index.tsx +47 -0
- package/src/lib/components/cards/infoCard/style.module.scss +53 -0
- package/src/lib/components/chip/icons/remove-button-highlighted.svg +4 -0
- package/src/lib/components/chip/icons/remove-button.svg +4 -0
- package/src/lib/components/chip/index.stories.mdx +101 -0
- package/src/lib/components/chip/index.tsx +38 -0
- package/src/lib/components/chip/style.module.scss +54 -0
- package/src/lib/components/comparisonTable/components/Chevron.tsx +19 -0
- package/src/lib/components/comparisonTable/components/Row/index.tsx +68 -0
- package/src/lib/components/comparisonTable/components/Row/style.module.scss +114 -0
- package/src/lib/components/comparisonTable/components/TableArrows/Arrow.tsx +19 -0
- package/src/lib/components/comparisonTable/components/TableArrows/index.tsx +52 -0
- package/src/lib/components/comparisonTable/components/TableArrows/style.module.scss +53 -0
- package/src/lib/components/comparisonTable/components/TableInfoButton/index.tsx +37 -0
- package/src/lib/components/comparisonTable/components/TableInfoButton/style.module.scss +30 -0
- package/src/lib/components/comparisonTable/components/TableRating/StarIcon.tsx +13 -0
- package/src/lib/components/comparisonTable/components/TableRating/ZapIcon.tsx +17 -0
- package/src/lib/components/comparisonTable/components/TableRating/index.tsx +45 -0
- package/src/lib/components/comparisonTable/components/TableRating/style.module.scss +13 -0
- package/src/lib/components/comparisonTable/components/TableRowHeader/index.tsx +35 -0
- package/src/lib/components/comparisonTable/components/TableRowHeader/style.module.scss +3 -0
- package/src/lib/components/comparisonTable/components/TableTrueFalse.tsx +40 -0
- package/src/lib/components/comparisonTable/hooks/useActiveTableArrows.ts +63 -0
- package/src/lib/components/comparisonTable/index.stories.mdx +254 -0
- package/src/lib/components/comparisonTable/index.tsx +211 -0
- package/src/lib/components/comparisonTable/style.module.scss +104 -0
- package/src/lib/components/dateSelector/datepicker.scss +406 -0
- package/src/lib/components/dateSelector/icons/calendar.svg +6 -0
- package/src/lib/components/dateSelector/icons/chevron-left.svg +3 -0
- package/src/lib/components/dateSelector/icons/chevron-right.svg +3 -0
- package/src/lib/components/dateSelector/index.stories.mdx +62 -0
- package/src/lib/components/dateSelector/index.test.ts +33 -0
- package/src/lib/components/dateSelector/index.tsx +247 -0
- package/src/lib/components/dateSelector/style.module.scss +77 -0
- package/src/lib/components/downloadButton/icons/check.svg +3 -0
- package/src/lib/components/downloadButton/icons/download.svg +5 -0
- package/src/lib/components/downloadButton/index.stories.mdx +59 -0
- package/src/lib/components/downloadButton/index.tsx +67 -0
- package/src/lib/components/downloadButton/style.module.scss +19 -0
- package/src/lib/components/downloadRing/icons/check-outside-circle.tsx +26 -0
- package/src/lib/components/downloadRing/icons/download-cloud.tsx +18 -0
- package/src/lib/components/downloadRing/icons/style.module.scss +7 -0
- package/src/lib/components/downloadRing/index.stories.mdx +35 -0
- package/src/lib/components/downloadRing/index.tsx +79 -0
- package/src/lib/components/downloadRing/style.module.scss +66 -0
- package/src/lib/components/dropzone/images/error.tsx +18 -0
- package/src/lib/components/dropzone/images/file.tsx +26 -0
- package/src/lib/components/dropzone/images/style.module.scss +7 -0
- package/src/lib/components/dropzone/images/upload-complete.tsx +24 -0
- package/src/lib/components/dropzone/images/upload.tsx +18 -0
- package/src/lib/components/dropzone/index.stories.mdx +44 -0
- package/src/lib/components/dropzone/index.tsx +152 -0
- package/src/lib/components/dropzone/style.module.scss +90 -0
- package/src/lib/components/input/a.stories.mdx +28 -0
- package/src/lib/components/input/autoSuggestInput/index.stories.mdx +137 -0
- package/src/lib/components/input/autoSuggestInput/index.tsx +81 -0
- package/src/lib/components/input/autoSuggestInput/style.module.scss +71 -0
- package/src/lib/components/input/autoSuggestMultiSelect/index.stories.mdx +115 -0
- package/src/lib/components/input/autoSuggestMultiSelect/index.tsx +75 -0
- package/src/lib/components/input/autoSuggestMultiSelect/style.module.scss +4 -0
- package/src/lib/components/input/currency/format/index.test.ts +49 -0
- package/src/lib/components/input/currency/format/index.ts +15 -0
- package/src/lib/components/input/currency/index.stories.mdx +25 -0
- package/src/lib/components/input/currency/index.test.tsx +56 -0
- package/src/lib/components/input/currency/index.tsx +53 -0
- package/src/lib/components/input/iban/formatIban/index.test.ts +11 -0
- package/src/lib/components/input/iban/formatIban/index.ts +22 -0
- package/src/lib/components/input/iban/index.stories.mdx +21 -0
- package/src/lib/components/input/iban/index.tsx +20 -0
- package/src/lib/components/input/index.stories.mdx +62 -0
- package/src/lib/components/input/index.tsx +51 -0
- package/src/lib/components/input/style.module.scss +94 -0
- package/src/lib/components/modal/bottomModal/img/close.svg +4 -0
- package/src/lib/components/modal/bottomModal/index.tsx +68 -0
- package/src/lib/components/modal/bottomModal/style.module.scss +104 -0
- package/src/lib/components/modal/bottomOrRegularModal/index.tsx +43 -0
- package/src/lib/components/modal/bottomOrRegularModal/style.module.scss +16 -0
- package/src/lib/components/modal/hooks/useOnClose.ts +51 -0
- package/src/lib/components/modal/index.stories.mdx +316 -0
- package/src/lib/components/modal/index.ts +14 -0
- package/src/lib/components/modal/regularModal/img/close.svg +4 -0
- package/src/lib/components/modal/regularModal/index.tsx +55 -0
- package/src/lib/components/modal/regularModal/style.module.scss +106 -0
- package/src/lib/components/multiDropzone/UploadFileCell/index.tsx +138 -0
- package/src/lib/components/multiDropzone/UploadFileCell/style.module.scss +101 -0
- package/src/lib/components/multiDropzone/icons/bmp-complete.svg +10 -0
- package/src/lib/components/multiDropzone/icons/bmp.svg +10 -0
- package/src/lib/components/multiDropzone/icons/doc-complete.svg +11 -0
- package/src/lib/components/multiDropzone/icons/doc.svg +11 -0
- package/src/lib/components/multiDropzone/icons/docx-complete.svg +12 -0
- package/src/lib/components/multiDropzone/icons/docx.svg +12 -0
- package/src/lib/components/multiDropzone/icons/eye.svg +4 -0
- package/src/lib/components/multiDropzone/icons/generic-complete.svg +4 -0
- package/src/lib/components/multiDropzone/icons/generic-error.svg +7 -0
- package/src/lib/components/multiDropzone/icons/generic.svg +4 -0
- package/src/lib/components/multiDropzone/icons/heic-complete.svg +11 -0
- package/src/lib/components/multiDropzone/icons/heic.svg +11 -0
- package/src/lib/components/multiDropzone/icons/index.ts +51 -0
- package/src/lib/components/multiDropzone/icons/jpeg-complete.svg +11 -0
- package/src/lib/components/multiDropzone/icons/jpeg.svg +11 -0
- package/src/lib/components/multiDropzone/icons/jpg-complete.svg +10 -0
- package/src/lib/components/multiDropzone/icons/jpg.svg +10 -0
- package/src/lib/components/multiDropzone/icons/pdf-complete.svg +8 -0
- package/src/lib/components/multiDropzone/icons/pdf.svg +8 -0
- package/src/lib/components/multiDropzone/icons/png-complete.svg +10 -0
- package/src/lib/components/multiDropzone/icons/png.svg +10 -0
- package/src/lib/components/multiDropzone/icons/trash.svg +6 -0
- package/src/lib/components/multiDropzone/icons/upload.svg +5 -0
- package/src/lib/components/multiDropzone/index.stories.mdx +91 -0
- package/src/lib/components/multiDropzone/index.tsx +99 -0
- package/src/lib/components/multiDropzone/style.module.scss +32 -0
- package/src/lib/components/segmentedControl/index.stories.mdx +47 -0
- package/src/lib/components/segmentedControl/index.tsx +105 -0
- package/src/lib/components/segmentedControl/style.module.scss +36 -0
- package/src/lib/components/signaturePad/img/reset.svg +4 -0
- package/src/lib/components/signaturePad/img/sign.svg +3 -0
- package/src/lib/components/signaturePad/index.stories.mdx +17 -0
- package/src/lib/components/signaturePad/index.tsx +96 -0
- package/src/lib/components/signaturePad/style.module.scss +90 -0
- package/src/lib/index.tsx +71 -0
- package/src/lib/models/autoSuggestInput/index.ts +4 -0
- package/src/lib/models/download.ts +1 -0
- package/src/lib/models/downloadRing/index.ts +6 -0
- package/src/lib/scss/index.scss +22 -0
- package/src/lib/scss/private/_reset.scss +149 -0
- package/src/lib/scss/private/base/_colors.scss +19 -0
- package/src/lib/scss/private/base/_cursors.scss +31 -0
- package/src/lib/scss/private/base/_display.scss +35 -0
- package/src/lib/scss/private/base/_grid.scss +52 -0
- package/src/lib/scss/private/base/_index.scss +9 -0
- package/src/lib/scss/private/base/_shadows.scss +2 -0
- package/src/lib/scss/private/base/_spacing.scss +89 -0
- package/src/lib/scss/private/base/_typography.scss +128 -0
- package/src/lib/scss/private/base/_width_and_height.scss +25 -0
- package/src/lib/scss/private/base/cursors.stories.mdx +18 -0
- package/src/lib/scss/private/base/demo.tsx +119 -0
- package/src/lib/scss/private/base/display.stories.mdx +19 -0
- package/src/lib/scss/private/base/flex/_flex.scss +63 -0
- package/src/lib/scss/private/base/flex/flex.stories.mdx +139 -0
- package/src/lib/scss/private/base/flex/style.module.scss +24 -0
- package/src/lib/scss/private/base/spacing.stories.mdx +173 -0
- package/src/lib/scss/private/base/style.module.scss +42 -0
- package/src/lib/scss/private/base/typography.stories.mdx +71 -0
- package/src/lib/scss/private/base/width_and_height.stories.mdx +172 -0
- package/src/lib/scss/private/components/_badge.scss +41 -0
- package/src/lib/scss/private/components/_buttons.scss +193 -0
- package/src/lib/scss/private/components/_cards.scss +32 -0
- package/src/lib/scss/private/components/_index.scss +6 -0
- package/src/lib/scss/private/components/_input.scss +241 -0
- package/src/lib/scss/private/components/_notices.scss +39 -0
- package/src/lib/scss/private/components/_spinner.scss +60 -0
- package/src/lib/scss/private/components/assets/checkmark.svg +3 -0
- package/src/lib/scss/private/components/assets/icon-form-dropdown.svg +3 -0
- package/src/lib/scss/private/components/badge.stories.mdx +37 -0
- package/src/lib/scss/private/components/button.stories.mdx +107 -0
- package/src/lib/scss/private/components/cards.stories.mdx +35 -0
- package/src/lib/scss/private/components/checkbox.stories.mdx +47 -0
- package/src/lib/scss/private/components/input.stories.mdx +33 -0
- package/src/lib/scss/private/components/notices.stories.mdx +37 -0
- package/src/lib/scss/private/components/radio.stories.mdx +47 -0
- package/src/lib/scss/private/components/select.stories.mdx +17 -0
- package/src/lib/scss/private/components/spinner.stories.mdx +25 -0
- package/src/lib/scss/public/colors/_index.scss +2 -0
- package/src/lib/scss/public/colors/default.scss +125 -0
- package/src/lib/scss/public/colors/overrides.scss +0 -0
- package/src/lib/scss/public/colors.stories.mdx +27 -0
- package/src/lib/scss/public/demo.tsx +285 -0
- package/src/lib/scss/public/font/_index.scss +2 -0
- package/src/lib/scss/public/font/default.scss +3 -0
- package/src/lib/scss/public/font/overrides.scss +0 -0
- package/src/lib/scss/public/font-weight.scss +9 -0
- package/src/lib/scss/public/font-weight.stories.mdx +32 -0
- package/src/lib/scss/public/grid.scss +21 -0
- package/src/lib/scss/public/grid.stories.mdx +41 -0
- package/src/lib/scss/third-party/_google_places.scss +62 -0
- package/src/lib/scss/third-party/_index.scss +1 -0
- package/src/lib/scss/utils/_index.scss +3 -0
- package/src/lib/util/calendarDate/index.test.ts +32 -0
- package/src/lib/util/calendarDate/index.ts +30 -0
- package/src/lib/util/zeroFill.test.ts +15 -0
- package/src/lib/util/zeroFill.ts +7 -0
- package/src/react-app-env.d.ts +1 -0
- package/src/setupTests.js +8 -0
- package/src/theme.stories.mdx +54 -0
|
@@ -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,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;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { formatIban } from '.';
|
|
2
|
+
|
|
3
|
+
describe('Format IBAN testing', () => {
|
|
4
|
+
it('Should return IBAN split by four characters', () => {
|
|
5
|
+
expect(formatIban('DE1234123412341234')).toEqual('DE12 3412 3412 3412 34');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it('Should return IBAN with all caps letters', () => {
|
|
9
|
+
expect(formatIban('de1234123412341234')).toEqual('DE12 3412 3412 3412 34');
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const IBAN_CHAR_LIMIT = 4;
|
|
2
|
+
|
|
3
|
+
export const formatIban = (iban?: string): string => {
|
|
4
|
+
if (iban) {
|
|
5
|
+
const cleanIban = iban
|
|
6
|
+
.replace(/\s\s+/g, ' ')
|
|
7
|
+
.replace(/[^0-9a-zA-Z]/gi, '')
|
|
8
|
+
.toUpperCase();
|
|
9
|
+
|
|
10
|
+
const values: string[] = [];
|
|
11
|
+
|
|
12
|
+
cleanIban.split('').forEach((char, idx) => {
|
|
13
|
+
if (idx % IBAN_CHAR_LIMIT === 0) {
|
|
14
|
+
values.push(cleanIban.substring(idx, idx + IBAN_CHAR_LIMIT));
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
return values.join(' ');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return '';
|
|
22
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Meta, Preview } from '@storybook/addon-docs/blocks';
|
|
2
|
+
|
|
3
|
+
import IbanInput from '.';
|
|
4
|
+
|
|
5
|
+
<Meta title="JSX/Inputs/Iban Input" />
|
|
6
|
+
|
|
7
|
+
# Iban Input
|
|
8
|
+
|
|
9
|
+
Iban input is a component built on top of the [Input](?path=/story/jsx-inputs-intro--page) component that give you a better formatting when entering [IBAN](https://en.wikipedia.org/wiki/International_Bank_Account_Number).
|
|
10
|
+
|
|
11
|
+
As the user enter their IBAN, a spacing will be applied for better readability.
|
|
12
|
+
|
|
13
|
+
<Preview>
|
|
14
|
+
<>
|
|
15
|
+
<h1 className="p-h1">Iban Input</h1>
|
|
16
|
+
<h4 className="p-h4 mt24">Empty</h4>
|
|
17
|
+
<IbanInput className="wmx5 mt8" />
|
|
18
|
+
<h4 className="p-h4 mt24">Filled</h4>
|
|
19
|
+
<IbanInput className="wmx5 mt8" value="DE91100000000123456789" />
|
|
20
|
+
</>
|
|
21
|
+
</Preview>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import Input, { InputProps } from '..';
|
|
2
|
+
|
|
3
|
+
import { formatIban } from './formatIban';
|
|
4
|
+
|
|
5
|
+
const IbanInput = ({
|
|
6
|
+
value,
|
|
7
|
+
onChange,
|
|
8
|
+
...props
|
|
9
|
+
}: {
|
|
10
|
+
value?: string;
|
|
11
|
+
onChange: (value: string) => void;
|
|
12
|
+
} & Omit<InputProps, 'onChange' | 'value' | 'ref'>) => (
|
|
13
|
+
<Input
|
|
14
|
+
value={formatIban(value)}
|
|
15
|
+
onChange={(e) => onChange(formatIban(e.target.value))}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export default IbanInput;
|