playbook_ui 12.26.1.pre.alpha.railsmultilevelimprovements842 → 12.27.0.pre.alpha.expandednotworking853

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) 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 +28 -3
  7. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_default.jsx +1 -0
  8. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.rb +0 -3
  9. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +109 -44
  10. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.html.erb +14 -0
  11. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.jsx +60 -0
  12. data/app/pb_kits/playbook/pb_phone_number_input/docs/example.yml +3 -1
  13. data/app/pb_kits/playbook/pb_phone_number_input/docs/index.js +1 -0
  14. data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.rb +6 -0
  15. data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.test.js +110 -62
  16. data/app/pb_kits/playbook/pb_rich_text_editor/_trix_styles.scss +2 -2
  17. data/app/pb_kits/playbook/pb_title/_title.scss +1 -1
  18. data/app/pb_kits/playbook/pb_title/_title.tsx +2 -3
  19. data/app/pb_kits/playbook/pb_title/docs/_title_light_weight.html.erb +1 -0
  20. data/app/pb_kits/playbook/pb_title/docs/_title_light_weight.jsx +6 -0
  21. data/app/pb_kits/playbook/pb_title/docs/_title_light_weight.md +1 -2
  22. data/app/pb_kits/playbook/pb_title/title.rb +3 -10
  23. data/app/pb_kits/playbook/pb_title/title.test.js +3 -3
  24. data/app/pb_kits/playbook/utilities/object.ts +3 -0
  25. data/dist/playbook-rails.js +4 -4
  26. data/lib/playbook/forms/builder/intl_telephone_field.rb +12 -0
  27. data/lib/playbook/forms/builder.rb +1 -0
  28. data/lib/playbook/version.rb +2 -2
  29. metadata +6 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 326f8eff50b791dc21ab3ec0bdb295adef8d14d3957277f4f92d8abdfe445686
4
- data.tar.gz: 0a9d5b6672174df0cb045cc3aea6b017d0e30f1a978df27e0529b7f1d3cb3472
3
+ metadata.gz: 3a910f0a34e4b15a05338890da54fbc4127569527ff821e0a1bc1d7798907b7b
4
+ data.tar.gz: 06da8f4e5abcae0b35ae9bb2bfcbc086fc334b92d8d0f18cdbbb61bd9e69cfbb
5
5
  SHA512:
6
- metadata.gz: 1b079256265e6bcdea4576a6930b527b944044f0064936376278147aad59cf1a5b311ba25632ca3878bb50e3fd62a33ccdf17cf805d7dd60eaece7d5d3e640fe
7
- data.tar.gz: fa05822858223b65f2a3a2755a1f7fcecea6946d7a1f31ea40a32ad10ebd5b3df128cc3736fdab16b6329ba283e50d201412a8d79c6027745a646d8c6c94ac05
6
+ metadata.gz: 7231015efe6dd12735a8b4ac07b6596b97ec320e7f13ce29ba67eccbd827f6ba0a3f63f52a76d5bcb29a429bbc9f653ffa0afea79d686976e1ad84b304aea0bc
7
+ data.tar.gz: 7f57205311d9ff7bb7f5df373d176fc921c917c88ca2fd3d8f413f10294fe214915a34c9dfa63dc57194312c70cb628ea50bb19df5e5b72c1510a1bcb7df2ec9
@@ -1,4 +1,6 @@
1
1
  // Manual Import -- Flatpickr Styles
2
+ @import "./tokens/typography";
3
+
2
4
  .flatpickr-calendar {
3
5
  background: transparent;
4
6
  opacity: 0;
@@ -295,7 +297,7 @@
295
297
  .flatpickr-current-month {
296
298
  font-size: 135%;
297
299
  line-height: inherit;
298
- font-weight: 300;
300
+ font-weight: $light;
299
301
  color: inherit;
300
302
  position: absolute;
301
303
  width: 75%;
@@ -310,7 +312,7 @@
310
312
  }
311
313
  .flatpickr-current-month span.cur-month {
312
314
  font-family: inherit;
313
- font-weight: 700;
315
+ font-weight: $bolder;
314
316
  color: inherit;
315
317
  display: inline-block;
316
318
  margin-left: 0.5ch;
@@ -341,7 +343,7 @@
341
343
  display: inline-block;
342
344
  font-size: inherit;
343
345
  font-family: inherit;
344
- font-weight: 300;
346
+ font-weight: $light;
345
347
  line-height: inherit;
346
348
  height: auto;
347
349
  border: 0;
@@ -371,7 +373,7 @@
371
373
  cursor: pointer;
372
374
  font-size: inherit;
373
375
  font-family: inherit;
374
- font-weight: 300;
376
+ font-weight: $light;
375
377
  height: auto;
376
378
  line-height: inherit;
377
379
  margin: -1px 0 0 0;
@@ -492,7 +494,7 @@ span.flatpickr-weekday {
492
494
  box-sizing: border-box;
493
495
  color: #393939;
494
496
  cursor: pointer;
495
- font-weight: 400;
497
+ font-weight: $regular;
496
498
  width: 14.2857143%;
497
499
  -webkit-flex-basis: 14.2857143%;
498
500
  -ms-flex-preferred-size: 14.2857143%;
@@ -707,11 +709,11 @@ span.flatpickr-weekday {
707
709
  appearance: textfield;
708
710
  }
709
711
  .flatpickr-time input.flatpickr-hour {
710
- font-weight: bold;
712
+ font-weight: $bolder;
711
713
  }
712
714
  .flatpickr-time input.flatpickr-minute,
713
715
  .flatpickr-time input.flatpickr-second {
714
- font-weight: 400;
716
+ font-weight: $regular;
715
717
  }
716
718
  .flatpickr-time input:focus {
717
719
  outline: 0;
@@ -723,7 +725,7 @@ span.flatpickr-weekday {
723
725
  float: left;
724
726
  line-height: inherit;
725
727
  color: #393939;
726
- font-weight: bold;
728
+ font-weight: $bolder;
727
729
  width: 2%;
728
730
  -webkit-user-select: none;
729
731
  -moz-user-select: none;
@@ -738,7 +740,7 @@ span.flatpickr-weekday {
738
740
  width: 18%;
739
741
  cursor: pointer;
740
742
  text-align: center;
741
- font-weight: 400;
743
+ font-weight: $regular;
742
744
  }
743
745
  .flatpickr-time input:hover,
744
746
  .flatpickr-time .flatpickr-am-pm:hover,
@@ -20,7 +20,7 @@
20
20
  color: $text_lt_default;
21
21
  cursor: pointer;
22
22
  display: inline-block;
23
- font-weight: 400;
23
+ font-weight: $regular;
24
24
  margin: 0.5px;
25
25
  justify-content: center;
26
26
  padding: 10px;
@@ -20,7 +20,7 @@
20
20
  color: $text_lt_default;
21
21
  cursor: pointer;
22
22
  display: inline-block;
23
- font-weight: 400;
23
+ font-weight: $regular;
24
24
  margin: 0.5px;
25
25
  justify-content: center;
26
26
  padding: 10px;
@@ -56,7 +56,7 @@ $form_pill_colors: (
56
56
  height: -moz-fit-content;
57
57
  .pb_form_pill_text, .pb_form_pill_close, .pb_form_pill_tag {
58
58
  font-size: 16px;
59
- font-weight: 400;
59
+ font-weight: $regular;
60
60
  }
61
61
  .pb_form_pill_text, .pb_form_pill_tag {
62
62
  line-height: 1.7;
@@ -51,8 +51,25 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
51
51
  const dropdownRef = useRef(null)
52
52
 
53
53
 
54
- //state for expanded property
55
- const [expanded, setExpanded] = useState([])
54
+ const getExpandedItems = (treeData: { [key: string]: string }[]) => {
55
+ let expandedItems: any[] = [];
56
+
57
+ const traverse = (items: string | any[]) => {
58
+ for (let i = 0; i < items.length; i++) {
59
+ const item = items[i];
60
+ if (item.expanded) {
61
+ expandedItems.push(item.id);
62
+ }
63
+ if (Array.isArray(item.children)) {
64
+ traverse(item.children);
65
+ }
66
+ }
67
+ }
68
+
69
+ traverse(treeData);
70
+ return expandedItems;
71
+ }
72
+
56
73
  //state for whether dropdown is open or closed
57
74
  const [isClosed, setIsClosed] = useState(true)
58
75
  //state from onchange for textinput, to use for filtering to create typeahead
@@ -63,6 +80,10 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
63
80
  const [formattedData, setFormattedData] = useState([])
64
81
  //state for return for default
65
82
  const [defaultReturn, setDefaultReturn] = useState([])
83
+ // Get expanded items from treeData.
84
+ const initialExpandedItems = getExpandedItems(treeData);
85
+ // Initialize state with expanded items.
86
+ const [expanded, setExpanded] = useState(initialExpandedItems);
66
87
 
67
88
 
68
89
  useEffect(() => {
@@ -153,11 +174,13 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
153
174
  return tree
154
175
  }
155
176
 
177
+
178
+
156
179
  //function to map over data and add parent_id + depth property to each item
157
180
  const addCheckedAndParentProperty = (
158
181
  treeData: { [key: string]: any }[],
159
182
  parent_id: string = null,
160
- depth: number = 0
183
+ depth: number = 0,
161
184
  ) => {
162
185
  if (!Array.isArray(treeData)) {
163
186
  return
@@ -295,6 +318,8 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
295
318
  )
296
319
  }
297
320
 
321
+
322
+
298
323
  return (
299
324
  <div {...ariaProps} {...dataProps} className={classes} id={id}>
300
325
  <div ref={dropdownRef} className='wrapper'>
@@ -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",
@@ -9,8 +9,6 @@ module Playbook
9
9
  default: []
10
10
  prop :return_all_selected, type: Playbook::Props::Boolean,
11
11
  default: false
12
- prop :return_complete_data, type: Playbook::Props::Boolean,
13
- default: false
14
12
 
15
13
  def classname
16
14
  generate_classname("pb_multi_level_select")
@@ -22,7 +20,6 @@ module Playbook
22
20
  name: name,
23
21
  treeData: tree_data,
24
22
  returnAllSelected: return_all_selected,
25
- returnCompleteData: return_complete_data,
26
23
  }
27
24
  end
28
25
  end
@@ -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