playbook_ui 15.3.0.pre.alpha.PLAY2624stickyColumnnullerror12217 → 15.3.0.pre.alpha.PLAY2630fctearlyautoClose12133

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ea7b38915f027f1c001f3d8a0b3a049f48a3a05fac9356694683cd6be63e4baf
4
- data.tar.gz: 0f9a8fa985742d492bfcc05f9c856528fd8add374d8bad8fe7dfde396b7e4885
3
+ metadata.gz: 7192248df5f0637ad12e46b7b132649b32b33307a6ff4f8dccad0ca9f63f6a7a
4
+ data.tar.gz: 827cf7efed9c7bfd05213a2836fb6260a249a9044e93cd5a788fe9e519588400
5
5
  SHA512:
6
- metadata.gz: 57c322955a95e0ac7fafa6a910a7ffee4d306ea589bc3b44a67d9dc0d4180ec0c0eca2fffb9e84b829245498f3bbb28c0e4ad13b2002345f544c9db5b09ce234
7
- data.tar.gz: 6cef80f787aa55523c90ef4eb42241fa354cd7bd085c89d365dbf3a4bbe3d6dac6738e39484fd68a918537e072cf7a0a52657e7e258bc9713b5fb5b1da4c6721
6
+ metadata.gz: d892a19d229008fd10343d7e37058830db0a06ed7082487781c4499b16906d631c77e451b18bd5d43ec5fd92b4dca7cf0bd7f4872ada3fe6c1155d918d6e428c
7
+ data.tar.gz: 1de75fec52872b94288ded06092f1fe94004e9de66ae74b82879524eb80d2e3256ac476e1120743c730770d5080130a7530ff25dab2ccb4e151a1de9adc5a344
@@ -17,7 +17,7 @@ export const getRowColorClass = (
17
17
  (inlineRowLoading && (rowHasNoChildren || row.getCanExpand()))
18
18
  );
19
19
 
20
- return row.getIsSelected() ? "bg-row-selection" : shouldShowExpandedBackground ? "bg-silver" : "pb-bg-row-white";
20
+ return row.getIsSelected() ? "bg-row-selection" : shouldShowExpandedBackground ? "bg-silver" : "bg-white";
21
21
  }
22
22
 
23
23
  /**
@@ -51,7 +51,7 @@
51
51
  background-color: lighten($silver, $opacity_7);
52
52
  }
53
53
 
54
- .pb-bg-row-white {
54
+ .bg-white {
55
55
  background-color: $white;
56
56
  }
57
57
 
@@ -278,7 +278,7 @@
278
278
  }
279
279
  }
280
280
 
281
- &.pb-bg-row-white {
281
+ &.bg-white {
282
282
  td:first-child {
283
283
  background-color: $white;
284
284
  }
@@ -756,7 +756,7 @@
756
756
  border-bottom-color: $border_dark !important;
757
757
  }
758
758
 
759
- .pb-bg-row-white {
759
+ .bg-white {
760
760
  background: $bg_dark_card;
761
761
  }
762
762
 
@@ -766,7 +766,7 @@
766
766
 
767
767
  // Dark mode virtualized rows
768
768
  .virtualized-table-row {
769
- &.pb-bg-row-white {
769
+ &.bg-white {
770
770
  background: $bg_dark_card !important;
771
771
 
772
772
  td:first-child {
@@ -308,10 +308,10 @@ test("Row toggle button exists and toggles subrows open and closed", () => {
308
308
  const kit = screen.getByTestId(testId)
309
309
  const rowButton = kit.querySelector(".gray-icon.expand-toggle-icon")
310
310
  expect(rowButton).toBeInTheDocument()
311
- const subRow1 = kit.querySelector(".pb-bg-row-white.depth-sub-row-1")
311
+ const subRow1 = kit.querySelector(".bg-white.depth-sub-row-1")
312
312
  expect(subRow1).not.toBeInTheDocument()
313
313
  rowButton.click()
314
- const subRow = kit.querySelector(".pb-bg-row-white.depth-sub-row-1")
314
+ const subRow = kit.querySelector(".bg-white.depth-sub-row-1")
315
315
  expect(subRow).toBeInTheDocument()
316
316
  })
317
317
 
@@ -328,13 +328,13 @@ test("toggleExpansionAll button exists and toggles subrows open and closed", asy
328
328
  const toggleButton = kit.querySelector(".gray-icon.toggle-all-icon");
329
329
  expect(toggleButton).toBeInTheDocument();
330
330
 
331
- const subRow1 = kit.querySelector(".pb-bg-row-white.depth-sub-row-1");
331
+ const subRow1 = kit.querySelector(".bg-white.depth-sub-row-1");
332
332
  expect(subRow1).not.toBeInTheDocument();
333
333
 
334
334
  toggleButton.click();
335
335
 
336
336
  await waitFor(() => {
337
- const subRow = kit.querySelector(".pb-bg-row-white.depth-sub-row-1");
337
+ const subRow = kit.querySelector(".bg-white.depth-sub-row-1");
338
338
  expect(subRow).toBeInTheDocument();
339
339
  })
340
340
  })
@@ -385,7 +385,7 @@ test("expandControl prop works as expected", () => {
385
385
  render (<AdvancedTableExpandControl/>)
386
386
 
387
387
  const kit = screen.getByTestId(testId)
388
- const subRow = kit.querySelector(".pb-bg-row-white.depth-sub-row-1")
388
+ const subRow = kit.querySelector(".bg-white.depth-sub-row-1")
389
389
  expect(subRow).toBeInTheDocument()
390
390
  })
391
391
 
@@ -51,7 +51,7 @@ export default class PbFlatAdvancedTable extends PbEnhancedElement {
51
51
 
52
52
  const tr = rowCb.closest("tr");
53
53
  tr?.classList.toggle("bg-row-selection", rowCb.checked);
54
- tr?.classList.toggle("pb-bg-row-white", !rowCb.checked);
54
+ tr?.classList.toggle("bg-white", !rowCb.checked);
55
55
  }
56
56
 
57
57
  if (allCb) {
@@ -62,7 +62,7 @@ export default class PbFlatAdvancedTable extends PbEnhancedElement {
62
62
  cb.checked = checked;
63
63
  const tr = cb.closest("tr");
64
64
  tr?.classList.toggle("bg-row-selection", checked);
65
- tr?.classList.toggle("pb-bg-row-white", !checked);
65
+ tr?.classList.toggle("bg-white", !checked);
66
66
  const id = cb.id;
67
67
  if (checked) this.selectedRows.add(id);
68
68
  else this.selectedRows.delete(id);
@@ -76,7 +76,7 @@ export default class PbAdvancedTable extends PbEnhancedElement {
76
76
  // Only apply styling if the checkbox is inside a table row
77
77
  if (rowEl) {
78
78
  rowEl.classList.add("bg-row-selection");
79
- rowEl.classList.remove("pb-bg-row-white", "bg-silver");
79
+ rowEl.classList.remove("bg-white", "bg-silver");
80
80
  }
81
81
  } else {
82
82
  // Only apply styling if the checkbox is inside a table row
@@ -85,9 +85,9 @@ export default class PbAdvancedTable extends PbEnhancedElement {
85
85
 
86
86
  if (this.isRowExpanded(rowEl)) {
87
87
  rowEl.classList.remove("bg-silver");
88
- rowEl.classList.add("pb-bg-row-white");
88
+ rowEl.classList.add("bg-white");
89
89
  } else {
90
- rowEl.classList.remove("pb-bg-row-white");
90
+ rowEl.classList.remove("bg-white");
91
91
  rowEl.classList.add("bg-silver");
92
92
  }
93
93
  }
@@ -120,7 +120,7 @@ export default class PbAdvancedTable extends PbEnhancedElement {
120
120
  if (isChecked) {
121
121
  PbAdvancedTable.selectedRows.add(rowId);
122
122
  rowEl.classList.add("bg-row-selection");
123
- rowEl.classList.remove("pb-bg-row-white", "bg-silver");
123
+ rowEl.classList.remove("bg-white", "bg-silver");
124
124
  } else {
125
125
  PbAdvancedTable.selectedRows.delete(rowId);
126
126
  }
@@ -130,9 +130,9 @@ export default class PbAdvancedTable extends PbEnhancedElement {
130
130
 
131
131
  if (this.isRowExpanded(rowEl)) {
132
132
  rowEl.classList.remove("bg-silver");
133
- rowEl.classList.add("pb-bg-row-white");
133
+ rowEl.classList.add("bg-white");
134
134
  } else {
135
- rowEl.classList.remove("pb-bg-row-white");
135
+ rowEl.classList.remove("bg-white");
136
136
  rowEl.classList.add("bg-silver");
137
137
  }
138
138
  }
@@ -407,7 +407,7 @@ export default class PbAdvancedTable extends PbEnhancedElement {
407
407
  const row = this.element.closest("tr");
408
408
  if (row) {
409
409
  row.classList.toggle("bg-silver", !isVisible);
410
- row.classList.toggle("pb-bg-row-white", isVisible);
410
+ row.classList.toggle("bg-white", isVisible);
411
411
  }
412
412
 
413
413
  this.addBorderRadiusOnLastVisibleRow();
@@ -52,7 +52,7 @@
52
52
  }
53
53
  }
54
54
 
55
- .pb-bg-row-white {
55
+ .bg-white {
56
56
  td:first-child,
57
57
  .sticky-left {
58
58
  background-color: $bg-main;
@@ -63,7 +63,7 @@
63
63
  &.bg-silver td:first-child {
64
64
  background-color: $bg-secondary;
65
65
  }
66
- &.pb-bg-row-white td:first-child {
66
+ &.bg-white td:first-child {
67
67
  background-color: $bg-main;
68
68
  }
69
69
  &.bg-row-selection td:first-child {
@@ -39,7 +39,7 @@ module Playbook
39
39
  end
40
40
 
41
41
  def classname
42
- generate_classname("pb_table_tr", "pb-bg-row-white", subrow_depth_classname, separator: " ")
42
+ generate_classname("pb_table_tr", "bg-white", subrow_depth_classname, separator: " ")
43
43
  end
44
44
 
45
45
  def td_classname(column, index)
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useState } from "react";
1
+ import React, { useEffect, useState, useRef } from "react";
2
2
  import classnames from "classnames";
3
3
 
4
4
  import { globalProps, GlobalProps } from "../utilities/globalProps";
@@ -36,6 +36,7 @@ type FixedConfirmationToastProps = {
36
36
 
37
37
  const FixedConfirmationToast = (props: FixedConfirmationToastProps): React.ReactElement => {
38
38
  const [showToast, toggleToast] = useState(true);
39
+ const timeoutRef = useRef<NodeJS.Timeout | null>(null);
39
40
 
40
41
  const {
41
42
  autoClose = 0,
@@ -67,21 +68,42 @@ const FixedConfirmationToast = (props: FixedConfirmationToastProps): React.React
67
68
 
68
69
  const htmlProps = buildHtmlProps(htmlOptions);
69
70
 
70
- const autoCloseToast = () => {
71
- if (autoClose && open) {
72
- setTimeout(() => {
71
+ useEffect(() => {
72
+ toggleToast(open);
73
+ }, [open]);
74
+
75
+ // Manage auto-close timeout separately
76
+ useEffect(() => {
77
+ // Clear any existing timeout
78
+ if (timeoutRef.current) {
79
+ clearTimeout(timeoutRef.current);
80
+ timeoutRef.current = null;
81
+ }
82
+
83
+ // Set new timeout if autoClose is enabled and toast is open
84
+ if (autoClose && open && showToast) {
85
+ timeoutRef.current = setTimeout(() => {
73
86
  toggleToast(false);
74
87
  onClose();
88
+ timeoutRef.current = null;
75
89
  }, autoClose);
76
90
  }
77
- };
78
91
 
79
- useEffect(() => {
80
- toggleToast(open);
81
- autoCloseToast();
82
- }, [open]);
92
+ // Cleanup function to clear timeout on unmount or when dependencies change
93
+ return () => {
94
+ if (timeoutRef.current) {
95
+ clearTimeout(timeoutRef.current);
96
+ timeoutRef.current = null;
97
+ }
98
+ };
99
+ }, [autoClose, open, showToast, onClose]);
83
100
 
84
101
  const handleClick = () => {
102
+ // Clear autoClose timeout when manually closing
103
+ if (timeoutRef.current) {
104
+ clearTimeout(timeoutRef.current);
105
+ timeoutRef.current = null;
106
+ }
85
107
  toggleToast(!closeable);
86
108
  onClose();
87
109
  };
@@ -8,13 +8,13 @@ const ERROR_MESSAGE_SELECTOR = '.pb_body_kit_negative'
8
8
  // Validation selectors
9
9
  const FORM_SELECTOR = 'form[data-pb-form-validation="true"]'
10
10
  const REQUIRED_FIELDS_SELECTOR = 'input[required],textarea[required],select[required]'
11
- const PHONE_NUMBER_VALIDATION_ERROR_SELECTOR = '[data-pb-phone-validation-error="true"]'
12
11
 
13
12
  const FIELD_EVENTS = [
14
13
  'change',
15
14
  'valid',
16
15
  'invalid',
17
16
  ]
17
+
18
18
  class PbFormValidation extends PbEnhancedElement {
19
19
  static get selector() {
20
20
  return FORM_SELECTOR
@@ -22,27 +22,12 @@ class PbFormValidation extends PbEnhancedElement {
22
22
 
23
23
  connect() {
24
24
  this.formValidationFields.forEach((field) => {
25
- // Skip phone number inputs - they handle their own validation
26
- const isPhoneNumberInput = field.closest('.pb_phone_number_input')
27
- if (isPhoneNumberInput) return
28
-
29
25
  FIELD_EVENTS.forEach((e) => {
30
26
  field.addEventListener(e, debounce((event) => {
31
27
  this.validateFormField(event)
32
28
  }, 250), false)
33
29
  })
34
30
  })
35
-
36
- // Add event listener to check for phone number validation errors
37
- this.element.addEventListener('submit', (event) => {
38
- // Use setTimeout to ensure React state updates have completed
39
- setTimeout(() => {
40
- if (this.hasPhoneNumberValidationErrors()) {
41
- event.preventDefault()
42
- return false
43
- }
44
- }, 0)
45
- })
46
31
  }
47
32
 
48
33
  validateFormField(event) {
@@ -60,58 +45,40 @@ class PbFormValidation extends PbEnhancedElement {
60
45
 
61
46
  showValidationMessage(target) {
62
47
  const { parentElement } = target
63
- const kitElement = parentElement.closest(KIT_SELECTOR)
64
-
65
- // FIX: Add null check for kitElement
66
- if (!kitElement) return
67
-
68
- // Check if this is a phone number input
69
- const isPhoneNumberInput = kitElement.classList.contains('pb_phone_number_input')
70
48
 
71
49
  // ensure clean error message state
72
50
  this.clearError(target)
73
- kitElement.classList.add('error')
51
+ parentElement.closest(KIT_SELECTOR).classList.add('error')
74
52
 
75
- // Only add error message if it's NOT a phone number input
76
- if (!isPhoneNumberInput) {
77
- // set the error message element
78
- const errorMessageContainer = this.errorMessageContainer
53
+ // set the error message element
54
+ const errorMessageContainer = this.errorMessageContainer
79
55
 
80
- if (target.dataset.message) target.setCustomValidity(target.dataset.message)
56
+ if (target.dataset.message) target.setCustomValidity(target.dataset.message)
81
57
 
82
- errorMessageContainer.innerHTML = target.validationMessage
58
+ errorMessageContainer.innerHTML = target.validationMessage
83
59
 
84
- // add the error message element to the dom tree
85
- parentElement.appendChild(errorMessageContainer)
86
- }
60
+ // add the error message element to the dom tree
61
+ parentElement.appendChild(errorMessageContainer)
87
62
  }
88
63
 
89
64
  clearError(target) {
90
65
  const { parentElement } = target
91
- const kitElement = parentElement.closest(KIT_SELECTOR)
92
- // Remove error class from kit element
93
- if (kitElement) kitElement.classList.remove('error')
94
- // Remove error message from parent element
66
+ parentElement.closest(KIT_SELECTOR).classList.remove('error')
95
67
  const errorMessageContainer = parentElement.querySelector(ERROR_MESSAGE_SELECTOR)
96
68
  if (errorMessageContainer) errorMessageContainer.remove()
97
69
  }
98
70
 
99
- // Check if there are phone number input errors
100
- hasPhoneNumberValidationErrors() {
101
- const phoneNumberErrors = this.element.querySelectorAll(PHONE_NUMBER_VALIDATION_ERROR_SELECTOR)
102
- return phoneNumberErrors.length > 0
103
- }
104
-
105
71
  get errorMessageContainer() {
106
72
  const errorContainer = document.createElement('div')
107
73
  const kitClassName = ERROR_MESSAGE_SELECTOR.replace(/\./, '')
108
74
  errorContainer.classList.add(kitClassName)
109
75
  return errorContainer
110
76
  }
77
+
111
78
  get formValidationFields() {
112
79
  return this._formValidationFields =
113
80
  this._formValidationFields || this.element.querySelectorAll(REQUIRED_FIELDS_SELECTOR)
114
81
  }
115
82
  }
116
83
 
117
- window.PbFormValidation = PbFormValidation
84
+ window.PbFormValidation = PbFormValidation
@@ -55,6 +55,7 @@ const formatToGlobalCountryName = (countryName: string) => {
55
55
 
56
56
  const formatAllCountries = () => {
57
57
  const countryData = intlTelInput.getCountryData()
58
+
58
59
  for (let i = 0; i < countryData.length; i++) {
59
60
  const country = countryData[i]
60
61
  country.name = formatToGlobalCountryName(country.name)
@@ -109,54 +110,18 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
109
110
 
110
111
  const inputRef = useRef<HTMLInputElement | null>(null)
111
112
  const itiRef = useRef<any>(null);
112
- const wrapperRef = useRef<HTMLDivElement | null>(null);
113
113
  const [inputValue, setInputValue] = useState(value)
114
114
  const [error, setError] = useState(props.error || "")
115
115
  const [dropDownIsOpen, setDropDownIsOpen] = useState(false)
116
116
  const [selectedData, setSelectedData] = useState()
117
117
  const [hasTyped, setHasTyped] = useState(false)
118
- const [formSubmitted, setFormSubmitted] = useState(false)
119
- const [hasStartedValidating, setHasStartedValidating] = useState(false)
120
-
121
- // Only sync initial error from props, not continuous updates
122
- // Once validation starts, internal validation takes over
123
- useEffect(() => {
124
- if (props.error && !hasStartedValidating) {
125
- setError(props.error)
126
- // If there's an initial error from props, mark as submitted so it shows
127
- if (props.error) {
128
- setFormSubmitted(true)
129
- }
130
- }
131
- }, [props.error, hasStartedValidating])
132
-
133
- // Function to update validation state on the wrapper element
134
- // Only applies when input is required
135
- const updateValidationState = (hasError: boolean) => {
136
- if (wrapperRef.current && required) {
137
- if (hasError) {
138
- wrapperRef.current.setAttribute('data-pb-phone-validation-error', 'true')
139
- } else {
140
- wrapperRef.current.removeAttribute('data-pb-phone-validation-error')
141
- }
142
- }
143
- }
144
-
145
- // Determine which error to display
146
- // Show internal errors on blur (hasTyped) or on form submission (formSubmitted)
147
- const shouldShowInternalError = (hasTyped || formSubmitted) && required && error
148
- const displayError = shouldShowInternalError ? error : ""
149
118
 
150
119
  useEffect(() => {
151
- const hasError = (error ?? '').length > 0
152
- if (hasError) {
120
+ if ((error ?? '').length > 0) {
153
121
  onValidate(false)
154
122
  } else {
155
123
  onValidate(true)
156
124
  }
157
-
158
- // Update validation state whenever error changes
159
- updateValidationState(hasError)
160
125
  }, [error, onValidate])
161
126
 
162
127
  const unformatNumber = (formattedNumber: any) => {
@@ -172,7 +137,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
172
137
 
173
138
  const validateTooLongNumber = (itiInit: any) => {
174
139
  if (!itiInit) return
175
-
176
140
  if (itiInit.getValidationError() === ValidationError.TooLong) {
177
141
  return showFormattedError('too long')
178
142
  } else {
@@ -182,11 +146,13 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
182
146
 
183
147
  const validateTooShortNumber = (itiInit: any) => {
184
148
  if (!itiInit) return
149
+
185
150
  // If field is empty, don't show "too short" error
186
151
  if (!inputValue || inputValue.trim() === '') {
187
152
  setError('')
188
153
  return false
189
154
  }
155
+
190
156
  if (itiInit.getValidationError() === ValidationError.TooShort) {
191
157
  return showFormattedError('too short')
192
158
  } else {
@@ -206,7 +172,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
206
172
  }
207
173
 
208
174
  const validateUnhandledError = (itiInit: any) => {
209
- if (!required || !itiInit) return
175
+ if (!itiInit) return
210
176
  if (itiInit.getValidationError() === ValidationError.SomethingWentWrong) {
211
177
  if (inputValue.length === 1) {
212
178
  return showFormattedError('too short')
@@ -218,6 +184,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
218
184
  }
219
185
  }
220
186
  }
187
+
221
188
  const validateMissingAreaCode = (itiInit: any) => {
222
189
  if (!itiInit) return
223
190
  if (itiInit.getValidationError() === ValidationError.MissingAreaCode) {
@@ -234,9 +201,8 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
234
201
  }
235
202
  }
236
203
 
237
- // Validation for required empty fields
238
204
  const validateRequiredField = () => {
239
- if (required && (!inputValue || inputValue.trim() === '')) {
205
+ if (!inputValue || inputValue.trim() === '') {
240
206
  setError('Missing phone number')
241
207
  return true
242
208
  }
@@ -244,24 +210,14 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
244
210
  }
245
211
 
246
212
  const validateErrors = () => {
247
- // Signal validation has started, so prop errors won't override internal validation
248
- if (!hasStartedValidating) {
249
- setHasStartedValidating(true)
250
- }
251
-
252
- // If field is empty, only show required field error if applicable
213
+ // If field is empty, show error message
253
214
  if (!inputValue || inputValue.trim() === '') {
254
215
  if (validateRequiredField()) return
255
- // Clear any existing errors if field is empty and not required
256
- if (!required) {
257
- setError('')
258
- }
259
216
  return
260
217
  }
261
218
 
262
- if (!hasTyped && !error) return
219
+ if (!hasTyped && !error) return
263
220
 
264
- // Run validation checks
265
221
  if (itiRef.current) isValid(itiRef.current.isValidNumber())
266
222
  if (validateOnlyNumbers(itiRef.current)) return
267
223
  if (validateTooLongNumber(itiRef.current)) return
@@ -271,29 +227,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
271
227
  if (validateRepeatCountryCode(itiRef.current)) return
272
228
  }
273
229
 
274
- // Add listener for form validation to track when validation should be shown
275
- useEffect(() => {
276
- const handleInvalid = (event: Event) => {
277
- const target = event.target as HTMLInputElement
278
- const phoneNumberContainer = target.closest('.pb_phone_number_input')
279
-
280
- if (phoneNumberContainer && phoneNumberContainer === wrapperRef.current) {
281
- const invalidInputName = target.name || target.getAttribute('name')
282
- if (invalidInputName === name) {
283
- setFormSubmitted(true)
284
- // Trigger validation when form is submitted
285
- validateErrors()
286
- }
287
- }
288
- }
289
-
290
- document.addEventListener('invalid', handleInvalid, true)
291
-
292
- return () => {
293
- document.removeEventListener('invalid', handleInvalid, true)
294
- }
295
- }, [name, inputValue])
296
-
297
230
  /*
298
231
  useImperativeHandle exposes the kit's input element to a parent component via a ref.
299
232
  See the Playbook docs for use cases.
@@ -305,12 +238,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
305
238
  setInputValue("")
306
239
  setError("")
307
240
  setHasTyped(false)
308
- setFormSubmitted(false)
309
- setHasStartedValidating(false)
310
- // Only clear validation state if field was required
311
- if (required) {
312
- updateValidationState(false)
313
- }
314
241
  },
315
242
  inputNode() {
316
243
  return inputRef.current
@@ -320,12 +247,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
320
247
  // Run validation and return error message or true
321
248
  const isEmpty = !inputValue || inputValue.trim() === ''
322
249
 
323
- if (required && isEmpty) {
324
- setError('Missing phone number')
325
- setFormSubmitted(true)
326
- return 'Missing phone number'
327
- }
328
-
329
250
  if (isEmpty) {
330
251
  // Show missing phone number error
331
252
  const errorMessage = 'Missing phone number'
@@ -345,7 +266,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
345
266
  const countryName = itiRef.current.getSelectedCountryData().name
346
267
  const errorMessage = `Invalid ${countryName} phone number (repeat country code)`
347
268
  setError(errorMessage)
348
- setFormSubmitted(true)
349
269
  setHasTyped(true)
350
270
  return errorMessage
351
271
  }
@@ -355,7 +275,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
355
275
  const countryName = itiRef.current.getSelectedCountryData().name
356
276
  const errorMessage = `Invalid ${countryName} phone number (enter numbers only)`
357
277
  setError(errorMessage)
358
- setFormSubmitted(true)
359
278
  setHasTyped(true)
360
279
  return errorMessage
361
280
  }
@@ -376,9 +295,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
376
295
  errorMessage = `Invalid ${countryName} phone number`
377
296
  }
378
297
 
379
- // Set the error state so the validation attribute gets added
380
298
  setError(errorMessage)
381
- setFormSubmitted(true)
382
299
  setHasTyped(true)
383
300
 
384
301
  return errorMessage
@@ -397,16 +314,11 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
397
314
 
398
315
  const handleOnChange = (evt: React.ChangeEvent<HTMLInputElement>) => {
399
316
  if (!hasTyped) setHasTyped(true)
400
- setInputValue(evt.target.value)
401
317
 
402
- // Reset form submitted state when user types
403
- if (formSubmitted) {
404
- setFormSubmitted(false)
405
- }
318
+ setInputValue(evt.target.value)
406
319
 
407
320
  let phoneNumberData
408
321
 
409
- // Handle formatAsYouType with input event
410
322
  if (formatAsYouType) {
411
323
  const formattedPhoneNumberData = getCurrentSelectedData(itiRef.current, evt.target.value)
412
324
  phoneNumberData = {...formattedPhoneNumberData, number: unformatNumber(formattedPhoneNumberData.number)}
@@ -417,15 +329,12 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
417
329
  setSelectedData(phoneNumberData)
418
330
  onChange(phoneNumberData)
419
331
  isValid(itiRef.current.isValidNumber())
420
-
421
- // Trigger validation after onChange for React Hook Form
422
- // This ensures validation state is up-to-date
423
- setTimeout(() => validateErrors(), 0)
424
332
  }
425
333
 
426
334
  // Separating Concerns as React Docs Recommend
427
335
  // This also Fixes things for our react_component rendering on the Rails Side
428
336
  useEffect(formatAllCountries, [])
337
+
429
338
  // If an initial country is not specified, the "globe" icon will show
430
339
  // Always set a country
431
340
  const fallbackCountry =
@@ -466,9 +375,9 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
466
375
  inputRef.current.addEventListener("open:countrydropdown", () => setDropDownIsOpen(true))
467
376
  inputRef.current.addEventListener("close:countrydropdown", () => setDropDownIsOpen(false))
468
377
 
469
- // Handle formatAsYouType with input event
470
- if (formatAsYouType) {
471
- inputRef.current.addEventListener("input", (evt: Event) => {
378
+ // Handle formatAsYouType with input event
379
+ if (formatAsYouType) {
380
+ inputRef.current.addEventListener("input", (evt: Event) => {
472
381
  const target = evt.target as HTMLInputElement
473
382
  const formattedValue = target.value
474
383
 
@@ -487,12 +396,13 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
487
396
  }
488
397
  }
489
398
  }, [])
399
+
490
400
  let textInputProps: {[key: string]: any} = {
491
401
  className: dropDownIsOpen ? 'dropdown_open' : '',
492
402
  dark,
493
403
  "data-phone-number": JSON.stringify(selectedData),
494
404
  disabled,
495
- error: hasTyped ? error : props.error || displayError,
405
+ error: hasTyped ? error : props.error,
496
406
  type: 'tel',
497
407
  id,
498
408
  label,
@@ -502,10 +412,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
502
412
  value: inputValue
503
413
  }
504
414
 
505
- let wrapperProps: Record<string, unknown> = {
506
- className: classes,
507
- ref: wrapperRef
508
- }
415
+ let wrapperProps: Record<string, unknown> = { className: classes }
509
416
 
510
417
  if (!isEmpty(aria)) textInputProps = {...textInputProps, ...ariaProps}
511
418
  if (!isEmpty(data)) wrapperProps = {...wrapperProps, ...dataProps}