playbook_ui 12.26.1 → 12.27.0

Sign up to get free protection for your applications and to get access to all the features.
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 +134 -125
  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 +7 -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 +8 -2
@@ -72,15 +72,18 @@ const MultiLevelSelectDefault = (props) => {
72
72
  return (
73
73
  <div>
74
74
  <MultiLevelSelect
75
- id="multiselect-default"
75
+ id='multiselect-default'
76
76
  onSelect={(selectedNodes) =>
77
- console.log("Selected Items", selectedNodes)
78
- }
77
+ console.log(
78
+ "Selected Items",
79
+ selectedNodes
80
+ )
81
+ }
79
82
  treeData={treeData}
80
83
  {...props}
81
84
  />
82
85
  </div>
83
- );
86
+ )
84
87
  };
85
88
 
86
89
  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;
@@ -5,9 +5,11 @@ examples:
5
5
  - phone_number_input_preferred_countries: Preferred Countries
6
6
  - phone_number_input_initial_country: Initial Country
7
7
  - phone_number_input_only_countries: Limited Countries
8
+ - phone_number_input_validation: Form Validation
8
9
 
9
10
  rails:
10
11
  - phone_number_input_default: Default
11
12
  - phone_number_input_preferred_countries: Preferred Countries
12
13
  - phone_number_input_initial_country: Initial Country
13
- - phone_number_input_only_countries: Limited Countries
14
+ - phone_number_input_only_countries: Limited Countries
15
+ - phone_number_input_validation: Form Validation
@@ -2,3 +2,4 @@ export { default as PhoneNumberInputDefault } from './_phone_number_input_defaul
2
2
  export { default as PhoneNumberInputPreferredCountries } from './_phone_number_input_preferred_countries'
3
3
  export { default as PhoneNumberInputInitialCountry } from './_phone_number_input_initial_country'
4
4
  export { default as PhoneNumberInputOnlyCountries } from './_phone_number_input_only_countries'
5
+ export { default as PhoneNumberInputValidation } from './_phone_number_input_validation'
@@ -5,6 +5,8 @@ module Playbook
5
5
  class PhoneNumberInput < Playbook::KitBase
6
6
  prop :disabled, type: Playbook::Props::Boolean,
7
7
  default: false
8
+ prop :required, type: Playbook::Props::Boolean,
9
+ default: false
8
10
  prop :initial_country, type: Playbook::Props::String,
9
11
  default: ""
10
12
  prop :label, type: Playbook::Props::String,
@@ -15,6 +17,8 @@ module Playbook
15
17
  default: []
16
18
  prop :preferred_countries, type: Playbook::Props::Array,
17
19
  default: []
20
+ prop :error, type: Playbook::Props::String,
21
+ default: ""
18
22
  prop :value, type: Playbook::Props::String,
19
23
  default: ""
20
24
 
@@ -27,11 +31,13 @@ module Playbook
27
31
  id: id,
28
32
  dark: dark,
29
33
  disabled: disabled,
34
+ error: error,
30
35
  initialCountry: initial_country,
31
36
  label: label,
32
37
  name: name,
33
38
  onlyCountries: only_countries,
34
39
  preferredCountries: preferred_countries,
40
+ required: required,
35
41
  value: value,
36
42
  }
37
43
  end