@toptal/picasso-tagselector 3.0.0 → 3.1.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.
- package/dist-package/src/TagSelector/TagSelector.d.ts +4 -2
- package/dist-package/src/TagSelector/TagSelector.d.ts.map +1 -1
- package/dist-package/src/TagSelector/TagSelector.js +5 -1
- package/dist-package/src/TagSelector/TagSelector.js.map +1 -1
- package/package.json +2 -2
- package/src/TagSelector/TagSelector.tsx +10 -2
- package/src/TagSelector/story/OtherOptionSubmissionOnEnter.example.tsx +37 -0
- package/src/TagSelector/story/index.jsx +10 -0
- package/src/TagSelector/test.tsx +59 -1
|
@@ -22,10 +22,12 @@ export interface Props extends BaseProps, Omit<InputHTMLAttributes<HTMLInputElem
|
|
|
22
22
|
otherOptionLabel?: string;
|
|
23
23
|
/** Callback invoked when other option selected */
|
|
24
24
|
onOtherOptionSelect?: (value: string) => void;
|
|
25
|
+
/** Select other option on Enter key press */
|
|
26
|
+
submitOtherOptionOnEnter?: boolean;
|
|
25
27
|
/** Allow to show the other option in the list of options */
|
|
26
28
|
showOtherOption?: boolean;
|
|
27
|
-
/** Label to show when no options were found */
|
|
28
|
-
noOptionsText?: string;
|
|
29
|
+
/** Label to show when no options were found (pass "null" to hide label completely) */
|
|
30
|
+
noOptionsText?: string | null;
|
|
29
31
|
/** List of options with unique labels */
|
|
30
32
|
options?: Item[] | null;
|
|
31
33
|
/** The list of values of the selected options, required for a controlled component. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TagSelector.d.ts","sourceRoot":"","sources":["../../../src/TagSelector/TagSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,mBAAmB,EACnB,SAAS,EACT,iBAAiB,EAClB,MAAM,OAAO,CAAA;AACd,OAAO,KAA+B,MAAM,OAAO,CAAA;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAG9C,OAAO,KAAK,EACV,iBAAiB,EACjB,IAAI,IAAI,gBAAgB,EACzB,MAAM,8BAA8B,CAAA;AAErC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gCAAgC,CAAA;AAK5D,MAAM,WAAW,IAAK,SAAQ,gBAAgB;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAID,eAAO,MAAM,wBAAwB,YAC1B,IAAI,EAAE,GAAG,IAAI,UACd,IAAI,EAAE,kBACE,IAAI,4BACnB,gBAAgB,EAAE,GAAG,IAWvB,CAAA;AAKD,MAAM,WAAW,KACf,SAAQ,SAAS,EACf,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC;IACnE,4BAA4B;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,6BAA6B;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,sDAAsD;IACtD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,mCAAmC;IACnC,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,kDAAkD;IAClD,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7C,4DAA4D;IAC5D,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB
|
|
1
|
+
{"version":3,"file":"TagSelector.d.ts","sourceRoot":"","sources":["../../../src/TagSelector/TagSelector.tsx"],"names":[],"mappings":"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"}
|
|
@@ -24,7 +24,7 @@ export const filterOutSelectedOptions = (options, values, getKey = (item) => ite
|
|
|
24
24
|
};
|
|
25
25
|
const getItemText = (item) => (item && item.text) || EMPTY_INPUT_VALUE;
|
|
26
26
|
export const TagSelector = forwardRef(function TagSelector(props, ref) {
|
|
27
|
-
const { disabled, enableAutofill, getDisplayValue = getItemText, getKey: customGetKey, inputValue = '', loading, noOptionsText, onBlur, onChange = noop, onFocus, onInputChange = noop, onOtherOptionSelect = noop, options = [], otherOptionLabel, placeholder, renderLabel: customRenderLabel, renderOption, showOtherOption, value: values = [], width, popperContainer, popperOptions, status, testIds, highlight } = props, rest = __rest(props, ["disabled", "enableAutofill", "getDisplayValue", "getKey", "inputValue", "loading", "noOptionsText", "onBlur", "onChange", "onFocus", "onInputChange", "onOtherOptionSelect", "options", "otherOptionLabel", "placeholder", "renderLabel", "renderOption", "showOtherOption", "value", "width", "popperContainer", "popperOptions", "status", "testIds", "highlight"]);
|
|
27
|
+
const { disabled, enableAutofill, getDisplayValue = getItemText, getKey: customGetKey, inputValue = '', loading, noOptionsText, onBlur, onChange = noop, onFocus, onInputChange = noop, onOtherOptionSelect = noop, options = [], otherOptionLabel, placeholder, renderLabel: customRenderLabel, renderOption, submitOtherOptionOnEnter, showOtherOption, value: values = [], width, popperContainer, popperOptions, status, testIds, highlight } = props, rest = __rest(props, ["disabled", "enableAutofill", "getDisplayValue", "getKey", "inputValue", "loading", "noOptionsText", "onBlur", "onChange", "onFocus", "onInputChange", "onOtherOptionSelect", "options", "otherOptionLabel", "placeholder", "renderLabel", "renderOption", "submitOtherOptionOnEnter", "showOtherOption", "value", "width", "popperContainer", "popperOptions", "status", "testIds", "highlight"]);
|
|
28
28
|
const handleDelete = (value) => {
|
|
29
29
|
if (disabled) {
|
|
30
30
|
return;
|
|
@@ -35,9 +35,13 @@ export const TagSelector = forwardRef(function TagSelector(props, ref) {
|
|
|
35
35
|
const handleKeyDown = (event, newInputValue) => {
|
|
36
36
|
const hasSelection = values.length;
|
|
37
37
|
const isDeleting = event.key === 'Backspace';
|
|
38
|
+
const isEnter = event.key === 'Enter';
|
|
38
39
|
if (hasSelection && !newInputValue && isDeleting) {
|
|
39
40
|
handleDelete(values[values.length - 1]);
|
|
40
41
|
}
|
|
42
|
+
if (submitOtherOptionOnEnter && isEnter && newInputValue !== '') {
|
|
43
|
+
handleOtherOptionSelect(newInputValue);
|
|
44
|
+
}
|
|
41
45
|
};
|
|
42
46
|
const handleSelect = (autocompleteItem) => {
|
|
43
47
|
const item = autocompleteItem;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TagSelector.js","sourceRoot":"","sources":["../../../src/TagSelector/TagSelector.tsx"],"names":[],"mappings":";;;;;;;;;;;AAOA,OAAO,KAAK,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAGnD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC3D,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAA;AAQ5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAOtD,MAAM,iBAAiB,GAAG,EAAE,CAAA;AAE5B,MAAM,CAAC,MAAM,wBAAwB,GAAG,CACtC,OAAsB,EACtB,MAAc,EACd,SAAS,CAAC,IAAU,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,EACR,EAAE;IAC7B,IAAI,CAAC,OAAO,EAAE;QACZ,OAAO,IAAI,CAAA;KACZ;IAED,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAChC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAW,CAAC,EAC9C,IAAI,GAAG,EAAU,CAClB,CAAA;IAED,OAAO,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAW,CAAC,CAAC,CAAA;AAC9E,CAAC,CAAA;AAED,MAAM,WAAW,GAAG,CAAC,IAAiB,EAAE,EAAE,CACxC,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"TagSelector.js","sourceRoot":"","sources":["../../../src/TagSelector/TagSelector.tsx"],"names":[],"mappings":";;;;;;;;;;;AAOA,OAAO,KAAK,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAGnD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC3D,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAA;AAQ5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAOtD,MAAM,iBAAiB,GAAG,EAAE,CAAA;AAE5B,MAAM,CAAC,MAAM,wBAAwB,GAAG,CACtC,OAAsB,EACtB,MAAc,EACd,SAAS,CAAC,IAAU,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,EACR,EAAE;IAC7B,IAAI,CAAC,OAAO,EAAE;QACZ,OAAO,IAAI,CAAA;KACZ;IAED,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAChC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAW,CAAC,EAC9C,IAAI,GAAG,EAAU,CAClB,CAAA;IAED,OAAO,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAW,CAAC,CAAC,CAAA;AAC9E,CAAC,CAAA;AAED,MAAM,WAAW,GAAG,CAAC,IAAiB,EAAE,EAAE,CACxC,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAA;AAgE1C,MAAM,CAAC,MAAM,WAAW,GAAG,UAAU,CACnC,SAAS,WAAW,CAAC,KAAK,EAAE,GAAG;IAC7B,MAAM,EACJ,QAAQ,EACR,cAAc,EACd,eAAe,GAAG,WAAW,EAC7B,MAAM,EAAE,YAAY,EACpB,UAAU,GAAG,EAAE,EACf,OAAO,EACP,aAAa,EACb,MAAM,EACN,QAAQ,GAAG,IAAI,EACf,OAAO,EACP,aAAa,GAAG,IAAI,EACpB,mBAAmB,GAAG,IAAI,EAC1B,OAAO,GAAG,EAAE,EACZ,gBAAgB,EAChB,WAAW,EACX,WAAW,EAAE,iBAAiB,EAC9B,YAAY,EACZ,wBAAwB,EACxB,eAAe,EACf,KAAK,EAAE,MAAM,GAAG,EAAE,EAClB,KAAK,EACL,eAAe,EACf,aAAa,EACb,MAAM,EACN,OAAO,EACP,SAAS,KAEP,KAAK,EADJ,IAAI,UACL,KAAK,EA5BH,kYA4BL,CAAQ,CAAA;IAET,MAAM,YAAY,GAAG,CAAC,KAAW,EAAE,EAAE;QACnC,IAAI,QAAQ,EAAE;YACZ,OAAM;SACP;QAED,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;QAEnC,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;IACnE,CAAC,CAAA;IAED,MAAM,aAAa,GAAG,CACpB,KAAsC,EACtC,aAAqB,EACrB,EAAE;QACF,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAA;QAClC,MAAM,UAAU,GAAG,KAAK,CAAC,GAAG,KAAK,WAAW,CAAA;QAC5C,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,KAAK,OAAO,CAAA;QAErC,IAAI,YAAY,IAAI,CAAC,aAAa,IAAI,UAAU,EAAE;YAChD,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAA;SACxC;QAED,IAAI,wBAAwB,IAAI,OAAO,IAAI,aAAa,KAAK,EAAE,EAAE;YAC/D,uBAAuB,CAAC,aAAa,CAAC,CAAA;SACvC;IACH,CAAC,CAAA;IAED,MAAM,YAAY,GAAG,CAAC,gBAAkC,EAAE,EAAE;QAC1D,MAAM,IAAI,GAAG,gBAAwB,CAAA;QAErC,QAAQ,CAAC,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,CAAC,CAAA;QAC3B,aAAa,CAAC,EAAE,CAAC,CAAA;IACnB,CAAC,CAAA;IAED,MAAM,uBAAuB,GAAG,CAAC,KAAa,EAAE,EAAE;QAChD,aAAa,CAAC,EAAE,CAAC,CAAA;QACjB,mBAAmB,CAAC,KAAK,CAAC,CAAA;IAC5B,CAAC,CAAA;IAED,MAAM,MAAM,GAAG,CAAC,IAAU,EAAU,EAAE;QACpC,IAAI,YAAY,EAAE;YAChB,OAAO,YAAY,CAAC,IAAI,CAAC,CAAA;SAC1B;QAED,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE;YAC5B,OAAO,IAAI,CAAC,KAAK,CAAA;SAClB;QAED,cAAc,CACZ,8EAA8E,CAC/E,CAAA;QAED,OAAO,EAAE,CAAA;IACX,CAAC,CAAA;IAED,MAAM,mBAAmB,GACvB,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAEnD,MAAM,WAAW,GAAG,CAAC,IAAU,EAAE,EAAE;QACjC,MAAM,YAAY,GAAG,eAAe,CAAC,IAAI,CAAC,CAAA;QAC1C,MAAM,gBAAgB,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAA;QACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,KAAK,OAAO,CAAA;QAExC,IAAI,iBAAiB,EAAE;YACrB,OAAO,iBAAiB,CAAC;gBACvB,QAAQ;gBACR,IAAI;gBACJ,YAAY;gBACZ,QAAQ,EAAE,gBAAgB;aAC3B,CAAC,CAAA;SACH;QAED,OAAO,CACL,oBAAC,gBAAgB,IACf,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,EACrC,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,gBAAgB,IAEzB,YAAY,CACI,CACpB,CAAA;IACH,CAAC,CAAA;IAED,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CACpC,oBAAC,QAAQ,IAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,IAAG,WAAW,CAAC,IAAI,CAAC,CAAY,CAC5D,CAAC,CAAA;IAEF,OAAO,CACL,oBAAC,YAAY,oBACP,IAAI,IACR,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,GAAG,EACR,WAAW,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,EAC1D,OAAO,EAAE,mBAAmB,EAC5B,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,YAAY,EACtB,mBAAmB,EAAE,uBAAuB,EAC5C,QAAQ,EAAE,aAAa,EACvB,SAAS,EAAE,aAAa,EACxB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,UAAU,EAC1B,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,EAClB,cAAc,EAAE,gBAA6C,EAC7D,KAAK,EAAE,KAAK,EACZ,eAAe,EAAE,eAAe,EAChC,eAAe,EAAE,gBAAgB,EACjC,aAAa,EAAE,aAAa,EAC5B,cAAc,EAAE,cAAc,EAC9B,eAAe,EAAE,eAAe,EAChC,YAAY,EAAE,YAAY,EAC1B,WAAW,EAAE,KAAK,EAClB,MAAM,EAAE,MAAM,EACd,eAAe,EAAE,eAAe,EAChC,aAAa,EAAE,aAAa,EAC5B,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,SAAS,IACpB,CACH,CAAA;AACH,CAAC,CACF,CAAA;AAED,WAAW,CAAC,YAAY,GAAG;IACzB,cAAc,EAAE,KAAK;IACrB,eAAe,EAAE,WAAW;IAC5B,OAAO,EAAE,KAAK;IACd,QAAQ,EAAE,IAAI;IACd,aAAa,EAAE,IAAI;IACnB,aAAa,EAAE,IAAI;IACnB,mBAAmB,EAAE,IAAI;IACzB,OAAO,EAAE,EAAE;IACX,gBAAgB,EAAE,kBAAkB;IACpC,aAAa,EAAE,kBAAkB;IACjC,WAAW,EAAE,EAAE;IACf,eAAe,EAAE,KAAK;IACtB,MAAM,EAAE,SAAS;CAClB,CAAA;AAED,WAAW,CAAC,WAAW,GAAG,aAAa,CAAA;AAEvC,eAAe,WAAW,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toptal/picasso-tagselector",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Toptal UI components library - Tagselector",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"homepage": "https://github.com/toptal/picasso/tree/master/packages/picasso#readme",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@toptal/picasso-autocomplete": "5.0.
|
|
25
|
+
"@toptal/picasso-autocomplete": "5.0.1",
|
|
26
26
|
"@toptal/picasso-form": "6.0.0",
|
|
27
27
|
"@toptal/picasso-outlined-input": "4.0.0",
|
|
28
28
|
"@toptal/picasso-shared": "15.0.0",
|
|
@@ -62,10 +62,12 @@ export interface Props
|
|
|
62
62
|
otherOptionLabel?: string
|
|
63
63
|
/** Callback invoked when other option selected */
|
|
64
64
|
onOtherOptionSelect?: (value: string) => void
|
|
65
|
+
/** Select other option on Enter key press */
|
|
66
|
+
submitOtherOptionOnEnter?: boolean
|
|
65
67
|
/** Allow to show the other option in the list of options */
|
|
66
68
|
showOtherOption?: boolean
|
|
67
|
-
/** Label to show when no options were found */
|
|
68
|
-
noOptionsText?: string
|
|
69
|
+
/** Label to show when no options were found (pass "null" to hide label completely) */
|
|
70
|
+
noOptionsText?: string | null
|
|
69
71
|
/** List of options with unique labels */
|
|
70
72
|
options?: Item[] | null
|
|
71
73
|
/** The list of values of the selected options, required for a controlled component. */
|
|
@@ -127,6 +129,7 @@ export const TagSelector = forwardRef<HTMLInputElement, Props>(
|
|
|
127
129
|
placeholder,
|
|
128
130
|
renderLabel: customRenderLabel,
|
|
129
131
|
renderOption,
|
|
132
|
+
submitOtherOptionOnEnter,
|
|
130
133
|
showOtherOption,
|
|
131
134
|
value: values = [],
|
|
132
135
|
width,
|
|
@@ -154,10 +157,15 @@ export const TagSelector = forwardRef<HTMLInputElement, Props>(
|
|
|
154
157
|
) => {
|
|
155
158
|
const hasSelection = values.length
|
|
156
159
|
const isDeleting = event.key === 'Backspace'
|
|
160
|
+
const isEnter = event.key === 'Enter'
|
|
157
161
|
|
|
158
162
|
if (hasSelection && !newInputValue && isDeleting) {
|
|
159
163
|
handleDelete(values[values.length - 1])
|
|
160
164
|
}
|
|
165
|
+
|
|
166
|
+
if (submitOtherOptionOnEnter && isEnter && newInputValue !== '') {
|
|
167
|
+
handleOtherOptionSelect(newInputValue)
|
|
168
|
+
}
|
|
161
169
|
}
|
|
162
170
|
|
|
163
171
|
const handleSelect = (autocompleteItem: AutocompleteItem) => {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import type { AutocompleteItem } from '@toptal/picasso'
|
|
3
|
+
import { TagSelector } from '@toptal/picasso'
|
|
4
|
+
|
|
5
|
+
const EMPTY_INPUT_VALUE = ''
|
|
6
|
+
const getDisplayValue = (item: AutocompleteItem | null) =>
|
|
7
|
+
item && item.text ? item.text : EMPTY_INPUT_VALUE
|
|
8
|
+
|
|
9
|
+
const Example = () => {
|
|
10
|
+
const [value, setValue] = useState<AutocompleteItem[]>([])
|
|
11
|
+
const [inputValue, setInputValue] = useState(EMPTY_INPUT_VALUE)
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div>
|
|
15
|
+
<TagSelector
|
|
16
|
+
noOptionsText={null}
|
|
17
|
+
placeholder='Type and press Enter to select...'
|
|
18
|
+
value={value}
|
|
19
|
+
inputValue={inputValue}
|
|
20
|
+
getDisplayValue={getDisplayValue}
|
|
21
|
+
onChange={selectedValues => {
|
|
22
|
+
window.console.log('onChange values: ', selectedValues)
|
|
23
|
+
setValue(selectedValues)
|
|
24
|
+
}}
|
|
25
|
+
onOtherOptionSelect={otherOption => {
|
|
26
|
+
setValue([...value, { value: otherOption, text: otherOption }])
|
|
27
|
+
}}
|
|
28
|
+
onInputChange={newInputValue => {
|
|
29
|
+
window.console.log('onInputChange value: ', newInputValue)
|
|
30
|
+
setInputValue(newInputValue)
|
|
31
|
+
}}
|
|
32
|
+
/>
|
|
33
|
+
</div>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default Example
|
|
@@ -30,6 +30,16 @@ page
|
|
|
30
30
|
},
|
|
31
31
|
'base/Tagselector'
|
|
32
32
|
)
|
|
33
|
+
.addExample(
|
|
34
|
+
'TagSelector/story/OtherOptionSubmissionOnEnter.example.tsx',
|
|
35
|
+
{
|
|
36
|
+
title: 'Other option submission on Enter',
|
|
37
|
+
description:
|
|
38
|
+
'Press Enter to submit other option value. Combine it with empty `options` and `noOptionsText` properties to hide the options dropdown and achieve regular input behavior and look.',
|
|
39
|
+
takeScreenshot: false,
|
|
40
|
+
},
|
|
41
|
+
'base/Tagselector'
|
|
42
|
+
)
|
|
33
43
|
.addExample(
|
|
34
44
|
'TagSelector/story/InitialSetValue.example.tsx',
|
|
35
45
|
'Initially set value',
|
package/src/TagSelector/test.tsx
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import React from 'react'
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import { screen } from '@testing-library/react'
|
|
2
3
|
import type { RenderResult } from '@toptal/picasso-test-utils'
|
|
3
4
|
import { render, fireEvent, waitFor } from '@toptal/picasso-test-utils'
|
|
4
5
|
import type { OmitInternalProps } from '@toptal/picasso-shared'
|
|
6
|
+
import type { AutocompleteItem } from '@toptal/picasso'
|
|
5
7
|
|
|
6
8
|
import type { Props } from './TagSelector'
|
|
7
9
|
import TagSelector, { filterOutSelectedOptions } from './TagSelector'
|
|
@@ -24,6 +26,34 @@ const renderTagSelector = (props: OmitInternalProps<Props>) => {
|
|
|
24
26
|
return render(<TagSelector {...props} />)
|
|
25
27
|
}
|
|
26
28
|
|
|
29
|
+
const TagSelectorWithValueManagement = (props: OmitInternalProps<Props>) => {
|
|
30
|
+
const [value, setValue] = useState<AutocompleteItem[]>([])
|
|
31
|
+
const [inputValue, setInputValue] = useState('')
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<TagSelector
|
|
35
|
+
value={value}
|
|
36
|
+
inputValue={inputValue}
|
|
37
|
+
onChange={selectedValues => {
|
|
38
|
+
setValue(selectedValues)
|
|
39
|
+
}}
|
|
40
|
+
onOtherOptionSelect={otherOption => {
|
|
41
|
+
setValue([...value, { value: otherOption, text: otherOption }])
|
|
42
|
+
}}
|
|
43
|
+
onInputChange={newInputValue => {
|
|
44
|
+
setInputValue(newInputValue)
|
|
45
|
+
}}
|
|
46
|
+
{...props}
|
|
47
|
+
/>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const renderTagSelectorWithValueManagement = (
|
|
52
|
+
props: OmitInternalProps<Props>
|
|
53
|
+
) => {
|
|
54
|
+
return render(<TagSelectorWithValueManagement {...props} />)
|
|
55
|
+
}
|
|
56
|
+
|
|
27
57
|
const selectOption = async (
|
|
28
58
|
renderResult: RenderResult,
|
|
29
59
|
input: HTMLElement,
|
|
@@ -121,6 +151,34 @@ describe('TagSelector', () => {
|
|
|
121
151
|
})
|
|
122
152
|
})
|
|
123
153
|
|
|
154
|
+
describe('when "submitOtherOptionOnEnter" is enabled', () => {
|
|
155
|
+
it('submits other options on Enter press', async () => {
|
|
156
|
+
const firstOtherOptionText = 'aaa'
|
|
157
|
+
const secondOtherOptionText = 'bbb'
|
|
158
|
+
const { getByText } = renderTagSelectorWithValueManagement({
|
|
159
|
+
...testProps,
|
|
160
|
+
options: null,
|
|
161
|
+
submitOtherOptionOnEnter: true,
|
|
162
|
+
testIds: {
|
|
163
|
+
input: 'input',
|
|
164
|
+
},
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
const input = screen.getByTestId('input')
|
|
168
|
+
|
|
169
|
+
fireEvent.change(input, { target: { value: firstOtherOptionText } })
|
|
170
|
+
fireEvent.keyDown(input, { key: 'Enter' })
|
|
171
|
+
fireEvent.change(input, { target: { value: secondOtherOptionText } })
|
|
172
|
+
fireEvent.keyDown(input, { key: 'Enter' })
|
|
173
|
+
|
|
174
|
+
const firstTag = getByText(firstOtherOptionText)
|
|
175
|
+
const secondTag = getByText(secondOtherOptionText)
|
|
176
|
+
|
|
177
|
+
expect(firstTag).toBeInTheDocument()
|
|
178
|
+
expect(secondTag).toBeInTheDocument()
|
|
179
|
+
})
|
|
180
|
+
})
|
|
181
|
+
|
|
124
182
|
describe('filterOutSelectedOptions', () => {
|
|
125
183
|
it("compares object's value attribute by default", () => {
|
|
126
184
|
const resultOptions = filterOutSelectedOptions(testOptions, [
|