playbook_ui 16.1.0.pre.alpha.play2712navkitadddisabledstatecollapsible13821 → 16.1.0.pre.alpha.play2771passphraseaccessibility14033
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_date_picker/_date_picker.tsx +14 -5
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_default.md +1 -0
- data/app/pb_kits/playbook/pb_dialog/_dialog.scss +8 -6
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +6 -0
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +37 -2
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_blank_selection_rails.md +3 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_blank_selection_react.md +3 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.html.erb +52 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.jsx +72 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.md +5 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height.jsx +33 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height_rails.html.erb +20 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height_rails.md +8 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height_react.md +8 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.html.erb +9 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.jsx +33 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.md +3 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +6 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/index.js +4 -1
- data/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +2 -2
- data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +6 -0
- data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +94 -0
- data/app/pb_kits/playbook/pb_dropdown/dropdown_container.rb +5 -1
- data/app/pb_kits/playbook/pb_dropdown/index.js +59 -4
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownContainer.tsx +3 -0
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +2 -1
- data/app/pb_kits/playbook/pb_filter/Filter/SortMenu.tsx +1 -1
- data/app/pb_kits/playbook/pb_filter/docs/_filter_default.html.erb +2 -2
- data/app/pb_kits/playbook/pb_filter/docs/_filter_default.jsx +16 -9
- data/app/pb_kits/playbook/pb_filter/filter.rb +2 -2
- data/app/pb_kits/playbook/pb_form/docs/_form_with_required_indicator.html.erb +2 -0
- data/app/pb_kits/playbook/pb_form/pb_form_validation.js +9 -2
- data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.html.erb +5 -5
- data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.jsx +4 -4
- data/app/pb_kits/playbook/pb_form_pill/form_pill.rb +4 -0
- data/app/pb_kits/playbook/pb_nav/_item.tsx +1 -5
- data/app/pb_kits/playbook/pb_nav/_nav.scss +1 -27
- data/app/pb_kits/playbook/pb_nav/docs/example.yml +0 -2
- data/app/pb_kits/playbook/pb_nav/docs/index.js +1 -2
- data/app/pb_kits/playbook/pb_nav/item.rb +2 -7
- data/app/pb_kits/playbook/pb_passphrase/_passphrase.tsx +40 -7
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_meter_settings.jsx +1 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_required_indicator.html.erb +7 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_required_indicator.jsx +24 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_required_indicator.md +3 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_passphrase/passphrase.rb +2 -0
- data/app/pb_kits/playbook/pb_passphrase/passphrase.test.jsx +30 -1
- data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +3 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_required_indicator.html.erb +5 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_required_indicator.jsx +14 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_required_indicator.md +3 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.rb +3 -0
- data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.test.js +34 -3
- data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.tsx +4 -3
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/example.yml +0 -1
- data/app/pb_kits/playbook/pb_textarea/_textarea.tsx +10 -0
- data/app/pb_kits/playbook/pb_textarea/docs/_textarea_default.html.erb +3 -3
- data/app/pb_kits/playbook/pb_textarea/docs/_textarea_default.jsx +3 -0
- data/app/pb_kits/playbook/pb_textarea/docs/_textarea_default.md +1 -0
- data/app/pb_kits/playbook/pb_textarea/textarea.html.erb +25 -9
- data/app/pb_kits/playbook/pb_textarea/textarea.rb +7 -1
- data/app/pb_kits/playbook/pb_time_picker/_time_picker.tsx +97 -11
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_on_handler.jsx +5 -2
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_required_indicator.html.erb +6 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_required_indicator.jsx +16 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_required_indicator.md +3 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_time_picker/time_picker.rb +3 -0
- data/app/pb_kits/playbook/pb_time_picker/time_picker.test.jsx +47 -1
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.test.jsx +24 -1
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +2 -1
- data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +4 -1
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_truncated_text.html.erb +1 -1
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_truncated_text.jsx +1 -1
- data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +4 -0
- data/dist/chunks/_typeahead-D-8xZ__X.js +1 -0
- data/dist/chunks/vendor.js +3 -3
- data/dist/menu.yml +1 -1
- 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/align_content.rb +13 -3
- data/lib/playbook/align_items.rb +13 -3
- data/lib/playbook/align_self.rb +13 -3
- data/lib/playbook/display.rb +5 -0
- data/lib/playbook/flex.rb +13 -3
- data/lib/playbook/flex_direction.rb +13 -3
- data/lib/playbook/flex_grow.rb +13 -3
- data/lib/playbook/flex_shrink.rb +13 -3
- data/lib/playbook/flex_wrap.rb +13 -3
- data/lib/playbook/forms/builder/phone_number_field.rb +9 -0
- data/lib/playbook/justify_content.rb +13 -3
- data/lib/playbook/justify_self.rb +13 -3
- data/lib/playbook/order.rb +13 -3
- data/lib/playbook/spacing.rb +39 -9
- data/lib/playbook/text_align.rb +13 -3
- data/lib/playbook/truncate.rb +1 -1
- data/lib/playbook/version.rb +1 -1
- data/lib/playbook/vertical_align.rb +13 -3
- data/lib/playbook/z_index.rb +5 -0
- metadata +26 -6
- data/app/pb_kits/playbook/pb_nav/docs/_collapsible_nav_disabled_item.html.erb +0 -24
- data/app/pb_kits/playbook/pb_nav/docs/_collapsible_nav_disabled_item.jsx +0 -89
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_rows.jsx +0 -67
- data/dist/chunks/_typeahead-BvrkfO0L.js +0 -1
|
@@ -5,12 +5,12 @@ import { globalProps, GlobalProps } from '../utilities/globalProps'
|
|
|
5
5
|
import Caption from '../pb_caption/_caption'
|
|
6
6
|
import SelectableCard from '../pb_selectable_card/_selectable_card'
|
|
7
7
|
import TextInput from '../pb_text_input/_text_input'
|
|
8
|
+
import colors from '../tokens/exports/_colors.module.scss'
|
|
8
9
|
|
|
9
10
|
import {
|
|
10
11
|
parseTime,
|
|
11
12
|
parseTimeToMinutes,
|
|
12
13
|
isTimeInRange as isTimeInRangeHelper,
|
|
13
|
-
isHourDisabled as isHourDisabledHelper,
|
|
14
14
|
isAnyAMTimeValid as isAnyAMTimeValidHelper,
|
|
15
15
|
isAnyPMTimeValid as isAnyPMTimeValidHelper,
|
|
16
16
|
getDisplayTime,
|
|
@@ -48,6 +48,7 @@ type TimePickerProps = {
|
|
|
48
48
|
onChange?: (time: string) => void,
|
|
49
49
|
onClose?: (time: string) => void,
|
|
50
50
|
required?: boolean,
|
|
51
|
+
requiredIndicator?: boolean,
|
|
51
52
|
showTimezone?: boolean,
|
|
52
53
|
timeFormat?: TimeFormat,
|
|
53
54
|
validationMessage?: string,
|
|
@@ -72,6 +73,7 @@ const TimePicker = (props: TimePickerProps): JSX.Element => {
|
|
|
72
73
|
onChange,
|
|
73
74
|
onClose,
|
|
74
75
|
required = false,
|
|
76
|
+
requiredIndicator = false,
|
|
75
77
|
showTimezone = false,
|
|
76
78
|
timeFormat = 'AMPM',
|
|
77
79
|
validationMessage,
|
|
@@ -114,10 +116,6 @@ const TimePicker = (props: TimePickerProps): JSX.Element => {
|
|
|
114
116
|
return isTimeInRangeHelper(h, m, mer, timeFormat, minTimeMinutes, maxTimeMinutes)
|
|
115
117
|
}
|
|
116
118
|
|
|
117
|
-
const isHourDisabled = (h: number, mer?: 'AM' | 'PM'): boolean => {
|
|
118
|
-
return isHourDisabledHelper(h, mer, timeFormat, minTimeMinutes, maxTimeMinutes)
|
|
119
|
-
}
|
|
120
|
-
|
|
121
119
|
const isCurrentTimeValid = (h: number, m: number, mer: 'AM' | 'PM'): boolean => {
|
|
122
120
|
return isTimeInRange(h, m, mer)
|
|
123
121
|
}
|
|
@@ -187,6 +185,28 @@ const TimePicker = (props: TimePickerProps): JSX.Element => {
|
|
|
187
185
|
const [showHourDropdown, setShowHourDropdown] = useState(false)
|
|
188
186
|
const [showMinuteDropdown, setShowMinuteDropdown] = useState(false)
|
|
189
187
|
|
|
188
|
+
// Clicking the clock add-on opens the dropdown
|
|
189
|
+
useEffect(() => {
|
|
190
|
+
if (disabled) return
|
|
191
|
+
|
|
192
|
+
const addOnCard = document.querySelector(`#${uniqueId}-input`)?.closest('.text_input_wrapper_add_on')?.querySelector('.add-on-card') as HTMLElement
|
|
193
|
+
|
|
194
|
+
if (addOnCard) {
|
|
195
|
+
const handleAddOnClick = (e: Event) => {
|
|
196
|
+
e.preventDefault()
|
|
197
|
+
e.stopPropagation()
|
|
198
|
+
setShowDropdown(true)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
addOnCard.addEventListener('click', handleAddOnClick)
|
|
202
|
+
addOnCard.style.cursor = 'pointer'
|
|
203
|
+
|
|
204
|
+
return () => {
|
|
205
|
+
addOnCard.removeEventListener('click', handleAddOnClick)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}, [uniqueId, disabled, setShowDropdown])
|
|
209
|
+
|
|
190
210
|
// Input dropdown scrolling
|
|
191
211
|
const scrollDropdownToSelected = (dropdownRef: React.RefObject<HTMLDivElement>) => {
|
|
192
212
|
if (dropdownRef.current) {
|
|
@@ -369,6 +389,10 @@ const TimePicker = (props: TimePickerProps): JSX.Element => {
|
|
|
369
389
|
}
|
|
370
390
|
}
|
|
371
391
|
|
|
392
|
+
const handleHourFocus = (e: React.FocusEvent<HTMLInputElement>) => {
|
|
393
|
+
e.target.select()
|
|
394
|
+
}
|
|
395
|
+
|
|
372
396
|
const handleHourBlur = () => {
|
|
373
397
|
const result = normalizeHourOnBlur(hourInputValue, hour, timeFormat)
|
|
374
398
|
setHour(result.hour)
|
|
@@ -393,6 +417,10 @@ const TimePicker = (props: TimePickerProps): JSX.Element => {
|
|
|
393
417
|
}
|
|
394
418
|
}
|
|
395
419
|
|
|
420
|
+
const handleMinuteFocus = (e: React.FocusEvent<HTMLInputElement>) => {
|
|
421
|
+
e.target.select()
|
|
422
|
+
}
|
|
423
|
+
|
|
396
424
|
const handleMinuteBlur = () => {
|
|
397
425
|
const result = normalizeMinuteOnBlur(minuteInputValue, minute)
|
|
398
426
|
setMinute(result.minute)
|
|
@@ -482,6 +510,30 @@ const TimePicker = (props: TimePickerProps): JSX.Element => {
|
|
|
482
510
|
e.preventDefault()
|
|
483
511
|
setShowHourDropdown(false)
|
|
484
512
|
closeDropdown()
|
|
513
|
+
} else if (e.key === 'ArrowDown') {
|
|
514
|
+
// ArrowDown increases value (like scrolling down a list)
|
|
515
|
+
e.preventDefault()
|
|
516
|
+
const { maxHour, minHour } = getHourConstraints(timeFormat)
|
|
517
|
+
const newHour = hour >= maxHour ? minHour : hour + 1
|
|
518
|
+
setHour(newHour)
|
|
519
|
+
setHourInputValue(timeFormat === '24hour' ? newHour.toString().padStart(2, '0') : newHour.toString())
|
|
520
|
+
setHasSelectedTime(true)
|
|
521
|
+
const timeString = get24HourTime(newHour, minute, meridiem, timeFormat)
|
|
522
|
+
if (onChange) {
|
|
523
|
+
onChange(timeString)
|
|
524
|
+
}
|
|
525
|
+
} else if (e.key === 'ArrowUp') {
|
|
526
|
+
// ArrowUp decreases value (like scrolling up a list)
|
|
527
|
+
e.preventDefault()
|
|
528
|
+
const { maxHour, minHour } = getHourConstraints(timeFormat)
|
|
529
|
+
const newHour = hour <= minHour ? maxHour : hour - 1
|
|
530
|
+
setHour(newHour)
|
|
531
|
+
setHourInputValue(timeFormat === '24hour' ? newHour.toString().padStart(2, '0') : newHour.toString())
|
|
532
|
+
setHasSelectedTime(true)
|
|
533
|
+
const timeString = get24HourTime(newHour, minute, meridiem, timeFormat)
|
|
534
|
+
if (onChange) {
|
|
535
|
+
onChange(timeString)
|
|
536
|
+
}
|
|
485
537
|
}
|
|
486
538
|
}
|
|
487
539
|
|
|
@@ -513,6 +565,28 @@ const TimePicker = (props: TimePickerProps): JSX.Element => {
|
|
|
513
565
|
e.preventDefault()
|
|
514
566
|
setShowMinuteDropdown(false)
|
|
515
567
|
closeDropdown()
|
|
568
|
+
} else if (e.key === 'ArrowDown') {
|
|
569
|
+
// ArrowDown increases value (like scrolling down a list)
|
|
570
|
+
e.preventDefault()
|
|
571
|
+
const newMinute = minute >= 59 ? 0 : minute + 1
|
|
572
|
+
setMinute(newMinute)
|
|
573
|
+
setMinuteInputValue(newMinute.toString().padStart(2, '0'))
|
|
574
|
+
setHasSelectedTime(true)
|
|
575
|
+
const timeString = get24HourTime(hour, newMinute, meridiem, timeFormat)
|
|
576
|
+
if (onChange) {
|
|
577
|
+
onChange(timeString)
|
|
578
|
+
}
|
|
579
|
+
} else if (e.key === 'ArrowUp') {
|
|
580
|
+
// ArrowUp decreases value (like scrolling up a list)
|
|
581
|
+
e.preventDefault()
|
|
582
|
+
const newMinute = minute <= 0 ? 59 : minute - 1
|
|
583
|
+
setMinute(newMinute)
|
|
584
|
+
setMinuteInputValue(newMinute.toString().padStart(2, '0'))
|
|
585
|
+
setHasSelectedTime(true)
|
|
586
|
+
const timeString = get24HourTime(hour, newMinute, meridiem, timeFormat)
|
|
587
|
+
if (onChange) {
|
|
588
|
+
onChange(timeString)
|
|
589
|
+
}
|
|
516
590
|
}
|
|
517
591
|
}
|
|
518
592
|
|
|
@@ -625,12 +699,22 @@ const TimePicker = (props: TimePickerProps): JSX.Element => {
|
|
|
625
699
|
>
|
|
626
700
|
{label && (
|
|
627
701
|
<label htmlFor={`${uniqueId}-input`}>
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
702
|
+
{requiredIndicator ? (
|
|
703
|
+
<Caption
|
|
704
|
+
className="pb_time_picker_kit_label"
|
|
705
|
+
marginBottom="xs"
|
|
706
|
+
size="md"
|
|
707
|
+
>
|
|
708
|
+
{label} <span style={{ color: `${colors.error}` }}>{'*'}</span>
|
|
709
|
+
</Caption>
|
|
710
|
+
) : (
|
|
711
|
+
<Caption
|
|
712
|
+
className="pb_time_picker_kit_label"
|
|
713
|
+
marginBottom="xs"
|
|
714
|
+
size="md"
|
|
715
|
+
text={label}
|
|
716
|
+
/>
|
|
717
|
+
)}
|
|
634
718
|
</label>
|
|
635
719
|
)}
|
|
636
720
|
<div className="time_picker_wrapper">
|
|
@@ -689,6 +773,7 @@ const TimePicker = (props: TimePickerProps): JSX.Element => {
|
|
|
689
773
|
onBlur={handleHourBlur}
|
|
690
774
|
onChange={handleHourChange}
|
|
691
775
|
onClick={() => { setShowHourDropdown(!showHourDropdown); setShowMinuteDropdown(false) }}
|
|
776
|
+
onFocus={handleHourFocus}
|
|
692
777
|
onKeyDown={handleHourKeyDown}
|
|
693
778
|
pattern="[0-9]*"
|
|
694
779
|
ref={hourInputRef}
|
|
@@ -734,6 +819,7 @@ const TimePicker = (props: TimePickerProps): JSX.Element => {
|
|
|
734
819
|
onBlur={handleMinuteBlur}
|
|
735
820
|
onChange={handleMinuteChange}
|
|
736
821
|
onClick={() => { setShowMinuteDropdown(!showMinuteDropdown); setShowHourDropdown(false) }}
|
|
822
|
+
onFocus={handleMinuteFocus}
|
|
737
823
|
onKeyDown={handleMinuteKeyDown}
|
|
738
824
|
pattern="[0-9]*"
|
|
739
825
|
ref={minuteInputRef}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
2
|
import TimePicker from '../../pb_time_picker/_time_picker'
|
|
3
3
|
import Body from '../../pb_body/_body'
|
|
4
|
+
import Flex from '../../pb_flex/_flex'
|
|
4
5
|
|
|
5
6
|
const TimePickerOnHandler = (props) => {
|
|
6
7
|
const [selectedTime, setSelectedTime] = useState('')
|
|
@@ -17,7 +18,9 @@ const TimePickerOnHandler = (props) => {
|
|
|
17
18
|
return (
|
|
18
19
|
<div>
|
|
19
20
|
{(selectedTime || closedTime) && (
|
|
20
|
-
<
|
|
21
|
+
<Flex marginBottom="sm"
|
|
22
|
+
orientation="column"
|
|
23
|
+
>
|
|
21
24
|
{selectedTime && (
|
|
22
25
|
<Body
|
|
23
26
|
text={`onChange: ${selectedTime}`}
|
|
@@ -29,7 +32,7 @@ const TimePickerOnHandler = (props) => {
|
|
|
29
32
|
text={`onClose: ${closedTime}`}
|
|
30
33
|
/>
|
|
31
34
|
)}
|
|
32
|
-
</
|
|
35
|
+
</Flex>
|
|
33
36
|
)}
|
|
34
37
|
<TimePicker
|
|
35
38
|
id="time-picker-on-handler"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import TimePicker from '../_time_picker'
|
|
3
|
+
|
|
4
|
+
const TimePickerRequiredIndicator = (props) => (
|
|
5
|
+
<div>
|
|
6
|
+
<TimePicker
|
|
7
|
+
id="time-picker-required-indicator"
|
|
8
|
+
label="Select Time"
|
|
9
|
+
requiredIndicator
|
|
10
|
+
{...props}
|
|
11
|
+
/>
|
|
12
|
+
</div>
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
export default TimePickerRequiredIndicator
|
|
16
|
+
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
The `requiredIndicator`/`required_indicator` prop displays a red asterisk (*) next to the label, visually indicating that the field is required. This is purely visual and does not enforce validation.
|
|
2
|
+
|
|
3
|
+
You can use `requiredIndicator`/`required_indicator` with any validation approach: HTML5 validation via the `required` prop, client-side validation, or backend validation. For this reason, it works independently and doesn't need to be paired with the `required` prop.
|
|
@@ -9,6 +9,7 @@ examples:
|
|
|
9
9
|
- time_picker_min_max_time: Min & Max Time
|
|
10
10
|
- time_picker_error: Error
|
|
11
11
|
- time_picker_disabled: Disabled
|
|
12
|
+
- time_picker_required_indicator: Required Indicator
|
|
12
13
|
- time_picker_input_options: Input Options
|
|
13
14
|
|
|
14
15
|
|
|
@@ -21,4 +22,5 @@ examples:
|
|
|
21
22
|
- time_picker_min_max_time: Min & Max Time
|
|
22
23
|
- time_picker_error: Error
|
|
23
24
|
- time_picker_disabled: Disabled
|
|
25
|
+
- time_picker_required_indicator: Required Indicator
|
|
24
26
|
- time_picker_on_handler: onChange & onClose Handlers
|
|
@@ -7,3 +7,4 @@ export { default as TimePickerOnHandler } from './_time_picker_on_handler.jsx'
|
|
|
7
7
|
export { default as TimePickerMinMaxTime } from './_time_picker_min_max_time.jsx'
|
|
8
8
|
export { default as TimePickerError } from './_time_picker_error.jsx'
|
|
9
9
|
export { default as TimePickerDisabled } from './_time_picker_disabled.jsx'
|
|
10
|
+
export { default as TimePickerRequiredIndicator } from './_time_picker_required_indicator.jsx'
|
|
@@ -16,6 +16,8 @@ module Playbook
|
|
|
16
16
|
prop :name, type: Playbook::Props::String
|
|
17
17
|
prop :required, type: Playbook::Props::Boolean,
|
|
18
18
|
default: false
|
|
19
|
+
prop :required_indicator, type: Playbook::Props::Boolean,
|
|
20
|
+
default: false
|
|
19
21
|
prop :show_timezone, type: Playbook::Props::Boolean,
|
|
20
22
|
default: false
|
|
21
23
|
prop :time_format, type: Playbook::Props::Enum,
|
|
@@ -68,6 +70,7 @@ module Playbook
|
|
|
68
70
|
minTime: min_time,
|
|
69
71
|
name: name,
|
|
70
72
|
required: required,
|
|
73
|
+
requiredIndicator: required_indicator,
|
|
71
74
|
showTimezone: show_timezone,
|
|
72
75
|
timeFormat: time_format,
|
|
73
76
|
validationMessage: validation_message,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import { render, screen, fireEvent } from '../utilities/test-utils'
|
|
2
|
+
import { render, screen, fireEvent, within } from '../utilities/test-utils'
|
|
3
3
|
|
|
4
4
|
import TimePicker from './_time_picker'
|
|
5
5
|
|
|
@@ -111,4 +111,50 @@ describe('TimePicker', () => {
|
|
|
111
111
|
expect(screen.getByText('AM')).toBeInTheDocument()
|
|
112
112
|
expect(screen.getByText('PM')).toBeInTheDocument()
|
|
113
113
|
})
|
|
114
|
+
|
|
115
|
+
test('renders required indicator asterisk when requiredIndicator is true', () => {
|
|
116
|
+
render(
|
|
117
|
+
<TimePicker
|
|
118
|
+
data={{ testid: 'required-indicator-picker' }}
|
|
119
|
+
label="Select Time"
|
|
120
|
+
requiredIndicator
|
|
121
|
+
/>
|
|
122
|
+
)
|
|
123
|
+
const kit = screen.getByTestId('required-indicator-picker')
|
|
124
|
+
const label = within(kit).getByText(/Select Time/)
|
|
125
|
+
|
|
126
|
+
expect(label).toBeInTheDocument()
|
|
127
|
+
expect(kit).toHaveTextContent('*')
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
test('requiredIndicator works independently of required prop', () => {
|
|
131
|
+
render(
|
|
132
|
+
<TimePicker
|
|
133
|
+
data={{ testid: 'indicator-without-required' }}
|
|
134
|
+
label="Select Time"
|
|
135
|
+
requiredIndicator
|
|
136
|
+
/>
|
|
137
|
+
)
|
|
138
|
+
const kit = screen.getByTestId('indicator-without-required')
|
|
139
|
+
|
|
140
|
+
expect(kit).toHaveTextContent('*')
|
|
141
|
+
const input = screen.getByPlaceholderText('Select Time')
|
|
142
|
+
expect(input).not.toHaveAttribute('required')
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
test('requiredIndicator and required can be used together', () => {
|
|
146
|
+
render(
|
|
147
|
+
<TimePicker
|
|
148
|
+
data={{ testid: 'both-props-picker' }}
|
|
149
|
+
label="Select Time"
|
|
150
|
+
required
|
|
151
|
+
requiredIndicator
|
|
152
|
+
/>
|
|
153
|
+
)
|
|
154
|
+
const kit = screen.getByTestId('both-props-picker')
|
|
155
|
+
const input = screen.getByPlaceholderText('Select Time')
|
|
156
|
+
|
|
157
|
+
expect(kit).toHaveTextContent('*')
|
|
158
|
+
expect(input).toHaveAttribute('required')
|
|
159
|
+
})
|
|
114
160
|
})
|
|
@@ -290,4 +290,27 @@ test('input display none shows number of selected items', () => {
|
|
|
290
290
|
const kit = screen.getByTestId('input-display-none-test')
|
|
291
291
|
const inputDisplayDiv = kit.querySelector(".pb_typeahead_selection_count")
|
|
292
292
|
expect(inputDisplayDiv).toHaveTextContent("2 items selected")
|
|
293
|
-
})
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
test('typeahead with pills that use name instead of label', () => {
|
|
296
|
+
const customOptions = [
|
|
297
|
+
{ name: 'Nihar', value: '1' },
|
|
298
|
+
{ name: 'kylehgousel', value: '2' },
|
|
299
|
+
]
|
|
300
|
+
|
|
301
|
+
render(
|
|
302
|
+
<Typeahead
|
|
303
|
+
data={{ testid: 'pills-custom-fields-test' }}
|
|
304
|
+
defaultValue={[{ name: 'Nihar', value: '1' }]}
|
|
305
|
+
getOptionLabel={(option) => option.name}
|
|
306
|
+
getOptionValue={(option) => option.value}
|
|
307
|
+
isMulti
|
|
308
|
+
options={customOptions}
|
|
309
|
+
/>
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
const kit = screen.getByTestId('pills-custom-fields-test')
|
|
313
|
+
const pill = kit.querySelector(".pb_form_pill_kit.pb_form_pill_primary")
|
|
314
|
+
expect(pill).toBeInTheDocument()
|
|
315
|
+
expect(pill).toHaveTextContent("Nihar")
|
|
316
|
+
})
|
|
@@ -18,9 +18,12 @@ type Props = {
|
|
|
18
18
|
|
|
19
19
|
const MultiValue = (props: Props) => {
|
|
20
20
|
const { removeProps, isFocused } = props
|
|
21
|
-
const { imageUrl
|
|
21
|
+
const { imageUrl } = props.data
|
|
22
22
|
const { dark, multiKit, pillColor, truncate, wrapped, inputDisplay } = props.selectProps
|
|
23
23
|
|
|
24
|
+
// Extract label - use data.label or data.name if available, otherwise use empty string
|
|
25
|
+
const label = props.data.label || props.data.name || ''
|
|
26
|
+
|
|
24
27
|
// If inputDisplay is "none", don't render the pill/badge, just return null (the count handled in ValueContainer file)
|
|
25
28
|
if (inputDisplay === 'none') {
|
|
26
29
|
return null
|