playbook_ui 15.5.0.pre.alpha.PLAY2581aggressivevalidation12653 → 15.5.0.pre.alpha.PLAY2581aggressivevalidation12698
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_dialog/docs/_dialog_compound_components.html.erb +31 -0
- data/app/pb_kits/playbook/pb_dialog/index.js +15 -10
- data/app/pb_kits/playbook/pb_file_upload/_file_upload.scss +4 -4
- data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +8 -36
- data/app/pb_kits/playbook/pb_typeahead/components/ClearIndicator.tsx +13 -2
- data/dist/chunks/{_typeahead-DLFhGsZU.js → _typeahead-C8dnmJBs.js} +1 -1
- data/dist/chunks/vendor.js +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/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ae823d2ea188c8b8b0476223c92ffb8cbf0604e42028c011f6fe12b81a25b8d1
|
|
4
|
+
data.tar.gz: 7d8758ea0c6fb712605df92777b66589a7d62c63b43cefb3e0cb0a4a104b9547
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b0bdb702a07ce7985616a28ee935a875208cf9e6292c78d4368408804bbd5a18743e52f642be425d0282b951035fbdbf89a51ced9f143ac314e9545e51069b7a
|
|
7
|
+
data.tar.gz: 2b3897ff7f64c3d7a618ba0ae8056bcc817b81dbf65088e66b800fec33f23144966bdceec47386a8356cc04727c62d8db0e5f5d3f6f0f53be142d04545a3329f
|
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
<%
|
|
2
|
+
options = [
|
|
3
|
+
{
|
|
4
|
+
label: "United States",
|
|
5
|
+
value: "unitedStates",
|
|
6
|
+
id: "us"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
label: "United Kingdom",
|
|
10
|
+
value: "unitedKingdom",
|
|
11
|
+
id: "gb"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
label: "Pakistan",
|
|
15
|
+
value: "pakistan",
|
|
16
|
+
id: "pk"
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
%>
|
|
20
|
+
|
|
21
|
+
|
|
1
22
|
<%= pb_rails("button", props: { text: "Open Complex Dialog", data:{"open-dialog": "dialog-complex"} }) %>
|
|
2
23
|
|
|
3
24
|
<%= pb_rails("dialog", props: { id:"dialog-complex", size: "lg", full_height: true }) do %>
|
|
@@ -10,6 +31,16 @@
|
|
|
10
31
|
<%= pb_rails("rich_text_editor", props: {id: "default", value: "Add your text here"}) %>
|
|
11
32
|
<%= pb_rails("caption", props: { text: "Type in a word or term too help find tickets later. ex. training, phone setup, hr", margin_bottom: "xs", margin_top: "sm" }) %>
|
|
12
33
|
<%= pb_rails("typeahead", props: { placeholder: "Tags.."}) %>
|
|
34
|
+
<%= pb_rails("dropdown", props: {options: options, autocomplete: true}) %>
|
|
35
|
+
<%= pb_rails("typeahead", props: {
|
|
36
|
+
id: "typeahead-default",
|
|
37
|
+
placeholder: "Select one...",
|
|
38
|
+
options: options,
|
|
39
|
+
name: :foo,
|
|
40
|
+
margin_top: "sm",
|
|
41
|
+
is_multi: false
|
|
42
|
+
})
|
|
43
|
+
%>
|
|
13
44
|
|
|
14
45
|
<% end %>
|
|
15
46
|
<%= pb_rails("dialog/dialog_footer", props: {cancel_button: "Back", confirm_button: "Send my Issue", confirm_button_id:"confirm-complex", id: "dialog-complex"}) %>
|
|
@@ -143,25 +143,30 @@ export default class PbDialog extends PbEnhancedElement {
|
|
|
143
143
|
|
|
144
144
|
// Close dialog box on outside click
|
|
145
145
|
dialogs.forEach((dialogElement) => {
|
|
146
|
-
const
|
|
147
|
-
if (
|
|
146
|
+
const originalClickHandler = dialogElement._outsideClickHandler
|
|
147
|
+
if (originalClickHandler) dialogElement.removeEventListener("click", originalClickHandler)
|
|
148
|
+
|
|
148
149
|
dialogElement._outsideClickHandler = (event) => {
|
|
149
150
|
const dialogParentDataset = dialogElement.parentElement.dataset
|
|
150
151
|
if (dialogParentDataset.overlayClick === "overlay_close") return
|
|
151
152
|
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
event.
|
|
156
|
-
event.
|
|
157
|
-
|
|
158
|
-
|
|
153
|
+
// Get the dialog's bounding box (the actual content area)
|
|
154
|
+
const rect = dialogElement.getBoundingClientRect()
|
|
155
|
+
const clickedInDialog = (
|
|
156
|
+
event.clientX >= rect.left &&
|
|
157
|
+
event.clientX <= rect.right &&
|
|
158
|
+
event.clientY >= rect.top &&
|
|
159
|
+
event.clientY <= rect.bottom
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
// Only close if clicked outside the dialog content (on the backdrop)
|
|
163
|
+
if (!clickedInDialog) {
|
|
159
164
|
dialogElement.close()
|
|
160
165
|
event.stopPropagation()
|
|
161
166
|
}
|
|
162
167
|
}
|
|
163
168
|
|
|
164
|
-
dialogElement.addEventListener("
|
|
169
|
+
dialogElement.addEventListener("click", dialogElement._outsideClickHandler);
|
|
165
170
|
})
|
|
166
171
|
}
|
|
167
172
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
.pb_file_upload_kit {
|
|
2
|
-
.
|
|
2
|
+
.pb_card_kit {
|
|
3
3
|
border: 1px #ccc dashed;
|
|
4
4
|
text-align: center;
|
|
5
5
|
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
}
|
|
12
12
|
&.error,
|
|
13
13
|
&.pb_file_upload_kit_error {
|
|
14
|
-
.
|
|
14
|
+
.pb_card_kit {
|
|
15
15
|
border-color: $error;
|
|
16
16
|
}
|
|
17
17
|
.pb_body_kit_negative {
|
|
@@ -30,12 +30,12 @@
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
.dark .pb_file_upload_kit {
|
|
33
|
-
.
|
|
33
|
+
.pb_card_kit {
|
|
34
34
|
border: 1px $text_dk_lighter dashed;
|
|
35
35
|
}
|
|
36
36
|
&.error,
|
|
37
37
|
&.pb_file_upload_kit_error {
|
|
38
|
-
.
|
|
38
|
+
.pb_card_kit {
|
|
39
39
|
border-color: $error_dark;
|
|
40
40
|
}
|
|
41
41
|
}
|
|
@@ -112,19 +112,14 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
112
112
|
const wrapperRef = useRef<HTMLDivElement | null>(null);
|
|
113
113
|
const hasBlurredRef = useRef<boolean>(false);
|
|
114
114
|
const formSubmittedRef = useRef<boolean>(false);
|
|
115
|
-
|
|
116
|
-
const getValueString = (val: any): string => {
|
|
117
|
-
if (typeof val === 'string') return val
|
|
118
|
-
if (val?.number) return val.number
|
|
119
|
-
return ""
|
|
120
|
-
}
|
|
121
|
-
const [inputValue, setInputValue] = useState(getValueString(value))
|
|
115
|
+
const [inputValue, setInputValue] = useState(value)
|
|
122
116
|
const [error, setError] = useState(props.error || "")
|
|
123
117
|
const [dropDownIsOpen, setDropDownIsOpen] = useState(false)
|
|
124
118
|
const [selectedData, setSelectedData] = useState()
|
|
125
119
|
const [hasTyped, setHasTyped] = useState(false)
|
|
126
120
|
const [hasBlurred, setHasBlurred] = useState(false)
|
|
127
121
|
const [formSubmitted, setFormSubmitted] = useState(false)
|
|
122
|
+
const [hasStartedValidating, setHasStartedValidating] = useState(false)
|
|
128
123
|
|
|
129
124
|
// Keep refs in sync with state for use in event listeners
|
|
130
125
|
useEffect(() => {
|
|
@@ -134,16 +129,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
134
129
|
useEffect(() => {
|
|
135
130
|
formSubmittedRef.current = formSubmitted
|
|
136
131
|
}, [formSubmitted])
|
|
137
|
-
const [hasStartedValidating, setHasStartedValidating] = useState(false)
|
|
138
|
-
|
|
139
|
-
// Sync value prop when it changes (e.g., from react-hook-form)
|
|
140
|
-
useEffect(() => {
|
|
141
|
-
const newValue = getValueString(props.value)
|
|
142
|
-
if (newValue !== inputValue) {
|
|
143
|
-
setInputValue(newValue)
|
|
144
|
-
}
|
|
145
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
146
|
-
}, [props.value])
|
|
147
132
|
|
|
148
133
|
// Only sync initial error from props, not continuous updates
|
|
149
134
|
// Once validation starts, internal validation takes over
|
|
@@ -173,10 +158,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
173
158
|
// Show internal errors only after blur (hasBlurred) or on form submission (formSubmitted)
|
|
174
159
|
const shouldShowInternalError = (hasBlurred || formSubmitted) && error
|
|
175
160
|
const displayError = shouldShowInternalError ? error : ""
|
|
176
|
-
|
|
177
|
-
// Only show error prop after blur (to prevent showing react-hook-form errors while typing)
|
|
178
|
-
const shouldShowPropError = hasBlurred || formSubmitted
|
|
179
|
-
const propError = shouldShowPropError ? (props.error || "") : ""
|
|
180
161
|
|
|
181
162
|
useEffect(() => {
|
|
182
163
|
const hasError = (error ?? '').length > 0
|
|
@@ -291,8 +272,8 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
291
272
|
}
|
|
292
273
|
|
|
293
274
|
// Only validate if field has been blurred or form has been submitted
|
|
294
|
-
//
|
|
295
|
-
if (!
|
|
275
|
+
// Use refs here since state updates are async and we need current values
|
|
276
|
+
if (!hasBlurredRef.current && !formSubmittedRef.current) return
|
|
296
277
|
|
|
297
278
|
// Run validation checks
|
|
298
279
|
if (itiRef.current) isValid(itiRef.current.isValidNumber())
|
|
@@ -306,7 +287,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
306
287
|
|
|
307
288
|
// Add listener for form validation to track when validation should be shown
|
|
308
289
|
useEffect(() => {
|
|
309
|
-
|
|
290
|
+
const handleInvalid = (event: Event) => {
|
|
310
291
|
const target = event.target as HTMLInputElement
|
|
311
292
|
const phoneNumberContainer = target.closest('.pb_phone_number_input')
|
|
312
293
|
|
|
@@ -444,12 +425,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
444
425
|
setFormSubmitted(false)
|
|
445
426
|
}
|
|
446
427
|
|
|
447
|
-
// Clear error when user starts typing (before blur)
|
|
448
|
-
// This prevents showing validation errors while typing
|
|
449
|
-
if (!hasBlurredRef.current && error) {
|
|
450
|
-
setError('')
|
|
451
|
-
}
|
|
452
|
-
|
|
453
428
|
let phoneNumberData
|
|
454
429
|
|
|
455
430
|
// Handle formatAsYouType with input event
|
|
@@ -465,6 +440,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
465
440
|
|
|
466
441
|
// Don't call isValid callback on change - only on blur or form submission
|
|
467
442
|
// This prevents triggering validation while typing
|
|
443
|
+
// Use refs to get current values in case this is called from event listener
|
|
468
444
|
if (hasBlurredRef.current || formSubmittedRef.current) {
|
|
469
445
|
isValid(itiRef.current.isValidNumber())
|
|
470
446
|
}
|
|
@@ -525,11 +501,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
525
501
|
setInputValue(formattedValue)
|
|
526
502
|
setHasTyped(true)
|
|
527
503
|
|
|
528
|
-
// Clear error when user types (before blur) - use ref to get current value
|
|
529
|
-
if (!hasBlurredRef.current) {
|
|
530
|
-
setError('')
|
|
531
|
-
}
|
|
532
|
-
|
|
533
504
|
// Get phone number data with unformatted number
|
|
534
505
|
const formattedPhoneNumberData = getCurrentSelectedData(telInputInit, formattedValue)
|
|
535
506
|
const phoneNumberData = {...formattedPhoneNumberData, number: unformatNumber(formattedPhoneNumberData.number)}
|
|
@@ -538,6 +509,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
538
509
|
onChange(phoneNumberData)
|
|
539
510
|
|
|
540
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)
|
|
541
513
|
if (hasBlurredRef.current || formSubmittedRef.current) {
|
|
542
514
|
isValid(telInputInit.isValidNumber())
|
|
543
515
|
}
|
|
@@ -550,7 +522,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
550
522
|
dark,
|
|
551
523
|
"data-phone-number": JSON.stringify(selectedData),
|
|
552
524
|
disabled,
|
|
553
|
-
error: displayError ||
|
|
525
|
+
error: displayError || props.error || "",
|
|
554
526
|
type: 'tel',
|
|
555
527
|
id,
|
|
556
528
|
label,
|
|
@@ -7,18 +7,29 @@ type ClearContainerProps = {
|
|
|
7
7
|
id: string,
|
|
8
8
|
},
|
|
9
9
|
clearValue: () => void,
|
|
10
|
+
innerProps?: any,
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
const ClearContainer = (props: ClearContainerProps): React.ReactElement => {
|
|
13
|
-
const { selectProps, clearValue } = props
|
|
13
|
+
const ClearContainer = (props: ClearContainerProps | any): React.ReactElement => {
|
|
14
|
+
const { selectProps, clearValue, innerProps } = props
|
|
14
15
|
useEffect(() => {
|
|
15
16
|
document.addEventListener(`pb-typeahead-kit-${selectProps.id}:clear`, clearValue)
|
|
16
17
|
}, [clearValue, selectProps.id])
|
|
17
18
|
|
|
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
|
+
|
|
18
25
|
return (
|
|
19
26
|
<components.ClearIndicator
|
|
20
27
|
className="clear_indicator"
|
|
21
28
|
{...props}
|
|
29
|
+
innerProps={{
|
|
30
|
+
...innerProps,
|
|
31
|
+
onMouseDown: handleMouseDown,
|
|
32
|
+
}}
|
|
22
33
|
/>
|
|
23
34
|
)
|
|
24
35
|
}
|