playbook_ui 12.24.0.pre.alpha.play824753 → 12.24.0.pre.alpha.railsmultilevelimprovements739

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f2e3e2044042e62d066d48c17d7135165b8c174388b509a208f92f959cc6acf3
4
- data.tar.gz: 40d8b2352a9fab721ca2e653bbd6adecba555feafc95c216f8186b04b7daed75
3
+ metadata.gz: ca13d82593114ee6fa9797c521e2e7da4b675f48923d7ad41c74fc14d5d224d6
4
+ data.tar.gz: 8f1309ad083e6e90c2a6278b7f3afb4cdcea79e06dcfbb7481ae9273d4f9f6ec
5
5
  SHA512:
6
- metadata.gz: 9ef16718ea387d46da9e0c705c748e31c0283dac477d65d5ed7102ccdcad8c6de0d016a958c8e95f4500cba0535e01ce58408944c1e18c66b295bdd163477f93
7
- data.tar.gz: c88b5a2fbeeacc2cb1d0cf33a9c46643ffecbcd617197df189167213ed8593c82b00c4c23d13a4c70619a614cf5359160d98f336c9efee81db709590d5f5fda3
6
+ metadata.gz: 8f1e03060911871628a3c79d92bb7c01fbedf754b705a2feefe065082b03dca4bde92bec88d390a508639a1e5ac1f157d0130775cf6056d55260beae9b149c18
7
+ data.tar.gz: 0ce1d542f2d0a41150fb52757501f9423392495c0554b5757330c178db347168973bb55f41fd86e017dee8475d27545f58332d60609667ec727d17da63241583
@@ -1,16 +1,11 @@
1
- import React, { forwardRef, useEffect, useRef, useState } from 'react'
1
+ import React, { useEffect, useRef, useState } from 'react'
2
2
  import classnames from 'classnames'
3
-
4
- import intlTelInput from 'intl-tel-input'
5
- import 'intl-tel-input/build/css/intlTelInput.css'
6
- import 'intl-tel-input/build/js/utils.js'
7
-
8
3
  import { buildAriaProps, buildCss, buildDataProps } from '../utilities/props'
9
4
  import { globalProps } from '../utilities/globalProps'
10
-
5
+ import intlTelInput from 'intl-tel-input'
6
+ import 'intl-tel-input/build/css/intlTelInput.css'
11
7
  import TextInput from '../pb_text_input/_text_input'
12
- import { Callback } from '../types'
13
- import { isEmpty } from '../utilities/object'
8
+ import 'intl-tel-input/build/js/utils.js'
14
9
 
15
10
  declare global {
16
11
  interface Window {
@@ -24,25 +19,20 @@ type PhoneNumberInputProps = {
24
19
  dark?: boolean,
25
20
  data?: { [key: string]: string },
26
21
  disabled?: boolean,
27
- error?: string,
28
22
  id?: string,
29
23
  initialCountry?: string,
30
24
  isValid?: (valid: boolean) => void,
31
25
  label?: string,
32
26
  name?: string,
33
27
  onChange?: (e: React.FormEvent<HTMLInputElement>) => void,
34
- onValidate?: Callback<boolean, void>,
35
28
  onlyCountries: string[],
36
29
  preferredCountries?: string[],
37
- required?: boolean,
38
30
  value?: string,
39
31
  }
40
32
 
41
33
  enum ValidationError {
42
34
  TooShort = 2,
43
35
  TooLong = 3,
44
- MissingAreaCode = 4,
45
- SomethingWentWrong = -99
46
36
  }
47
37
 
48
38
  const formatToGlobalCountryName = (countryName: string) => {
@@ -50,10 +40,10 @@ const formatToGlobalCountryName = (countryName: string) => {
50
40
  }
51
41
 
52
42
  const formatAllCountries = () => {
53
- const countryData = window.intlTelInputGlobals.getCountryData()
43
+ let countryData = window.intlTelInputGlobals.getCountryData()
54
44
 
55
45
  for (let i = 0; i < countryData.length; i++) {
56
- const country = countryData[i]
46
+ let country = countryData[i]
57
47
  country.name = formatToGlobalCountryName(country.name)
58
48
  }
59
49
  }
@@ -81,9 +71,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps) => {
81
71
  onChange = () => {
82
72
  void 0
83
73
  },
84
- onValidate = () => null,
85
74
  onlyCountries = [],
86
- required = false,
87
75
  preferredCountries = [],
88
76
  value = "",
89
77
  } = props
@@ -99,84 +87,39 @@ const PhoneNumberInput = (props: PhoneNumberInputProps) => {
99
87
  const inputRef = useRef<HTMLInputElement>()
100
88
  const [inputValue, setInputValue] = useState(value)
101
89
  const [itiInit, setItiInit] = useState<any>()
102
- const [error, setError] = useState(props.error)
90
+ const [error, setError] = useState('')
103
91
  const [dropDownIsOpen, setDropDownIsOpen] = useState(false)
104
92
  const [selectedData, setSelectedData] = useState()
105
93
 
106
- useEffect(() => {
107
- if (error?.length > 0) {
108
- onValidate(false)
109
- } else {
110
- onValidate(true)
111
- }
112
- }, [error, onValidate])
113
-
114
- if (itiInit) isValid(itiInit.isValidNumber())
115
-
116
- const showFormattedError = (reason = '') => {
117
- const countryName = itiInit.getSelectedCountryData().name
118
- const reasonText = reason.length > 0 ? ` (${reason})` : ''
119
- setError(`Invalid ${countryName} phone number${reasonText}`)
120
- return true
121
- }
122
-
123
94
  const validateTooLongNumber = (itiInit: any) => {
124
- if (!itiInit) return
125
- if (itiInit.getValidationError() === ValidationError.TooLong) {
126
- return showFormattedError('too long')
127
- } else {
128
- setError('')
129
- }
130
- }
95
+ const error = itiInit.getValidationError()
131
96
 
132
- const validateTooShortNumber = (itiInit: any) => {
133
- if (!itiInit) return
134
- if (itiInit.getValidationError() === ValidationError.TooShort) {
135
- return showFormattedError('too short')
97
+ if (error === ValidationError.TooLong) {
98
+ const countryName = itiInit.getSelectedCountryData().name
99
+ setError(`Invalid ${countryName} phone number (too long)`)
136
100
  } else {
137
- if (inputValue.length === 1) {
138
- return showFormattedError('too short')
139
- } else {
140
- setError('')
141
- }
101
+ setError("")
142
102
  }
143
103
  }
144
104
 
145
- const validateOnlyNumbers = (itiInit: any) => {
146
- if (!itiInit) return
147
- if (inputValue && !containOnlyNumbers(inputValue)) {
148
- return showFormattedError('enter numbers only')
149
- }
150
- }
105
+ const validateTooShortNumber = () => {
106
+ const error = itiInit.getValidationError()
151
107
 
152
- const validateUnhandledError = (itiInit: any) => {
153
- if (!required || !itiInit) return
154
- if (itiInit.getValidationError() === ValidationError.SomethingWentWrong) {
155
- if (inputValue.length === 1) {
156
- return showFormattedError('too short')
157
- } else if (inputValue.length === 0) {
158
- setError('Missing phone number')
159
- return true
160
- } else {
161
- return showFormattedError()
162
- }
108
+ if (error === ValidationError.TooShort) {
109
+ const countryName = itiInit.getSelectedCountryData().name
110
+ setError(`Invalid ${countryName} phone number (too short)`)
163
111
  }
164
112
  }
165
113
 
166
- const validateMissingAreaCode = (itiInit: any) => {
167
- if (!required || !itiInit) return
168
- if (itiInit.getValidationError() === ValidationError.MissingAreaCode) {
169
- showFormattedError('missing area code')
170
- return true
114
+ const validateOnlyNumbers = () => {
115
+ if (inputValue && !containOnlyNumbers(inputValue)) {
116
+ setError("Invalid phone number. Enter numbers only.")
171
117
  }
172
118
  }
173
119
 
174
120
  const validateErrors = () => {
175
- if (validateOnlyNumbers(itiInit)) return
176
- if (validateTooLongNumber(itiInit)) return
177
- if (validateTooShortNumber(itiInit)) return
178
- if (validateUnhandledError(itiInit)) return
179
- if (validateMissingAreaCode(itiInit)) return
121
+ validateTooShortNumber()
122
+ validateOnlyNumbers()
180
123
  }
181
124
 
182
125
  const getCurrentSelectedData = (itiInit: any, inputValue: string) => {
@@ -185,31 +128,34 @@ const PhoneNumberInput = (props: PhoneNumberInputProps) => {
185
128
 
186
129
  const handleOnChange = (evt: React.ChangeEvent<HTMLInputElement>) => {
187
130
  setInputValue(evt.target.value)
131
+ validateTooLongNumber(itiInit)
188
132
  const phoneNumberData = getCurrentSelectedData(itiInit, evt.target.value)
189
133
  setSelectedData(phoneNumberData)
190
134
  onChange(phoneNumberData)
135
+ isValid(itiInit.isValidNumber())
191
136
  }
192
137
 
193
138
  // Separating Concerns as React Docs Recommend
194
139
  // This also Fixes things for our react_component rendering on the Rails Side
195
- useEffect(formatAllCountries, [])
140
+ useEffect(() => {
141
+ formatAllCountries()
142
+ }, [])
196
143
 
197
144
  useEffect(() => {
198
- const telInputInit = intlTelInput(inputRef.current, {
145
+ const telInputInit = new intlTelInput(inputRef.current, {
199
146
  separateDialCode: true,
200
147
  preferredCountries,
201
148
  allowDropdown: !disabled,
202
- autoInsertDialCode: false,
203
149
  initialCountry,
204
150
  onlyCountries,
205
- utilsScript: "https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/18.1.6/js/utils.min.js"
206
- })
151
+ }
152
+ )
207
153
 
208
154
  inputRef.current.addEventListener("countrychange", (evt: Event) => {
155
+ validateTooLongNumber(telInputInit)
209
156
  const phoneNumberData = getCurrentSelectedData(telInputInit, (evt.target as HTMLInputElement).value)
210
157
  setSelectedData(phoneNumberData)
211
158
  onChange(phoneNumberData)
212
- validateErrors()
213
159
  })
214
160
 
215
161
  inputRef.current.addEventListener("open:countrydropdown", () => setDropDownIsOpen(true))
@@ -218,35 +164,24 @@ const PhoneNumberInput = (props: PhoneNumberInputProps) => {
218
164
  setItiInit(telInputInit)
219
165
  }, [])
220
166
 
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
-
242
167
  return (
243
- <div {...wrapperProps}>
168
+ <div {...ariaProps} {...dataProps} className={classes}>
244
169
  <TextInput
245
- ref={inputRef}
246
- {...textInputProps}
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}
247
182
  />
248
183
  </div>
249
184
  )
250
185
  }
251
186
 
252
- export default forwardRef(PhoneNumberInput)
187
+ export default PhoneNumberInput
@@ -5,11 +5,9 @@ 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
8
 
10
9
  rails:
11
10
  - phone_number_input_default: Default
12
11
  - phone_number_input_preferred_countries: Preferred Countries
13
12
  - phone_number_input_initial_country: Initial Country
14
- - phone_number_input_only_countries: Limited Countries
15
- - phone_number_input_validation: Form Validation
13
+ - phone_number_input_only_countries: Limited Countries
@@ -2,4 +2,3 @@ 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,8 +5,6 @@ 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
10
8
  prop :initial_country, type: Playbook::Props::String,
11
9
  default: ""
12
10
  prop :label, type: Playbook::Props::String,
@@ -17,8 +15,6 @@ module Playbook
17
15
  default: []
18
16
  prop :preferred_countries, type: Playbook::Props::Array,
19
17
  default: []
20
- prop :error, type: Playbook::Props::String,
21
- default: ""
22
18
  prop :value, type: Playbook::Props::String,
23
19
  default: ""
24
20
 
@@ -31,13 +27,11 @@ module Playbook
31
27
  id: id,
32
28
  dark: dark,
33
29
  disabled: disabled,
34
- error: error,
35
30
  initialCountry: initial_country,
36
31
  label: label,
37
32
  name: name,
38
33
  onlyCountries: only_countries,
39
34
  preferredCountries: preferred_countries,
40
- required: required,
41
35
  value: value,
42
36
  }
43
37
  end
@@ -1,122 +1,74 @@
1
- import React from "react";
2
- import { render, screen } from "../utilities/test-utils";
3
- import PhoneNumberInput from "./_phone_number_input";
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("should be disabled", () => {
8
- const props = {
9
- disabled: true,
10
- id: testId,
11
- };
12
-
13
- render(<PhoneNumberInput {...props} />);
14
- const kit = screen.getByRole("textbox");
15
- expect(kit).toBeDisabled();
16
- });
17
-
18
- test("should be enabled by default", () => {
19
- const props = {
20
- id: testId,
21
- };
22
-
23
- render(<PhoneNumberInput {...props} />);
24
- const kit = screen.getByRole("textbox");
25
- expect(kit).not.toBeDisabled();
26
- });
27
-
28
- test("should have label", () => {
29
- const label = "Phone Number";
30
- const props = {
31
- id: testId,
32
- label,
33
- };
34
-
35
- render(<PhoneNumberInput {...props} />);
36
- const kit = screen.getByText(label);
37
- expect(kit).toBeInTheDocument();
38
- });
39
-
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
- };
7
+ test('should be disabled', () => {
8
+ const props = {
9
+ disabled: true,
10
+ id: testId,
11
+ }
70
12
 
71
- render(<PhoneNumberInput {...props} />);
72
- const kit = screen.getByRole("textbox");
73
- expect(kit).toHaveDisplayValue(value);
74
- });
13
+ render(<PhoneNumberInput {...props} />)
14
+ const kit = screen.getByRole("textbox")
15
+ expect(kit).toBeDisabled()
16
+ })
75
17
 
76
- //TODO: test required field presence
77
- test("should pass required prop", () => {
78
- const props = {
79
- id: testId,
80
- };
18
+ test('should be enabled by default', () => {
19
+ const props = {
20
+ id: testId,
21
+ }
81
22
 
82
- render(
83
- <PhoneNumberInput
84
- required
85
- {...props}
86
- />
87
- );
88
- const kit = screen.getByRole("textbox");
89
- expect(kit).toHaveAttribute("required");
90
- });
23
+ render(<PhoneNumberInput {...props} />)
24
+ const kit = screen.getByRole("textbox")
25
+ expect(kit).not.toBeDisabled()
26
+ })
91
27
 
92
- test("should have error attribute when invalid", () => {
28
+ test('should have label', () => {
29
+ const label = 'Phone Number'
93
30
  const props = {
94
- id: testId,
95
- error: "Something isn't right here."
96
- };
31
+ id: testId,
32
+ label,
33
+ }
97
34
 
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)
35
+ render(<PhoneNumberInput {...props} />)
36
+ const kit = screen.getByText(label)
37
+ expect(kit).toBeInTheDocument()
38
+ })
109
39
 
40
+ test('should pass data prop', () => {
110
41
  const props = {
111
- id: testId,
112
- onValidate: handleOnValidate
113
- };
42
+ data: { testid: testId },
43
+ id: testId,
44
+ }
114
45
 
115
- render(
116
- <PhoneNumberInput
117
- {...props}
118
- />
119
- );
46
+ render(<PhoneNumberInput {...props} />)
47
+ const kit = screen.getByTestId(testId)
48
+ expect(kit).toBeInTheDocument()
49
+ })
120
50
 
121
- expect(handleOnValidate).toBeCalledWith(true)
122
- });
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
+ }
70
+
71
+ render(<PhoneNumberInput {...props} />)
72
+ const kit = screen.getByRole("textbox")
73
+ expect(kit).toHaveDisplayValue(value)
74
+ })