playbook_ui 12.26.1 → 12.27.0.pre.alpha.expandednotworking853

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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_date_picker/sass_partials/_flatpickr_styles.scss +11 -9
  3. data/app/pb_kits/playbook/pb_date_picker/sass_partials/_month_and_year_styles.scss +1 -1
  4. data/app/pb_kits/playbook/pb_date_picker/sass_partials/_week_styles.scss +1 -1
  5. data/app/pb_kits/playbook/pb_form_pill/_form_pill.scss +1 -1
  6. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +161 -127
  7. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_default.html.erb +17 -17
  8. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_default.jsx +8 -4
  9. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_default.md +2 -2
  10. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_return_all_selected.html.erb +19 -16
  11. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_return_all_selected.jsx +2 -2
  12. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_form.html.erb +72 -0
  13. data/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +1 -0
  14. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.html.erb +7 -3
  15. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.rb +3 -0
  16. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +109 -44
  17. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.html.erb +14 -0
  18. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.jsx +60 -0
  19. data/app/pb_kits/playbook/pb_phone_number_input/docs/example.yml +3 -1
  20. data/app/pb_kits/playbook/pb_phone_number_input/docs/index.js +1 -0
  21. data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.rb +6 -0
  22. data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.test.js +110 -62
  23. data/app/pb_kits/playbook/pb_rich_text_editor/_trix_styles.scss +2 -2
  24. data/app/pb_kits/playbook/pb_title/_title.scss +1 -1
  25. data/app/pb_kits/playbook/pb_title/_title.tsx +2 -3
  26. data/app/pb_kits/playbook/pb_title/docs/_title_light_weight.html.erb +1 -0
  27. data/app/pb_kits/playbook/pb_title/docs/_title_light_weight.jsx +6 -0
  28. data/app/pb_kits/playbook/pb_title/docs/_title_light_weight.md +1 -2
  29. data/app/pb_kits/playbook/pb_title/title.rb +3 -10
  30. data/app/pb_kits/playbook/pb_title/title.test.js +3 -3
  31. data/app/pb_kits/playbook/utilities/object.ts +3 -0
  32. data/dist/playbook-rails.js +4 -4
  33. data/lib/playbook/forms/builder/intl_telephone_field.rb +12 -0
  34. data/lib/playbook/forms/builder/multi_level_select_field.rb +12 -0
  35. data/lib/playbook/forms/builder.rb +2 -0
  36. data/lib/playbook/version.rb +2 -2
  37. metadata +13 -7
@@ -1,72 +1,72 @@
1
1
  <% treeData = [{
2
2
  label: "Power Home Remodeling",
3
3
  value: "Power Home Remodeling",
4
- id: "powerhome1",
4
+ id: "100",
5
5
  expanded: true,
6
6
  children: [
7
7
  {
8
8
  label: "People",
9
9
  value: "People",
10
- id: "people1",
10
+ id: "101",
11
11
  children: [
12
12
  {
13
13
  label: "Talent Acquisition",
14
14
  value: "Talent Acquisition",
15
- id: "talent1",
15
+ id: "102",
16
16
  },
17
17
  {
18
18
  label: "Business Affairs",
19
19
  value: "Business Affairs",
20
- id: "business1",
20
+ id: "103",
21
21
  children: [
22
22
  {
23
23
  label: "Initiatives",
24
24
  value: "Initiatives",
25
- id: "initiative1",
25
+ id: "104",
26
26
  },
27
27
  {
28
28
  label: "Learning & Development",
29
29
  value: "Learning & Development",
30
- id: "development1",
30
+ id: "105",
31
31
  },
32
32
  ],
33
33
  },
34
34
  {
35
35
  label: "People Experience",
36
36
  value: "People Experience",
37
- id: "experience1",
37
+ id: "106",
38
38
  },
39
39
  ],
40
40
  },
41
41
  {
42
42
  label: "Contact Center",
43
43
  value: "Contact Center",
44
- id: "contact1",
44
+ id: "107",
45
45
  children: [
46
46
  {
47
47
  label: "Appointment Management",
48
48
  value: "Appointment Management",
49
- id: "appointment1",
49
+ id: "108",
50
50
  },
51
51
  {
52
52
  label: "Customer Service",
53
53
  value: "Customer Service",
54
- id: "customer1",
54
+ id: "109",
55
55
  },
56
56
  {
57
57
  label: "Energy",
58
58
  value: "Energy",
59
- id: "energy1",
59
+ id: "110",
60
60
  },
61
61
  ],
62
62
  },
63
63
  ],
64
- }] %>
64
+ }] %>
65
65
 
66
66
 
67
- <%= pb_rails("multi_level_select", props: {
68
- id: "default-multi-level-select",
69
- tree_data:treeData
70
- }) %>
71
-
72
67
 
68
+ <%= pb_rails("multi_level_select", props: {
69
+ id: "multi-level-select-default-rails",
70
+ name: "my_array",
71
+ tree_data: treeData
72
+ }) %>
@@ -12,6 +12,7 @@ const treeData = [
12
12
  label: "People",
13
13
  value: "People",
14
14
  id: "people1",
15
+ expanded: true,
15
16
  children: [
16
17
  {
17
18
  label: "Talent Acquisition",
@@ -72,15 +73,18 @@ const MultiLevelSelectDefault = (props) => {
72
73
  return (
73
74
  <div>
74
75
  <MultiLevelSelect
75
- id="multiselect-default"
76
+ id='multiselect-default'
76
77
  onSelect={(selectedNodes) =>
77
- console.log("Selected Items", selectedNodes)
78
- }
78
+ console.log(
79
+ "Selected Items",
80
+ selectedNodes
81
+ )
82
+ }
79
83
  treeData={treeData}
80
84
  {...props}
81
85
  />
82
86
  </div>
83
- );
87
+ )
84
88
  };
85
89
 
86
90
  export default MultiLevelSelectDefault;
@@ -1,5 +1,5 @@
1
1
  The MultiLevelSelect kit renders a multi leveled select dropdown based on data from the user. `treeData` is a required prop that is expected to contain the data in the form of an array of objects. See code snippet for an example data array.
2
2
 
3
- For the React version of the kit, the `onSelect` prop returns an array of all checked items, irrespective of whether it is a parent, child or grandchild. Open the console on this example and check and uncheck checkboxes to see this is action!
3
+ For the React version of the kit, the `onSelect` prop returns an array of objects. This array contains all checked items irrespective of whether it is a parent, child or grandchild. Open the console on this example and check and uncheck checkboxes to see this is action!
4
4
 
5
- For the Rails version, the array of checked items is attached to the DOM in a data attribute titled `data-tree` on the wrapping div around the MultiLevelSelect.
5
+ For the Rails version of the kit, there is no onselect. The form submits as a array of strings, following the typical rails pattern of utilizing hidden inputs. The strings are the values of the selected items' ids. For example, ["103", "106", "107"].
@@ -1,72 +1,75 @@
1
1
  <% treeData = [{
2
2
  label: "Power Home Remodeling",
3
3
  value: "Power Home Remodeling",
4
- id: "powerhome1",
4
+ id: "100",
5
5
  expanded: true,
6
-
7
6
  children: [
8
7
  {
9
8
  label: "People",
10
9
  value: "People",
11
- id: "people1",
10
+ id: "101",
12
11
  children: [
13
12
  {
14
13
  label: "Talent Acquisition",
15
14
  value: "Talent Acquisition",
16
- id: "talent1",
15
+ id: "102",
17
16
  },
18
17
  {
19
18
  label: "Business Affairs",
20
19
  value: "Business Affairs",
21
- id: "business1",
20
+ id: "103",
22
21
  children: [
23
22
  {
24
23
  label: "Initiatives",
25
24
  value: "Initiatives",
26
- id: "initiative1",
25
+ id: "104",
27
26
  },
28
27
  {
29
28
  label: "Learning & Development",
30
29
  value: "Learning & Development",
31
- id: "development1",
30
+ id: "105",
32
31
  },
33
32
  ],
34
33
  },
35
34
  {
36
35
  label: "People Experience",
37
36
  value: "People Experience",
38
- id: "experience1",
37
+ id: "106",
39
38
  },
40
39
  ],
41
40
  },
42
41
  {
43
42
  label: "Contact Center",
44
43
  value: "Contact Center",
45
- id: "contact1",
44
+ id: "107",
46
45
  children: [
47
46
  {
48
47
  label: "Appointment Management",
49
48
  value: "Appointment Management",
50
- id: "appointment1",
49
+ id: "108",
51
50
  },
52
51
  {
53
52
  label: "Customer Service",
54
53
  value: "Customer Service",
55
- id: "customer1",
54
+ id: "109",
56
55
  },
57
56
  {
58
57
  label: "Energy",
59
58
  value: "Energy",
60
- id: "energy1",
59
+ id: "110",
61
60
  },
62
61
  ],
63
62
  },
64
63
  ],
65
- }] %>
64
+ }] %>
65
+
66
+
66
67
 
67
68
 
68
69
  <%= pb_rails("multi_level_select", props: {
69
- id: "parent-persistence-multi-level-select",
70
- tree_data:treeData,
70
+ id: "multi-level-select-return-all-selected-rails",
71
+ name: "my_data_array",
72
+ tree_data: treeData,
71
73
  return_all_selected: true
72
- }) %>
74
+ }) %>
75
+
@@ -72,9 +72,9 @@ const MultiLevelSelectReturnAllSelected = (props) => {
72
72
  return (
73
73
  <div>
74
74
  <MultiLevelSelect
75
- id="multiselect-parent-persistence"
75
+ id="multi-level-select-return-all-selected"
76
76
  onSelect={(selectedNodes) =>
77
- console.log("Selected Items", selectedNodes)
77
+ console.log("Selected Items with Return All Selected Data", selectedNodes)
78
78
  }
79
79
  returnAllSelected
80
80
  treeData={treeData}
@@ -0,0 +1,72 @@
1
+ <%= pb_form_with(scope: :example, url: "", method: :get) do |form| %>
2
+
3
+ <% treeData = [{
4
+ label: "Power Home Remodeling",
5
+ value: "Power Home Remodeling",
6
+ id: "powerhome1",
7
+ expanded: true,
8
+ children: [
9
+ {
10
+ label: "People",
11
+ value: "People",
12
+ id: "people1",
13
+ children: [
14
+ {
15
+ label: "Talent Acquisition",
16
+ value: "Talent Acquisition",
17
+ id: "talent1",
18
+ },
19
+ {
20
+ label: "Business Affairs",
21
+ value: "Business Affairs",
22
+ id: "business1",
23
+ children: [
24
+ {
25
+ label: "Initiatives",
26
+ value: "Initiatives",
27
+ id: "initiative1",
28
+ },
29
+ {
30
+ label: "Learning & Development",
31
+ value: "Learning & Development",
32
+ id: "development1",
33
+ },
34
+ ],
35
+ },
36
+ {
37
+ label: "People Experience",
38
+ value: "People Experience",
39
+ id: "experience1",
40
+ },
41
+ ],
42
+ },
43
+ {
44
+ label: "Contact Center",
45
+ value: "Contact Center",
46
+ id: "contact1",
47
+ children: [
48
+ {
49
+ label: "Appointment Management",
50
+ value: "Appointment Management",
51
+ id: "appointment1",
52
+ },
53
+ {
54
+ label: "Customer Service",
55
+ value: "Customer Service",
56
+ id: "customer1",
57
+ },
58
+ {
59
+ label: "Energy",
60
+ value: "Energy",
61
+ id: "energy1",
62
+ },
63
+ ],
64
+ },
65
+ ],
66
+ }] %>
67
+
68
+ <%= form.multi_level_select :example, props: {id: "with-form-multi-level-select", tree_data: treeData, return_all_selected: true, margin_bottom: "sm" } %>
69
+ <%= form.actions do |action| %>
70
+ <%= action.button props: { type: "submit", text: "Submit", variant: "primary", margin_top: "lg" } %>
71
+ <% end %>
72
+ <% end %>
@@ -2,6 +2,7 @@ examples:
2
2
  rails:
3
3
  - multi_level_select_default: Default
4
4
  - multi_level_select_return_all_selected: Return All Selected
5
+ - multi_level_select_with_form: With Form
5
6
 
6
7
  react:
7
8
  - multi_level_select_default: Default
@@ -1,3 +1,7 @@
1
- <div id="pb_data_wrapper_<%= object.id %>" data-tree="">
2
- <%= react_component("MultiLevelSelect", object.multi_level_select_options ) %>
3
- </div>
1
+ <%= content_tag(:div,
2
+ aria: object.aria,
3
+ data: object.data,
4
+ id: object.id,
5
+ class: object.classname) do %>
6
+ <%= react_component("MultiLevelSelect", object.multi_level_select_options) %>
7
+ <% end %>
@@ -3,6 +3,8 @@
3
3
  module Playbook
4
4
  module PbMultiLevelSelect
5
5
  class MultiLevelSelect < Playbook::KitBase
6
+ prop :id
7
+ prop :name
6
8
  prop :tree_data, type: Playbook::Props::Array,
7
9
  default: []
8
10
  prop :return_all_selected, type: Playbook::Props::Boolean,
@@ -15,6 +17,7 @@ module Playbook
15
17
  def multi_level_select_options
16
18
  {
17
19
  id: id,
20
+ name: name,
18
21
  treeData: tree_data,
19
22
  returnAllSelected: return_all_selected,
20
23
  }
@@ -1,12 +1,17 @@
1
- import React, { useEffect, useRef, useState } from 'react'
1
+ import React, { forwardRef, useEffect, useRef, useState } from 'react'
2
2
  import classnames from 'classnames'
3
- import { buildAriaProps, buildCss, buildDataProps } from '../utilities/props'
4
- import { globalProps } from '../utilities/globalProps'
3
+
5
4
  import intlTelInput from 'intl-tel-input'
6
5
  import 'intl-tel-input/build/css/intlTelInput.css'
7
- import TextInput from '../pb_text_input/_text_input'
8
6
  import 'intl-tel-input/build/js/utils.js'
9
7
 
8
+ import { buildAriaProps, buildCss, buildDataProps } from '../utilities/props'
9
+ import { globalProps } from '../utilities/globalProps'
10
+
11
+ import TextInput from '../pb_text_input/_text_input'
12
+ import { Callback } from '../types'
13
+ import { isEmpty } from '../utilities/object'
14
+
10
15
  declare global {
11
16
  interface Window {
12
17
  intlTelInputGlobals: any
@@ -19,20 +24,25 @@ type PhoneNumberInputProps = {
19
24
  dark?: boolean,
20
25
  data?: { [key: string]: string },
21
26
  disabled?: boolean,
27
+ error?: string,
22
28
  id?: string,
23
29
  initialCountry?: string,
24
30
  isValid?: (valid: boolean) => void,
25
31
  label?: string,
26
32
  name?: string,
27
33
  onChange?: (e: React.FormEvent<HTMLInputElement>) => void,
34
+ onValidate?: Callback<boolean, void>,
28
35
  onlyCountries: string[],
29
36
  preferredCountries?: string[],
37
+ required?: boolean,
30
38
  value?: string,
31
39
  }
32
40
 
33
41
  enum ValidationError {
34
42
  TooShort = 2,
35
43
  TooLong = 3,
44
+ MissingAreaCode = 4,
45
+ SomethingWentWrong = -99
36
46
  }
37
47
 
38
48
  const formatToGlobalCountryName = (countryName: string) => {
@@ -40,10 +50,10 @@ const formatToGlobalCountryName = (countryName: string) => {
40
50
  }
41
51
 
42
52
  const formatAllCountries = () => {
43
- let countryData = window.intlTelInputGlobals.getCountryData()
53
+ const countryData = window.intlTelInputGlobals.getCountryData()
44
54
 
45
55
  for (let i = 0; i < countryData.length; i++) {
46
- let country = countryData[i]
56
+ const country = countryData[i]
47
57
  country.name = formatToGlobalCountryName(country.name)
48
58
  }
49
59
  }
@@ -71,7 +81,9 @@ const PhoneNumberInput = (props: PhoneNumberInputProps) => {
71
81
  onChange = () => {
72
82
  void 0
73
83
  },
84
+ onValidate = () => null,
74
85
  onlyCountries = [],
86
+ required = false,
75
87
  preferredCountries = [],
76
88
  value = "",
77
89
  } = props
@@ -87,39 +99,83 @@ const PhoneNumberInput = (props: PhoneNumberInputProps) => {
87
99
  const inputRef = useRef<HTMLInputElement>()
88
100
  const [inputValue, setInputValue] = useState(value)
89
101
  const [itiInit, setItiInit] = useState<any>()
90
- const [error, setError] = useState('')
102
+ const [error, setError] = useState(props.error)
91
103
  const [dropDownIsOpen, setDropDownIsOpen] = useState(false)
92
104
  const [selectedData, setSelectedData] = useState()
93
105
 
106
+ useEffect(() => {
107
+ if (error?.length > 0) {
108
+ onValidate(false)
109
+ } else {
110
+ onValidate(true)
111
+ }
112
+ }, [error, onValidate])
113
+
114
+ const showFormattedError = (reason = '') => {
115
+ const countryName = itiInit.getSelectedCountryData().name
116
+ const reasonText = reason.length > 0 ? ` (${reason})` : ''
117
+ setError(`Invalid ${countryName} phone number${reasonText}`)
118
+ return true
119
+ }
120
+
94
121
  const validateTooLongNumber = (itiInit: any) => {
95
- const error = itiInit.getValidationError()
122
+ if (!itiInit) return
123
+ if (itiInit.getValidationError() === ValidationError.TooLong) {
124
+ return showFormattedError('too long')
125
+ } else {
126
+ setError('')
127
+ }
128
+ }
96
129
 
97
- if (error === ValidationError.TooLong) {
98
- const countryName = itiInit.getSelectedCountryData().name
99
- setError(`Invalid ${countryName} phone number (too long)`)
130
+ const validateTooShortNumber = (itiInit: any) => {
131
+ if (!itiInit) return
132
+ if (itiInit.getValidationError() === ValidationError.TooShort) {
133
+ return showFormattedError('too short')
100
134
  } else {
101
- setError("")
135
+ if (inputValue.length === 1) {
136
+ return showFormattedError('too short')
137
+ } else {
138
+ setError('')
139
+ }
102
140
  }
103
141
  }
104
142
 
105
- const validateTooShortNumber = () => {
106
- const error = itiInit.getValidationError()
143
+ const validateOnlyNumbers = (itiInit: any) => {
144
+ if (!itiInit) return
145
+ if (inputValue && !containOnlyNumbers(inputValue)) {
146
+ return showFormattedError('enter numbers only')
147
+ }
148
+ }
107
149
 
108
- if (error === ValidationError.TooShort) {
109
- const countryName = itiInit.getSelectedCountryData().name
110
- setError(`Invalid ${countryName} phone number (too short)`)
150
+ const validateUnhandledError = (itiInit: any) => {
151
+ if (!required || !itiInit) return
152
+ if (itiInit.getValidationError() === ValidationError.SomethingWentWrong) {
153
+ if (inputValue.length === 1) {
154
+ return showFormattedError('too short')
155
+ } else if (inputValue.length === 0) {
156
+ setError('Missing phone number')
157
+ return true
158
+ } else {
159
+ return showFormattedError()
160
+ }
111
161
  }
112
162
  }
113
163
 
114
- const validateOnlyNumbers = () => {
115
- if (inputValue && !containOnlyNumbers(inputValue)) {
116
- setError("Invalid phone number. Enter numbers only.")
164
+ const validateMissingAreaCode = (itiInit: any) => {
165
+ if (!required || !itiInit) return
166
+ if (itiInit.getValidationError() === ValidationError.MissingAreaCode) {
167
+ showFormattedError('missing area code')
168
+ return true
117
169
  }
118
170
  }
119
171
 
120
172
  const validateErrors = () => {
121
- validateTooShortNumber()
122
- validateOnlyNumbers()
173
+ if (itiInit) isValid(itiInit.isValidNumber())
174
+ if (validateOnlyNumbers(itiInit)) return
175
+ if (validateTooLongNumber(itiInit)) return
176
+ if (validateTooShortNumber(itiInit)) return
177
+ if (validateUnhandledError(itiInit)) return
178
+ if (validateMissingAreaCode(itiInit)) return
123
179
  }
124
180
 
125
181
  const getCurrentSelectedData = (itiInit: any, inputValue: string) => {
@@ -128,7 +184,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps) => {
128
184
 
129
185
  const handleOnChange = (evt: React.ChangeEvent<HTMLInputElement>) => {
130
186
  setInputValue(evt.target.value)
131
- validateTooLongNumber(itiInit)
132
187
  const phoneNumberData = getCurrentSelectedData(itiInit, evt.target.value)
133
188
  setSelectedData(phoneNumberData)
134
189
  onChange(phoneNumberData)
@@ -137,25 +192,24 @@ const PhoneNumberInput = (props: PhoneNumberInputProps) => {
137
192
 
138
193
  // Separating Concerns as React Docs Recommend
139
194
  // This also Fixes things for our react_component rendering on the Rails Side
140
- useEffect(() => {
141
- formatAllCountries()
142
- }, [])
195
+ useEffect(formatAllCountries, [])
143
196
 
144
197
  useEffect(() => {
145
- const telInputInit = new intlTelInput(inputRef.current, {
198
+ const telInputInit = intlTelInput(inputRef.current, {
146
199
  separateDialCode: true,
147
200
  preferredCountries,
148
201
  allowDropdown: !disabled,
202
+ autoInsertDialCode: false,
149
203
  initialCountry,
150
204
  onlyCountries,
151
- }
152
- )
205
+ utilsScript: "https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/18.1.6/js/utils.min.js"
206
+ })
153
207
 
154
208
  inputRef.current.addEventListener("countrychange", (evt: Event) => {
155
- validateTooLongNumber(telInputInit)
156
209
  const phoneNumberData = getCurrentSelectedData(telInputInit, (evt.target as HTMLInputElement).value)
157
210
  setSelectedData(phoneNumberData)
158
211
  onChange(phoneNumberData)
212
+ validateErrors()
159
213
  })
160
214
 
161
215
  inputRef.current.addEventListener("open:countrydropdown", () => setDropDownIsOpen(true))
@@ -164,24 +218,35 @@ const PhoneNumberInput = (props: PhoneNumberInputProps) => {
164
218
  setItiInit(telInputInit)
165
219
  }, [])
166
220
 
221
+ let textInputProps: {[key: string]: any} = {
222
+ className: dropDownIsOpen ? 'dropdown_open' : '',
223
+ dark,
224
+ "data-phone-number": JSON.stringify(selectedData),
225
+ disabled,
226
+ error,
227
+ type: 'tel',
228
+ id,
229
+ label,
230
+ name,
231
+ onBlur: validateErrors,
232
+ onChange: handleOnChange,
233
+ value: inputValue
234
+ }
235
+
236
+ let wrapperProps: Record<string, unknown> = { className: classes }
237
+
238
+ if (!isEmpty(aria)) textInputProps = {...textInputProps, ...ariaProps}
239
+ if (!isEmpty(data)) wrapperProps = {...wrapperProps, ...dataProps}
240
+ if (required) textInputProps.required = true
241
+
167
242
  return (
168
- <div {...ariaProps} {...dataProps} className={classes}>
243
+ <div {...wrapperProps}>
169
244
  <TextInput
170
- className={dropDownIsOpen ? 'dropdown_open' : ''}
171
- dark={dark}
172
- data-phone-number={JSON.stringify(selectedData)}
173
- disabled={disabled}
174
- error={error}
175
- id={id}
176
- label={label}
177
- name={name}
178
- onBlur={() => validateErrors()}
179
- onChange={handleOnChange}
180
- ref={inputRef}
181
- value={inputValue}
245
+ ref={inputRef}
246
+ {...textInputProps}
182
247
  />
183
248
  </div>
184
249
  )
185
250
  }
186
251
 
187
- export default PhoneNumberInput
252
+ export default forwardRef(PhoneNumberInput)
@@ -0,0 +1,14 @@
1
+ <form id="example-form-validation" action="" method="get">
2
+ <%= pb_rails("phone_number_input", props: { error: "Missing phone number.", id: "validation", initial_country: "af", value: "", required: true }) %>
3
+ <%= pb_rails("button", props: {html_type: "submit", text: "Save Phone Number"}) %>
4
+ </form>
5
+
6
+ <% content_for(:pb_js) do %>
7
+ <%= javascript_tag do %>
8
+ document.addEventListener('DOMContentLoaded', function () {
9
+ document.querySelector('#example-form-validation').addEventListener('submit', function (e) {
10
+ if (e.target.querySelectorAll('[error]:not([error=""])').length > 0) e.preventDefault();
11
+ })
12
+ })
13
+ <% end %>
14
+ <% end %>
@@ -0,0 +1,60 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { Button, FixedConfirmationToast, PhoneNumberInput } from "../../";
3
+
4
+ const PhoneNumberInputValidation = (props) => {
5
+ const [formErrors, setFormErrors] = useState("");
6
+ const [showFormErrors, setShowFormErrors] = useState(false);
7
+ const [phoneNumber, setPhoneNumber] = useState("");
8
+ const [countryCode, setCountryCode] = useState("af");
9
+
10
+ const handleOnValidate = (valid) => {
11
+ setFormErrors(
12
+ valid ? "" : "Please correct the fields below and try again."
13
+ );
14
+ };
15
+
16
+ const handleOnChange = ({ iso2, number }) => {
17
+ setCountryCode(iso2);
18
+ setPhoneNumber(number);
19
+ };
20
+
21
+ const handleOnSubmit = (e) => {
22
+ if (showFormErrors) e.preventDefault()
23
+ }
24
+
25
+ useEffect(() => {
26
+ setShowFormErrors(formErrors.length > 0);
27
+ }, [formErrors]);
28
+
29
+ return (
30
+ <form
31
+ action=""
32
+ method="get"
33
+ onSubmit={handleOnSubmit}
34
+ >
35
+ {showFormErrors && (
36
+ <FixedConfirmationToast
37
+ marginBottom="md"
38
+ status="error"
39
+ text={formErrors}
40
+ />
41
+ )}
42
+ <PhoneNumberInput
43
+ error="Missing phone number."
44
+ id="validation"
45
+ initialCountry={countryCode}
46
+ onChange={handleOnChange}
47
+ onValidate={handleOnValidate}
48
+ required
49
+ value={phoneNumber}
50
+ {...props}
51
+ />
52
+ <Button
53
+ htmlType="submit"
54
+ text="Save Phone Number"
55
+ />
56
+ </form>
57
+ );
58
+ };
59
+
60
+ export default PhoneNumberInputValidation;