playbook_ui 12.26.1.pre.alpha.PLAY603datepickerquickpickinputpresetdropdown831 → 12.26.1.pre.alpha.PLAY860PhoneNumInputOptions836
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/pb_kits/playbook/pb_date_picker/_date_picker.scss +0 -26
- data/app/pb_kits/playbook/pb_date_picker/_date_picker.tsx +95 -102
- data/app/pb_kits/playbook/pb_date_picker/date_picker.html.erb +2 -18
- data/app/pb_kits/playbook/pb_date_picker/date_picker.rb +4 -15
- data/app/pb_kits/playbook/pb_date_picker/date_picker.test.js +1 -84
- data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.ts +4 -38
- data/app/pb_kits/playbook/pb_date_picker/docs/example.yml +0 -4
- data/app/pb_kits/playbook/pb_date_picker/docs/index.js +1 -3
- data/app/pb_kits/playbook/pb_date_picker/sass_partials/_calendar_input_icon.scss +2 -3
- data/app/pb_kits/playbook/pb_nav/_item.tsx +1 -1
- data/app/pb_kits/playbook/pb_nav/_subtle_mixin.scss +1 -1
- data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +132 -45
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_access_input_element.jsx +26 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_access_input_element.md +3 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_clear_field.jsx +30 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_clear_field.md +3 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.html.erb +14 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.jsx +60 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/example.yml +5 -1
- data/app/pb_kits/playbook/pb_phone_number_input/docs/index.js +3 -0
- data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.rb +6 -0
- data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.test.js +110 -62
- data/app/pb_kits/playbook/utilities/object.ts +3 -0
- data/dist/playbook-rails.js +7 -279
- data/lib/playbook/forms/builder/intl_telephone_field.rb +12 -0
- data/lib/playbook/forms/builder.rb +1 -0
- data/lib/playbook/version.rb +1 -1
- metadata +10 -11
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_rails.html.erb +0 -12
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_rails.md +0 -3
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_range_limit.html.erb +0 -12
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_range_limit.jsx +0 -18
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_range_limit.md +0 -1
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_react.jsx +0 -17
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_react.md +0 -1
- data/app/pb_kits/playbook/pb_date_picker/plugins/quickPick.tsx +0 -168
- data/app/pb_kits/playbook/pb_date_picker/sass_partials/_quick_pick_styles.scss +0 -75
@@ -1,12 +1,17 @@
|
|
1
|
-
import React, { useEffect, useRef, useState } from 'react'
|
1
|
+
import React, { forwardRef, useEffect, useRef, useState, useImperativeHandle } from 'react'
|
2
2
|
import classnames from 'classnames'
|
3
|
-
|
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
|
-
|
53
|
+
const countryData = window.intlTelInputGlobals.getCountryData()
|
44
54
|
|
45
55
|
for (let i = 0; i < countryData.length; i++) {
|
46
|
-
|
56
|
+
const country = countryData[i]
|
47
57
|
country.name = formatToGlobalCountryName(country.name)
|
48
58
|
}
|
49
59
|
}
|
@@ -54,7 +64,7 @@ const containOnlyNumbers = (value: string) => {
|
|
54
64
|
return /^[()+\-\ .\d]*$/g.test(value)
|
55
65
|
}
|
56
66
|
|
57
|
-
const PhoneNumberInput = (props: PhoneNumberInputProps) => {
|
67
|
+
const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.MutableRefObject<unknown>) => {
|
58
68
|
const {
|
59
69
|
aria = {},
|
60
70
|
className,
|
@@ -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,100 @@ 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
|
+
/*
|
115
|
+
useImperativeHandle exposes the kit's input element to a parent component via a ref.
|
116
|
+
See the Playbook docs for use cases.
|
117
|
+
Read: https://react.dev/reference/react/useImperativeHandle
|
118
|
+
*/
|
119
|
+
useImperativeHandle(ref, () => {
|
120
|
+
return {
|
121
|
+
clearField() {
|
122
|
+
setInputValue("")
|
123
|
+
setError("")
|
124
|
+
},
|
125
|
+
inputNode() {
|
126
|
+
return inputRef.current
|
127
|
+
}
|
128
|
+
}
|
129
|
+
})
|
130
|
+
|
131
|
+
const showFormattedError = (reason = '') => {
|
132
|
+
const countryName = itiInit.getSelectedCountryData().name
|
133
|
+
const reasonText = reason.length > 0 ? ` (${reason})` : ''
|
134
|
+
setError(`Invalid ${countryName} phone number${reasonText}`)
|
135
|
+
return true
|
136
|
+
}
|
137
|
+
|
94
138
|
const validateTooLongNumber = (itiInit: any) => {
|
95
|
-
|
139
|
+
if (!itiInit) return
|
140
|
+
if (itiInit.getValidationError() === ValidationError.TooLong) {
|
141
|
+
return showFormattedError('too long')
|
142
|
+
} else {
|
143
|
+
setError('')
|
144
|
+
}
|
145
|
+
}
|
96
146
|
|
97
|
-
|
98
|
-
|
99
|
-
|
147
|
+
const validateTooShortNumber = (itiInit: any) => {
|
148
|
+
if (!itiInit) return
|
149
|
+
if (itiInit.getValidationError() === ValidationError.TooShort) {
|
150
|
+
return showFormattedError('too short')
|
100
151
|
} else {
|
101
|
-
|
152
|
+
if (inputValue.length === 1) {
|
153
|
+
return showFormattedError('too short')
|
154
|
+
} else {
|
155
|
+
setError('')
|
156
|
+
}
|
102
157
|
}
|
103
158
|
}
|
104
159
|
|
105
|
-
const
|
106
|
-
|
160
|
+
const validateOnlyNumbers = (itiInit: any) => {
|
161
|
+
if (!itiInit) return
|
162
|
+
if (inputValue && !containOnlyNumbers(inputValue)) {
|
163
|
+
return showFormattedError('enter numbers only')
|
164
|
+
}
|
165
|
+
}
|
107
166
|
|
108
|
-
|
109
|
-
|
110
|
-
|
167
|
+
const validateUnhandledError = (itiInit: any) => {
|
168
|
+
if (!required || !itiInit) return
|
169
|
+
if (itiInit.getValidationError() === ValidationError.SomethingWentWrong) {
|
170
|
+
if (inputValue.length === 1) {
|
171
|
+
return showFormattedError('too short')
|
172
|
+
} else if (inputValue.length === 0) {
|
173
|
+
setError('Missing phone number')
|
174
|
+
return true
|
175
|
+
} else {
|
176
|
+
return showFormattedError()
|
177
|
+
}
|
111
178
|
}
|
112
179
|
}
|
113
180
|
|
114
|
-
const
|
115
|
-
if (
|
116
|
-
|
181
|
+
const validateMissingAreaCode = (itiInit: any) => {
|
182
|
+
if (!required || !itiInit) return
|
183
|
+
if (itiInit.getValidationError() === ValidationError.MissingAreaCode) {
|
184
|
+
showFormattedError('missing area code')
|
185
|
+
return true
|
117
186
|
}
|
118
187
|
}
|
119
188
|
|
120
189
|
const validateErrors = () => {
|
121
|
-
|
122
|
-
validateOnlyNumbers()
|
190
|
+
if (itiInit) isValid(itiInit.isValidNumber())
|
191
|
+
if (validateOnlyNumbers(itiInit)) return
|
192
|
+
if (validateTooLongNumber(itiInit)) return
|
193
|
+
if (validateTooShortNumber(itiInit)) return
|
194
|
+
if (validateUnhandledError(itiInit)) return
|
195
|
+
if (validateMissingAreaCode(itiInit)) return
|
123
196
|
}
|
124
197
|
|
125
198
|
const getCurrentSelectedData = (itiInit: any, inputValue: string) => {
|
@@ -128,7 +201,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps) => {
|
|
128
201
|
|
129
202
|
const handleOnChange = (evt: React.ChangeEvent<HTMLInputElement>) => {
|
130
203
|
setInputValue(evt.target.value)
|
131
|
-
validateTooLongNumber(itiInit)
|
132
204
|
const phoneNumberData = getCurrentSelectedData(itiInit, evt.target.value)
|
133
205
|
setSelectedData(phoneNumberData)
|
134
206
|
onChange(phoneNumberData)
|
@@ -137,25 +209,24 @@ const PhoneNumberInput = (props: PhoneNumberInputProps) => {
|
|
137
209
|
|
138
210
|
// Separating Concerns as React Docs Recommend
|
139
211
|
// This also Fixes things for our react_component rendering on the Rails Side
|
140
|
-
useEffect(
|
141
|
-
formatAllCountries()
|
142
|
-
}, [])
|
212
|
+
useEffect(formatAllCountries, [])
|
143
213
|
|
144
214
|
useEffect(() => {
|
145
|
-
const telInputInit =
|
215
|
+
const telInputInit = intlTelInput(inputRef.current, {
|
146
216
|
separateDialCode: true,
|
147
217
|
preferredCountries,
|
148
218
|
allowDropdown: !disabled,
|
219
|
+
autoInsertDialCode: false,
|
149
220
|
initialCountry,
|
150
221
|
onlyCountries,
|
151
|
-
|
152
|
-
)
|
222
|
+
utilsScript: "intl-tel-input/build/js/utils.js"
|
223
|
+
})
|
153
224
|
|
154
225
|
inputRef.current.addEventListener("countrychange", (evt: Event) => {
|
155
|
-
validateTooLongNumber(telInputInit)
|
156
226
|
const phoneNumberData = getCurrentSelectedData(telInputInit, (evt.target as HTMLInputElement).value)
|
157
227
|
setSelectedData(phoneNumberData)
|
158
228
|
onChange(phoneNumberData)
|
229
|
+
validateErrors()
|
159
230
|
})
|
160
231
|
|
161
232
|
inputRef.current.addEventListener("open:countrydropdown", () => setDropDownIsOpen(true))
|
@@ -164,24 +235,40 @@ const PhoneNumberInput = (props: PhoneNumberInputProps) => {
|
|
164
235
|
setItiInit(telInputInit)
|
165
236
|
}, [])
|
166
237
|
|
238
|
+
let textInputProps: {[key: string]: any} = {
|
239
|
+
className: dropDownIsOpen ? 'dropdown_open' : '',
|
240
|
+
dark,
|
241
|
+
"data-phone-number": JSON.stringify(selectedData),
|
242
|
+
disabled,
|
243
|
+
error,
|
244
|
+
type: 'tel',
|
245
|
+
id,
|
246
|
+
label,
|
247
|
+
name,
|
248
|
+
onBlur: validateErrors,
|
249
|
+
onChange: handleOnChange,
|
250
|
+
value: inputValue
|
251
|
+
}
|
252
|
+
|
253
|
+
let wrapperProps: Record<string, unknown> = { className: classes }
|
254
|
+
|
255
|
+
if (!isEmpty(aria)) textInputProps = {...textInputProps, ...ariaProps}
|
256
|
+
if (!isEmpty(data)) wrapperProps = {...wrapperProps, ...dataProps}
|
257
|
+
if (required) textInputProps.required = true
|
258
|
+
|
167
259
|
return (
|
168
|
-
<div {...
|
260
|
+
<div {...wrapperProps}>
|
169
261
|
<TextInput
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
name={name}
|
178
|
-
onBlur={() => validateErrors()}
|
179
|
-
onChange={handleOnChange}
|
180
|
-
ref={inputRef}
|
181
|
-
value={inputValue}
|
262
|
+
ref={
|
263
|
+
inputNode => {
|
264
|
+
ref ? ref.current = inputNode : null
|
265
|
+
inputRef.current = inputNode
|
266
|
+
}
|
267
|
+
}
|
268
|
+
{...textInputProps}
|
182
269
|
/>
|
183
270
|
</div>
|
184
271
|
)
|
185
272
|
}
|
186
273
|
|
187
|
-
export default PhoneNumberInput
|
274
|
+
export default forwardRef(PhoneNumberInput)
|
data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_access_input_element.jsx
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
import React, { useEffect, useRef } from 'react'
|
2
|
+
import { Body, PhoneNumberInput } from '../..'
|
3
|
+
|
4
|
+
const PhoneNumberInputAccessInputElement = (props) => {
|
5
|
+
// 1. Create a ref - this accesses the kit's input element.
|
6
|
+
const ref = useRef()
|
7
|
+
|
8
|
+
// 2. Add any event listener to ref.current.inputNode() inside a useEffect hook.
|
9
|
+
useEffect(() => {
|
10
|
+
ref.current.inputNode().addEventListener("click", () => alert("Clicked!"))
|
11
|
+
})
|
12
|
+
|
13
|
+
// 3. Pass the ref to the ref prop.
|
14
|
+
return (
|
15
|
+
<>
|
16
|
+
<Body text="Click the input field below:" />
|
17
|
+
<PhoneNumberInput
|
18
|
+
id="access-input-element"
|
19
|
+
ref={ref}
|
20
|
+
{...props}
|
21
|
+
/>
|
22
|
+
</>
|
23
|
+
)
|
24
|
+
}
|
25
|
+
|
26
|
+
export default PhoneNumberInputAccessInputElement
|
data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_access_input_element.md
ADDED
@@ -0,0 +1,3 @@
|
|
1
|
+
To access the kit's input element attributes or add event listeners, create a `ref` inside your parent component, pass it to the kit's `ref` prop, and use `ref.current.inputNode()` with your desired attribute or event listener inside a `useEffect` hook. `useEffect` is necessary because the `ref` will be initially `undefined`.
|
2
|
+
|
3
|
+
`inputNode()` is a custom function inside the kit that returns the input DOM element and its attributes. For example, to get the `name` attribute, use `ref.current.inputNode().name`
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import React, { useRef } from 'react'
|
2
|
+
import { Button, PhoneNumberInput } from '../..'
|
3
|
+
|
4
|
+
const PhoneNumberInputClearField = (props) => {
|
5
|
+
// 1. Create a ref - this accesses the kit's input element.
|
6
|
+
const ref = useRef()
|
7
|
+
|
8
|
+
// 2. Use clearField() to clear the field.
|
9
|
+
const handleClick = () => {
|
10
|
+
ref.current.clearField()
|
11
|
+
}
|
12
|
+
|
13
|
+
// 3. Pass the ref to the ref prop.
|
14
|
+
return (
|
15
|
+
<>
|
16
|
+
<PhoneNumberInput
|
17
|
+
id="clear-field"
|
18
|
+
ref={ref}
|
19
|
+
{...props}
|
20
|
+
/>
|
21
|
+
|
22
|
+
<Button
|
23
|
+
onClick={handleClick}
|
24
|
+
text="Clear the Input Field"
|
25
|
+
/>
|
26
|
+
</>
|
27
|
+
)
|
28
|
+
}
|
29
|
+
|
30
|
+
export default PhoneNumberInputClearField
|
@@ -0,0 +1,3 @@
|
|
1
|
+
To clear a number inside the input element, create a `ref` inside your parent component, pass it to the kit's `ref` prop, and use `ref.current.clearField()`.
|
2
|
+
|
3
|
+
`clearField()` is a custom function inside the kit to clear numbers and the error message while still providing validation.
|
@@ -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,13 @@ 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
|
9
|
+
- phone_number_input_clear_field: Clearing the Input Field
|
10
|
+
- phone_number_input_access_input_element: Accessing the Input Element
|
8
11
|
|
9
12
|
rails:
|
10
13
|
- phone_number_input_default: Default
|
11
14
|
- phone_number_input_preferred_countries: Preferred Countries
|
12
15
|
- phone_number_input_initial_country: Initial Country
|
13
|
-
- phone_number_input_only_countries: Limited Countries
|
16
|
+
- phone_number_input_only_countries: Limited Countries
|
17
|
+
- phone_number_input_validation: Form Validation
|
@@ -2,3 +2,6 @@ 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'
|
6
|
+
export { default as PhoneNumberInputClearField } from './_phone_number_input_clear_field'
|
7
|
+
export { default as PhoneNumberInputAccessInputElement } from './_phone_number_input_access_input_element'
|
@@ -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
|
@@ -1,74 +1,122 @@
|
|
1
|
-
import React from
|
2
|
-
import { render, screen } from
|
3
|
-
import PhoneNumberInput from
|
1
|
+
import React from "react";
|
2
|
+
import { render, screen } from "../utilities/test-utils";
|
3
|
+
import PhoneNumberInput from "./_phone_number_input";
|
4
4
|
|
5
|
-
const testId = "phoneNumberInput"
|
5
|
+
const testId = "phoneNumberInput";
|
6
6
|
|
7
|
-
test(
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
test("should be disabled", () => {
|
8
|
+
const props = {
|
9
|
+
disabled: true,
|
10
|
+
id: testId,
|
11
|
+
};
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
})
|
13
|
+
render(<PhoneNumberInput {...props} />);
|
14
|
+
const kit = screen.getByRole("textbox");
|
15
|
+
expect(kit).toBeDisabled();
|
16
|
+
});
|
17
17
|
|
18
|
-
test(
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
test("should be enabled by default", () => {
|
19
|
+
const props = {
|
20
|
+
id: testId,
|
21
|
+
};
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
})
|
23
|
+
render(<PhoneNumberInput {...props} />);
|
24
|
+
const kit = screen.getByRole("textbox");
|
25
|
+
expect(kit).not.toBeDisabled();
|
26
|
+
});
|
27
27
|
|
28
|
-
test(
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
28
|
+
test("should have label", () => {
|
29
|
+
const label = "Phone Number";
|
30
|
+
const props = {
|
31
|
+
id: testId,
|
32
|
+
label,
|
33
|
+
};
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
})
|
35
|
+
render(<PhoneNumberInput {...props} />);
|
36
|
+
const kit = screen.getByText(label);
|
37
|
+
expect(kit).toBeInTheDocument();
|
38
|
+
});
|
39
39
|
|
40
|
-
test(
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
40
|
+
test("should pass data prop", () => {
|
41
|
+
const props = {
|
42
|
+
data: { testid: testId },
|
43
|
+
id: testId,
|
44
|
+
};
|
45
|
+
|
46
|
+
render(<PhoneNumberInput {...props} />);
|
47
|
+
const kit = screen.getByTestId(testId);
|
48
|
+
expect(kit).toBeInTheDocument();
|
49
|
+
});
|
50
|
+
|
51
|
+
test("should pass className prop", () => {
|
52
|
+
const className = "custom-class-name";
|
53
|
+
const props = {
|
54
|
+
className,
|
55
|
+
data: { testid: testId },
|
56
|
+
id: testId,
|
57
|
+
};
|
58
|
+
|
59
|
+
render(<PhoneNumberInput {...props} />);
|
60
|
+
const kit = screen.getByTestId(testId);
|
61
|
+
expect(kit).toHaveClass(className);
|
62
|
+
});
|
63
|
+
|
64
|
+
test("should pass value prop", () => {
|
65
|
+
const value = "1234567890";
|
66
|
+
const props = {
|
67
|
+
id: testId,
|
68
|
+
value,
|
69
|
+
};
|
45
70
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
})
|
71
|
+
render(<PhoneNumberInput {...props} />);
|
72
|
+
const kit = screen.getByRole("textbox");
|
73
|
+
expect(kit).toHaveDisplayValue(value);
|
74
|
+
});
|
50
75
|
|
51
|
-
test
|
52
|
-
|
76
|
+
//TODO: test required field presence
|
77
|
+
test("should pass required prop", () => {
|
78
|
+
const props = {
|
79
|
+
id: testId,
|
80
|
+
};
|
81
|
+
|
82
|
+
render(
|
83
|
+
<PhoneNumberInput
|
84
|
+
required
|
85
|
+
{...props}
|
86
|
+
/>
|
87
|
+
);
|
88
|
+
const kit = screen.getByRole("textbox");
|
89
|
+
expect(kit).toHaveAttribute("required");
|
90
|
+
});
|
91
|
+
|
92
|
+
test("should have error attribute when invalid", () => {
|
53
93
|
const props = {
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
94
|
+
id: testId,
|
95
|
+
error: "Something isn't right here."
|
96
|
+
};
|
97
|
+
|
98
|
+
render(
|
99
|
+
<PhoneNumberInput
|
100
|
+
{...props}
|
101
|
+
/>
|
102
|
+
);
|
103
|
+
const kit = screen.getByRole("textbox");
|
104
|
+
expect(kit).toHaveAttribute("error");
|
105
|
+
});
|
106
|
+
|
107
|
+
test("should trigger callback", () => {
|
108
|
+
const handleOnValidate = jest.fn((valid) => valid)
|
109
|
+
|
66
110
|
const props = {
|
67
|
-
|
68
|
-
|
69
|
-
}
|
70
|
-
|
71
|
-
render(
|
72
|
-
|
73
|
-
|
74
|
-
|
111
|
+
id: testId,
|
112
|
+
onValidate: handleOnValidate
|
113
|
+
};
|
114
|
+
|
115
|
+
render(
|
116
|
+
<PhoneNumberInput
|
117
|
+
{...props}
|
118
|
+
/>
|
119
|
+
);
|
120
|
+
|
121
|
+
expect(handleOnValidate).toBeCalledWith(true)
|
122
|
+
});
|