playbook_ui 15.6.0.pre.alpha.play265012865 → 15.6.0.pre.rc.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.
- checksums.yaml +4 -4
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_table_props.html.erb +163 -1
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_table_props.jsx +190 -0
- data/app/pb_kits/playbook/pb_background/_background.tsx +6 -6
- data/app/pb_kits/playbook/pb_background/background.test.js +1 -5
- data/app/pb_kits/playbook/pb_background/docs/_background_light.html.erb +1 -1
- data/app/pb_kits/playbook/pb_background/docs/_background_light.jsx +1 -0
- data/app/pb_kits/playbook/pb_background/docs/example.yml +2 -2
- data/app/pb_kits/playbook/pb_bar_graph/_bar_graph.tsx +0 -6
- data/app/pb_kits/playbook/pb_dialog/docs/_dialog_compound_components.html.erb +0 -31
- data/app/pb_kits/playbook/pb_draggable/context/index.tsx +6 -156
- data/app/pb_kits/playbook/pb_draggable/context/types.ts +3 -8
- data/app/pb_kits/playbook/pb_draggable/docs/example.yml +2 -3
- data/app/pb_kits/playbook/pb_draggable/docs/index.js +1 -2
- data/app/pb_kits/playbook/pb_draggable/draggable.test.jsx +1 -77
- data/app/pb_kits/playbook/pb_file_upload/_file_upload.scss +4 -4
- data/app/pb_kits/playbook/pb_home_address_street/_home_address_street.tsx +22 -34
- data/app/pb_kits/playbook/pb_home_address_street/city_emphasis.html.erb +12 -16
- data/app/pb_kits/playbook/pb_home_address_street/docs/_home_address_street_default.html.erb +1 -1
- data/app/pb_kits/playbook/pb_home_address_street/none_emphasis.html.erb +12 -16
- data/app/pb_kits/playbook/pb_home_address_street/street_emphasis.html.erb +12 -16
- data/app/pb_kits/playbook/pb_legend/_legend.tsx +1 -7
- data/app/pb_kits/playbook/pb_multiple_users/_multiple_users.scss +0 -10
- data/app/pb_kits/playbook/pb_multiple_users/_multiple_users.tsx +15 -66
- data/app/pb_kits/playbook/pb_multiple_users/docs/example.yml +0 -1
- data/app/pb_kits/playbook/pb_multiple_users/docs/index.js +0 -1
- data/app/pb_kits/playbook/pb_multiple_users/multiple_users.test.js +0 -25
- data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +10 -44
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.html.erb +4 -34
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.jsx +7 -16
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.test.jsx +0 -15
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +0 -3
- data/app/pb_kits/playbook/pb_typeahead/components/ClearIndicator.tsx +2 -13
- data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +1 -6
- data/app/pb_kits/playbook/pb_typeahead/components/ValueContainer.tsx +7 -34
- data/app/pb_kits/playbook/pb_typeahead/docs/example.yml +0 -2
- data/app/pb_kits/playbook/pb_typeahead/docs/index.js +1 -2
- data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +1 -6
- data/dist/chunks/_typeahead-kRdz5zPn.js +6 -0
- data/dist/chunks/vendor.js +2 -2
- data/dist/menu.yml +2 -3
- data/dist/playbook-rails-react-bindings.js +1 -1
- data/dist/playbook-rails.js +1 -1
- data/dist/playbook.css +1 -1
- data/lib/playbook/version.rb +2 -2
- metadata +3 -13
- data/app/pb_kits/playbook/pb_background/docs/_background_light.md +0 -1
- data/app/pb_kits/playbook/pb_draggable/docs/_draggable_multiple_containers_dropzone.jsx +0 -180
- data/app/pb_kits/playbook/pb_draggable/docs/_draggable_multiple_containers_dropzone.md +0 -22
- data/app/pb_kits/playbook/pb_multiple_users/docs/_multiple_users_with_tooltip.jsx +0 -42
- data/app/pb_kits/playbook/pb_multiple_users/docs/_multiple_users_with_tooltip.md +0 -1
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_input_display.html.erb +0 -30
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_input_display.jsx +0 -37
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_input_display.md +0 -3
- data/app/pb_kits/playbook/utilities/DEPRECATION_WARNINGS.md +0 -82
- data/app/pb_kits/playbook/utilities/deprecated.ts +0 -65
- data/dist/chunks/_typeahead-Mjy3POtc.js +0 -6
|
@@ -5,19 +5,17 @@ import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../uti
|
|
|
5
5
|
import { globalProps } from '../utilities/globalProps'
|
|
6
6
|
|
|
7
7
|
import Avatar from '../pb_avatar/_avatar'
|
|
8
|
-
import Tooltip from '../pb_tooltip/_tooltip'
|
|
9
8
|
|
|
10
9
|
type MultipleUsersProps = {
|
|
11
10
|
aria?: { [key: string]: string },
|
|
12
11
|
className?: string,
|
|
13
12
|
dark?: boolean,
|
|
14
13
|
data?: { [key: string]: string },
|
|
15
|
-
htmlOptions?: {
|
|
14
|
+
htmlOptions?: {[key: string]: string | number | boolean | (() => void)},
|
|
16
15
|
id?: string,
|
|
17
16
|
maxDisplayedUsers?: number,
|
|
18
17
|
reverse?: boolean,
|
|
19
18
|
size?: "md" | "lg" | "sm" | "xl" | "xs" | "xxs",
|
|
20
|
-
withTooltip?: boolean,
|
|
21
19
|
users: Array<{ [key: string]: string }>,
|
|
22
20
|
}
|
|
23
21
|
|
|
@@ -32,7 +30,6 @@ const MultipleUsers = (props: MultipleUsersProps): React.ReactElement => {
|
|
|
32
30
|
maxDisplayedUsers = 4,
|
|
33
31
|
reverse = false,
|
|
34
32
|
size = 'xs',
|
|
35
|
-
withTooltip = false,
|
|
36
33
|
users,
|
|
37
34
|
} = props
|
|
38
35
|
|
|
@@ -65,70 +62,22 @@ const MultipleUsers = (props: MultipleUsersProps): React.ReactElement => {
|
|
|
65
62
|
className={classes}
|
|
66
63
|
id={id}
|
|
67
64
|
>
|
|
68
|
-
{
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
{...avatarData}
|
|
79
|
-
className={"pb_multiple_users_item" + (withTooltip ? " user_tooltip" : "")}
|
|
80
|
-
dark={dark}
|
|
81
|
-
imageAlt={avatarData.name}
|
|
82
|
-
key={index}
|
|
83
|
-
size={size}
|
|
84
|
-
/>
|
|
85
|
-
</Tooltip>
|
|
86
|
-
))}
|
|
65
|
+
{usersToDisplay.map((avatarData, index) => (
|
|
66
|
+
<Avatar
|
|
67
|
+
{...avatarData}
|
|
68
|
+
className="pb_multiple_users_item"
|
|
69
|
+
dark={dark}
|
|
70
|
+
imageAlt={avatarData.name}
|
|
71
|
+
key={index}
|
|
72
|
+
size={size}
|
|
73
|
+
/>
|
|
74
|
+
))}
|
|
87
75
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
<div>
|
|
93
|
-
{
|
|
94
|
-
usersToDisplay.length < users.length ?
|
|
95
|
-
users.slice(displayCount).map((u, i) => (
|
|
96
|
-
<div key={i}>{u.tooltip}</div>
|
|
97
|
-
))
|
|
98
|
-
:
|
|
99
|
-
''
|
|
100
|
-
}
|
|
101
|
-
</div>
|
|
102
|
-
}
|
|
103
|
-
zIndex={10}
|
|
104
|
-
>
|
|
105
|
-
<div className={itemClasses + (withTooltip ? " user_count_tooltip" : "")}>
|
|
106
|
-
{`+${users.length - displayCount}`}
|
|
107
|
-
</div>
|
|
108
|
-
</Tooltip>
|
|
109
|
-
}
|
|
110
|
-
</>
|
|
111
|
-
:
|
|
112
|
-
<>
|
|
113
|
-
{usersToDisplay.map((avatarData, index) => (
|
|
114
|
-
<Avatar
|
|
115
|
-
{...avatarData}
|
|
116
|
-
className="pb_multiple_users_item"
|
|
117
|
-
dark={dark}
|
|
118
|
-
imageAlt={avatarData.name}
|
|
119
|
-
key={index}
|
|
120
|
-
size={size}
|
|
121
|
-
/>
|
|
122
|
-
))}
|
|
123
|
-
|
|
124
|
-
{users.length > maxDisplayedUsers &&
|
|
125
|
-
<div className={itemClasses}>
|
|
126
|
-
{`+${users.length - 3}`}
|
|
127
|
-
</div>
|
|
128
|
-
}
|
|
129
|
-
</>
|
|
76
|
+
{ users.length > maxDisplayedUsers &&
|
|
77
|
+
<div className={itemClasses}>
|
|
78
|
+
{`+${users.length - 3}`}
|
|
79
|
+
</div>
|
|
130
80
|
}
|
|
131
|
-
|
|
132
81
|
</div>
|
|
133
82
|
)
|
|
134
83
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
export { default as MultipleUsersDefault } from './_multiple_users_default.jsx'
|
|
2
2
|
export { default as MultipleUsersReverse } from './_multiple_users_reverse.jsx'
|
|
3
3
|
export { default as MultipleUsersSize } from './_multiple_users_size.jsx'
|
|
4
|
-
export { default as MultipleUsersWithTooltip } from './_multiple_users_with_tooltip.jsx'
|
|
@@ -49,29 +49,4 @@ test('should render aria-label', () => {
|
|
|
49
49
|
|
|
50
50
|
const kit = screen.getByTestId(testId)
|
|
51
51
|
expect(kit).toHaveAttribute('aria-label', testId)
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
test('should render withTooltip prop', () => {
|
|
55
|
-
render(
|
|
56
|
-
<MultipleUsers
|
|
57
|
-
data={{ testid: testId }}
|
|
58
|
-
users={[
|
|
59
|
-
{
|
|
60
|
-
name: 'Patrick Welch',
|
|
61
|
-
imageUrl: 'https://randomuser.me/api/portraits/men/9.jpg',
|
|
62
|
-
tooltip: "Patrick Welch - Online"
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
name: 'Lucille Sanchez',
|
|
66
|
-
imageUrl: 'https://randomuser.me/api/portraits/women/6.jpg',
|
|
67
|
-
tooltip: "Lucille Sanchez - Offline"
|
|
68
|
-
},
|
|
69
|
-
]}
|
|
70
|
-
withTooltip
|
|
71
|
-
/>
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
const kit = screen.getByTestId(testId)
|
|
75
|
-
const childWithTooltip = kit.querySelector('.pb_tooltip_kit')
|
|
76
|
-
expect(childWithTooltip).not.toBeNull()
|
|
77
52
|
})
|
|
@@ -110,25 +110,13 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
110
110
|
const inputRef = useRef<HTMLInputElement | null>(null)
|
|
111
111
|
const itiRef = useRef<any>(null);
|
|
112
112
|
const wrapperRef = useRef<HTMLDivElement | null>(null);
|
|
113
|
-
const hasBlurredRef = useRef<boolean>(false);
|
|
114
|
-
const formSubmittedRef = useRef<boolean>(false);
|
|
115
113
|
const [inputValue, setInputValue] = useState(value)
|
|
116
114
|
const [error, setError] = useState(props.error || "")
|
|
117
115
|
const [dropDownIsOpen, setDropDownIsOpen] = useState(false)
|
|
118
116
|
const [selectedData, setSelectedData] = useState()
|
|
119
117
|
const [hasTyped, setHasTyped] = useState(false)
|
|
120
|
-
const [hasBlurred, setHasBlurred] = useState(false)
|
|
121
118
|
const [formSubmitted, setFormSubmitted] = useState(false)
|
|
122
119
|
const [hasStartedValidating, setHasStartedValidating] = useState(false)
|
|
123
|
-
|
|
124
|
-
// Keep refs in sync with state for use in event listeners
|
|
125
|
-
useEffect(() => {
|
|
126
|
-
hasBlurredRef.current = hasBlurred
|
|
127
|
-
}, [hasBlurred])
|
|
128
|
-
|
|
129
|
-
useEffect(() => {
|
|
130
|
-
formSubmittedRef.current = formSubmitted
|
|
131
|
-
}, [formSubmitted])
|
|
132
120
|
|
|
133
121
|
// Only sync initial error from props, not continuous updates
|
|
134
122
|
// Once validation starts, internal validation takes over
|
|
@@ -155,8 +143,8 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
155
143
|
}
|
|
156
144
|
|
|
157
145
|
// Determine which error to display
|
|
158
|
-
// Show internal errors
|
|
159
|
-
const shouldShowInternalError = (
|
|
146
|
+
// Show internal errors on blur (hasTyped) or on form submission (formSubmitted)
|
|
147
|
+
const shouldShowInternalError = (hasTyped || formSubmitted) && required && error
|
|
160
148
|
const displayError = shouldShowInternalError ? error : ""
|
|
161
149
|
|
|
162
150
|
useEffect(() => {
|
|
@@ -271,9 +259,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
271
259
|
return
|
|
272
260
|
}
|
|
273
261
|
|
|
274
|
-
|
|
275
|
-
// Use refs here since state updates are async and we need current values
|
|
276
|
-
if (!hasBlurredRef.current && !formSubmittedRef.current) return
|
|
262
|
+
if (!hasTyped && !error) return
|
|
277
263
|
|
|
278
264
|
// Run validation checks
|
|
279
265
|
if (itiRef.current) isValid(itiRef.current.isValidNumber())
|
|
@@ -294,7 +280,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
294
280
|
if (phoneNumberContainer && phoneNumberContainer === wrapperRef.current) {
|
|
295
281
|
const invalidInputName = target.name || target.getAttribute('name')
|
|
296
282
|
if (invalidInputName === name) {
|
|
297
|
-
formSubmittedRef.current = true
|
|
298
283
|
setFormSubmitted(true)
|
|
299
284
|
// Trigger validation when form is submitted
|
|
300
285
|
validateErrors()
|
|
@@ -320,9 +305,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
320
305
|
setInputValue("")
|
|
321
306
|
setError("")
|
|
322
307
|
setHasTyped(false)
|
|
323
|
-
hasBlurredRef.current = false
|
|
324
|
-
setHasBlurred(false)
|
|
325
|
-
formSubmittedRef.current = false
|
|
326
308
|
setFormSubmitted(false)
|
|
327
309
|
setHasStartedValidating(false)
|
|
328
310
|
// Only clear validation state if field was required
|
|
@@ -340,7 +322,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
340
322
|
|
|
341
323
|
if (required && isEmpty) {
|
|
342
324
|
setError('Missing phone number')
|
|
343
|
-
formSubmittedRef.current = true
|
|
344
325
|
setFormSubmitted(true)
|
|
345
326
|
return 'Missing phone number'
|
|
346
327
|
}
|
|
@@ -397,7 +378,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
397
378
|
|
|
398
379
|
// Set the error state so the validation attribute gets added
|
|
399
380
|
setError(errorMessage)
|
|
400
|
-
formSubmittedRef.current = true
|
|
401
381
|
setFormSubmitted(true)
|
|
402
382
|
setHasTyped(true)
|
|
403
383
|
|
|
@@ -421,7 +401,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
421
401
|
|
|
422
402
|
// Reset form submitted state when user types
|
|
423
403
|
if (formSubmitted) {
|
|
424
|
-
formSubmittedRef.current = false
|
|
425
404
|
setFormSubmitted(false)
|
|
426
405
|
}
|
|
427
406
|
|
|
@@ -437,15 +416,11 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
437
416
|
|
|
438
417
|
setSelectedData(phoneNumberData)
|
|
439
418
|
onChange(phoneNumberData)
|
|
440
|
-
|
|
441
|
-
// Don't call isValid callback on change - only on blur or form submission
|
|
442
|
-
// This prevents triggering validation while typing
|
|
443
|
-
// Use refs to get current values in case this is called from event listener
|
|
444
|
-
if (hasBlurredRef.current || formSubmittedRef.current) {
|
|
445
|
-
isValid(itiRef.current.isValidNumber())
|
|
446
|
-
}
|
|
419
|
+
isValid(itiRef.current.isValidNumber())
|
|
447
420
|
|
|
448
|
-
//
|
|
421
|
+
// Trigger validation after onChange for React Hook Form
|
|
422
|
+
// This ensures validation state is up-to-date
|
|
423
|
+
setTimeout(() => validateErrors(), 0)
|
|
449
424
|
}
|
|
450
425
|
|
|
451
426
|
// Separating Concerns as React Docs Recommend
|
|
@@ -507,12 +482,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
507
482
|
|
|
508
483
|
setSelectedData(phoneNumberData)
|
|
509
484
|
onChange(phoneNumberData)
|
|
510
|
-
|
|
511
|
-
// Don't call isValid callback on change - only on blur or form submission
|
|
512
|
-
// Use refs to check current blur state in the event listener (closure issue)
|
|
513
|
-
if (hasBlurredRef.current || formSubmittedRef.current) {
|
|
514
|
-
isValid(telInputInit.isValidNumber())
|
|
515
|
-
}
|
|
485
|
+
isValid(telInputInit.isValidNumber())
|
|
516
486
|
})
|
|
517
487
|
}
|
|
518
488
|
}
|
|
@@ -522,16 +492,12 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
522
492
|
dark,
|
|
523
493
|
"data-phone-number": JSON.stringify(selectedData),
|
|
524
494
|
disabled,
|
|
525
|
-
error:
|
|
495
|
+
error: hasTyped ? error : props.error || displayError,
|
|
526
496
|
type: 'tel',
|
|
527
497
|
id,
|
|
528
498
|
label,
|
|
529
499
|
name,
|
|
530
|
-
onBlur:
|
|
531
|
-
hasBlurredRef.current = true
|
|
532
|
-
setHasBlurred(true)
|
|
533
|
-
validateErrors()
|
|
534
|
-
},
|
|
500
|
+
onBlur: validateErrors,
|
|
535
501
|
onChange: formatAsYouType ? undefined : handleOnChange,
|
|
536
502
|
value: inputValue
|
|
537
503
|
}
|
data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.html.erb
CHANGED
|
@@ -1,42 +1,12 @@
|
|
|
1
1
|
<form id="example-form-validation" action="" method="get">
|
|
2
|
-
<%= pb_rails("phone_number_input", props: {
|
|
3
|
-
id: "validation",
|
|
4
|
-
initial_country: "af",
|
|
5
|
-
value: "",
|
|
6
|
-
required: true
|
|
7
|
-
}) %>
|
|
2
|
+
<%= pb_rails("phone_number_input", props: { error: "Missing phone number", id: "validation", initial_country: "af", value: "", required: true }) %>
|
|
8
3
|
<%= pb_rails("button", props: {html_type: "submit", text: "Save Phone Number"}) %>
|
|
9
4
|
</form>
|
|
10
5
|
|
|
11
6
|
<%= javascript_tag do %>
|
|
12
7
|
document.addEventListener('DOMContentLoaded', function () {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
function waitForComponent() {
|
|
17
|
-
const phoneInput = form.querySelector('#validation');
|
|
18
|
-
|
|
19
|
-
if (!phoneInput) {
|
|
20
|
-
setTimeout(waitForComponent, 100);
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Wait for intl-tel-input to initialize, then focus and blur to trigger validation
|
|
25
|
-
setTimeout(function() {
|
|
26
|
-
phoneInput.focus({ preventScroll: true });
|
|
27
|
-
setTimeout(function() {
|
|
28
|
-
phoneInput.blur();
|
|
29
|
-
}, 100);
|
|
30
|
-
}, 500);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
waitForComponent();
|
|
34
|
-
|
|
35
|
-
// Prevent form submission if there are validation errors
|
|
36
|
-
form.addEventListener('submit', function (e) {
|
|
37
|
-
if (e.target.querySelectorAll('[error]:not([error=""])').length > 0) {
|
|
38
|
-
e.preventDefault();
|
|
39
|
-
}
|
|
40
|
-
});
|
|
8
|
+
document.querySelector('#example-form-validation').addEventListener('submit', function (e) {
|
|
9
|
+
if (e.target.querySelectorAll('[error]:not([error=""])').length > 0) e.preventDefault();
|
|
10
|
+
})
|
|
41
11
|
})
|
|
42
12
|
<% end %>
|
|
@@ -10,19 +10,8 @@ const PhoneNumberInputValidation = (props) => {
|
|
|
10
10
|
const [showFormErrors, setShowFormErrors] = useState(false);
|
|
11
11
|
const [phoneNumber, setPhoneNumber] = useState("");
|
|
12
12
|
const [countryCode, setCountryCode] = useState("af");
|
|
13
|
-
const [isValid, setIsValid] = useState(false);
|
|
14
|
-
const [hasInteracted, setHasInteracted] = useState(false);
|
|
15
|
-
|
|
16
|
-
// Start with initial error - will be cleared on blur if valid
|
|
17
|
-
const initialError = (
|
|
18
|
-
<>
|
|
19
|
-
<Icon icon="warning" /> Missing phone number.
|
|
20
|
-
</>
|
|
21
|
-
);
|
|
22
13
|
|
|
23
14
|
const handleOnValidate = (valid) => {
|
|
24
|
-
setIsValid(valid);
|
|
25
|
-
setHasInteracted(true);
|
|
26
15
|
setFormErrors(
|
|
27
16
|
valid ? "" : "Please correct the fields below and try again."
|
|
28
17
|
);
|
|
@@ -34,16 +23,18 @@ const PhoneNumberInputValidation = (props) => {
|
|
|
34
23
|
};
|
|
35
24
|
|
|
36
25
|
const handleOnSubmit = (e) => {
|
|
37
|
-
if (
|
|
26
|
+
if (showFormErrors) e.preventDefault()
|
|
38
27
|
}
|
|
39
28
|
|
|
40
29
|
useEffect(() => {
|
|
41
30
|
setShowFormErrors(formErrors.length > 0);
|
|
42
31
|
}, [formErrors]);
|
|
43
32
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
33
|
+
const error = (
|
|
34
|
+
<>
|
|
35
|
+
<Icon icon="warning" /> Missing phone number.
|
|
36
|
+
</>
|
|
37
|
+
)
|
|
47
38
|
|
|
48
39
|
return (
|
|
49
40
|
<form
|
|
@@ -59,7 +50,7 @@ const PhoneNumberInputValidation = (props) => {
|
|
|
59
50
|
/>
|
|
60
51
|
)}
|
|
61
52
|
<PhoneNumberInput
|
|
62
|
-
error={
|
|
53
|
+
error={error}
|
|
63
54
|
id="validation"
|
|
64
55
|
initialCountry={countryCode}
|
|
65
56
|
onChange={handleOnChange}
|
|
@@ -276,18 +276,3 @@ test('multi-value badges have tabIndex and focus class when focused', () => {
|
|
|
276
276
|
})
|
|
277
277
|
})
|
|
278
278
|
|
|
279
|
-
test('input display none shows number of selected items', () => {
|
|
280
|
-
render(
|
|
281
|
-
<Typeahead
|
|
282
|
-
data={{ testid: 'input-display-none-test' }}
|
|
283
|
-
defaultValue={[options[0], options[1]]}
|
|
284
|
-
inputDisplay="none"
|
|
285
|
-
isMulti
|
|
286
|
-
options={options}
|
|
287
|
-
/>
|
|
288
|
-
)
|
|
289
|
-
|
|
290
|
-
const kit = screen.getByTestId('input-display-none-test')
|
|
291
|
-
const inputDisplayDiv = kit.querySelector(".pb_typeahead_selection_count")
|
|
292
|
-
expect(inputDisplayDiv).toHaveTextContent("2 items selected")
|
|
293
|
-
})
|
|
@@ -43,7 +43,6 @@ type TypeaheadProps = {
|
|
|
43
43
|
error?: string,
|
|
44
44
|
htmlOptions?: {[key: string]: string | number | boolean | (() => void)},
|
|
45
45
|
id?: string,
|
|
46
|
-
inputDisplay?: "pills" | "none",
|
|
47
46
|
label?: string,
|
|
48
47
|
loadOptions?: string | Noop,
|
|
49
48
|
getOptionLabel?: string | (() => string),
|
|
@@ -90,7 +89,6 @@ const Typeahead = forwardRef<HTMLInputElement, TypeaheadProps>(({
|
|
|
90
89
|
getOptionValue,
|
|
91
90
|
htmlOptions = {},
|
|
92
91
|
id,
|
|
93
|
-
inputDisplay = "pills",
|
|
94
92
|
name,
|
|
95
93
|
loadOptions = noop,
|
|
96
94
|
marginBottom = "sm",
|
|
@@ -240,7 +238,6 @@ const Typeahead = forwardRef<HTMLInputElement, TypeaheadProps>(({
|
|
|
240
238
|
getOptionValue: isString(getOptionValue) ? get(window, getOptionValue) : getOptionValue,
|
|
241
239
|
defaultOptions: true,
|
|
242
240
|
id: id || uniqueId(),
|
|
243
|
-
inputDisplay: inputDisplay,
|
|
244
241
|
inline: false,
|
|
245
242
|
isClearable: true,
|
|
246
243
|
isSearchable: true,
|
|
@@ -7,29 +7,18 @@ type ClearContainerProps = {
|
|
|
7
7
|
id: string,
|
|
8
8
|
},
|
|
9
9
|
clearValue: () => void,
|
|
10
|
-
innerProps?: any,
|
|
11
10
|
}
|
|
12
11
|
|
|
13
|
-
const ClearContainer = (props: ClearContainerProps
|
|
14
|
-
const { selectProps, clearValue
|
|
12
|
+
const ClearContainer = (props: ClearContainerProps): React.ReactElement => {
|
|
13
|
+
const { selectProps, clearValue } = props
|
|
15
14
|
useEffect(() => {
|
|
16
15
|
document.addEventListener(`pb-typeahead-kit-${selectProps.id}:clear`, clearValue)
|
|
17
16
|
}, [clearValue, selectProps.id])
|
|
18
17
|
|
|
19
|
-
// To stop this from bubbling up when inside a dialog or other modal
|
|
20
|
-
const handleMouseDown = (event: React.MouseEvent) => {
|
|
21
|
-
event.stopPropagation()
|
|
22
|
-
innerProps?.onMouseDown?.(event)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
18
|
return (
|
|
26
19
|
<components.ClearIndicator
|
|
27
20
|
className="clear_indicator"
|
|
28
21
|
{...props}
|
|
29
|
-
innerProps={{
|
|
30
|
-
...innerProps,
|
|
31
|
-
onMouseDown: handleMouseDown,
|
|
32
|
-
}}
|
|
33
22
|
/>
|
|
34
23
|
)
|
|
35
24
|
}
|
|
@@ -19,12 +19,7 @@ type Props = {
|
|
|
19
19
|
const MultiValue = (props: Props) => {
|
|
20
20
|
const { removeProps, isFocused } = props
|
|
21
21
|
const { imageUrl, label } = props.data
|
|
22
|
-
const { dark, multiKit, pillColor, truncate, wrapped
|
|
23
|
-
|
|
24
|
-
// If inputDisplay is "none", don't render the pill/badge, just return null (the count handled in ValueContainer file)
|
|
25
|
-
if (inputDisplay === 'none') {
|
|
26
|
-
return null
|
|
27
|
-
}
|
|
22
|
+
const { dark, multiKit, pillColor, truncate, wrapped } = props.selectProps
|
|
28
23
|
|
|
29
24
|
const formPillProps = {
|
|
30
25
|
marginRight: 'xs',
|
|
@@ -1,42 +1,15 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { components } from 'react-select'
|
|
3
|
-
import Body from '../../pb_body/_body'
|
|
4
3
|
|
|
5
4
|
type ValueContainerProps = {
|
|
6
|
-
children: React.ReactNode
|
|
7
|
-
selectProps?: Record<string, unknown>,
|
|
8
|
-
hasValue?: boolean,
|
|
5
|
+
children: React.ReactNode,
|
|
9
6
|
}
|
|
10
7
|
|
|
11
|
-
const ValueContainer = (props: ValueContainerProps
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (inputDisplay === 'none' && hasValue && value) {
|
|
18
|
-
const selectedCount = Array.isArray(value) ? value.length : 0
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<components.ValueContainer
|
|
22
|
-
className="text_input_value_container"
|
|
23
|
-
{...props}
|
|
24
|
-
>
|
|
25
|
-
<Body
|
|
26
|
-
className="pb_typeahead_selection_count"
|
|
27
|
-
text={`${selectedCount} item${selectedCount !== 1 ? 's' : ''} selected`}
|
|
28
|
-
/>
|
|
29
|
-
{children}
|
|
30
|
-
</components.ValueContainer>
|
|
31
|
-
)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return (
|
|
35
|
-
<components.ValueContainer
|
|
36
|
-
className="text_input_value_container"
|
|
37
|
-
{...props}
|
|
38
|
-
/>
|
|
39
|
-
)
|
|
40
|
-
}
|
|
8
|
+
const ValueContainer = (props: ValueContainerProps): React.ReactElement => (
|
|
9
|
+
<components.ValueContainer
|
|
10
|
+
className="text_input_value_container"
|
|
11
|
+
{...props}
|
|
12
|
+
/>
|
|
13
|
+
)
|
|
41
14
|
|
|
42
15
|
export default ValueContainer
|
|
@@ -5,7 +5,6 @@ examples:
|
|
|
5
5
|
- typeahead_default_options: With Default Options
|
|
6
6
|
- typeahead_with_context: With Context
|
|
7
7
|
- typeahead_with_pills: With Pills
|
|
8
|
-
- typeahead_input_display: Input Display
|
|
9
8
|
- typeahead_without_pills: Without Pills (Single Select)
|
|
10
9
|
- typeahead_with_pills_async: With Pills (Async Data)
|
|
11
10
|
- typeahead_with_pills_async_users: With Pills (Async Data w/ Users)
|
|
@@ -27,7 +26,6 @@ examples:
|
|
|
27
26
|
- typeahead_react_hook: React Hook
|
|
28
27
|
- typeahead_with_highlight: With Highlight
|
|
29
28
|
- typeahead_with_pills: With Pills
|
|
30
|
-
- typeahead_input_display: Input Display
|
|
31
29
|
- typeahead_with_pills_async: With Pills (Async Data)
|
|
32
30
|
- typeahead_with_pills_async_users: With Pills (Async Data w/ Users)
|
|
33
31
|
- typeahead_with_pills_async_custom_options: With Pills (Async Data w/ Custom Options)
|
|
@@ -17,5 +17,4 @@ export { default as TypeaheadReactHook } from './_typeahead_react_hook.jsx'
|
|
|
17
17
|
export { default as TypeaheadDisabled } from './_typeahead_disabled.jsx'
|
|
18
18
|
export { default as TypeaheadPreserveInput } from './_typeahead_preserve_input.jsx'
|
|
19
19
|
export { default as TypeaheadDefaultValue } from './_typeahead_default_value.jsx'
|
|
20
|
-
export { default as TypeaheadCustomOptions } from './_typeahead_custom_options.jsx'
|
|
21
|
-
export { default as TypeaheadInputDisplay } from './_typeahead_input_display.jsx'
|
|
20
|
+
export { default as TypeaheadCustomOptions } from './_typeahead_custom_options.jsx'
|
|
@@ -25,10 +25,6 @@ module Playbook
|
|
|
25
25
|
prop :is_multi, type: Playbook::Props::Boolean,
|
|
26
26
|
default: true
|
|
27
27
|
|
|
28
|
-
prop :input_display, type: Playbook::Props::Enum,
|
|
29
|
-
values: %w[none pills],
|
|
30
|
-
default: "pills"
|
|
31
|
-
|
|
32
28
|
prop :pills, type: Playbook::Props::Boolean,
|
|
33
29
|
default: false
|
|
34
30
|
|
|
@@ -82,7 +78,7 @@ module Playbook
|
|
|
82
78
|
end
|
|
83
79
|
|
|
84
80
|
def is_react?
|
|
85
|
-
pills || !is_multi || wrapped
|
|
81
|
+
pills || !is_multi || wrapped
|
|
86
82
|
end
|
|
87
83
|
|
|
88
84
|
def typeahead_react_options
|
|
@@ -95,7 +91,6 @@ module Playbook
|
|
|
95
91
|
id: id,
|
|
96
92
|
inline: inline,
|
|
97
93
|
isMulti: is_multi,
|
|
98
|
-
inputDisplay: input_display,
|
|
99
94
|
label: label,
|
|
100
95
|
marginBottom: margin_bottom,
|
|
101
96
|
multiKit: multi_kit,
|