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

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 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
+ })