playbook_ui 12.3.0.pre.alpha.patchtest1 → 12.3.1.pre.alpha.phone1

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: '0928d7277d3ae8bafae0638314ca0e5b76d4fd09e8c402e281644196b7917903'
4
- data.tar.gz: f62293859cddfd9bfe8493e9fd3a40a7515881e99262a329e8914390b99a632c
3
+ metadata.gz: 2d0da36e2f894f76eb618fae4726c5fbdd52e6aa451ec1c8095fe32409be47d8
4
+ data.tar.gz: 79f4acd1edf5ade98a889ee5f0bd1a0c47c0fbb4f9851b13ef717f84d10b0378
5
5
  SHA512:
6
- metadata.gz: afba11e867700ad41542a1e6a6dbff294bbe0d56dbee36ea7484cce52b8c8fc7575dc08c4f63fee38518833102abcc1815b57a4d2b71276291a1a5058a221ea7
7
- data.tar.gz: bb698f0b069b59bfac04f05235f1a3a0f73b1ca3fe7e0c00613ba213a398307ee7666f29820f9249ef410ff48395c2b3674bf18d2815fd81f7e23d1aac14d86c
6
+ metadata.gz: 5d569535b3adc70df8c8ef5900196beca2bb253effbead6d47b6b8479346ebbe7790a3dc9db111e7901c1286b0c49f5bbed459242aaa39179c07c4149141ab35
7
+ data.tar.gz: ada28cdce704202dee2338e8b88e2218ec1397712db2ac392f37f133c597c6ad4f5684f9d85afa2659d8fb8d3782f28fcc95009f0440491444f749bd71c909ea
@@ -61,6 +61,7 @@
61
61
  @import 'pb_passphrase/passphrase';
62
62
  @import 'pb_person/person';
63
63
  @import 'pb_person_contact/person_contact';
64
+ @import 'pb_phone_number_input/phone_number_input';
64
65
  @import 'pb_pill/pill';
65
66
  @import 'pb_popover/popover';
66
67
  @import 'pb_progress_pills/progress_pills';
@@ -106,4 +107,3 @@
106
107
  @import './utilities/display';
107
108
  @import './utilities/flexbox';
108
109
  @import './utilities/focus';
109
-
@@ -62,6 +62,7 @@ kits:
62
62
  - hashtag
63
63
  - pill
64
64
  - pagination
65
+ - phone_number_input
65
66
  - popover
66
67
  - progress:
67
68
  - progress_pills
@@ -69,6 +69,7 @@ export { default as Passphrase } from './pb_passphrase/_passphrase'
69
69
  export { default as PbReactPopover } from './pb_popover/_popover'
70
70
  export { default as Person } from './pb_person/_person'
71
71
  export { default as PersonContact } from './pb_person_contact/_person_contact'
72
+ export { default as PhoneNumberInput} from './pb_phone_number_input/_phone_number_input'
72
73
  export { default as Pill } from './pb_pill/_pill'
73
74
  export { default as ProgressPills } from './pb_progress_pills/_progress_pills'
74
75
  export { default as ProgressSimple } from './pb_progress_simple/_progress_simple'
@@ -0,0 +1,82 @@
1
+ @import "../tokens/colors";
2
+
3
+ .pb_phone_number_input {
4
+ input::placeholder {
5
+ color: $focus_input_light;
6
+ }
7
+
8
+ .iti__country.iti__highlight {
9
+ background-color: $hover_light;
10
+
11
+ .iti__country-name, .iti__dial-code {
12
+ color: $primary
13
+ }
14
+ }
15
+
16
+ .iti--allow-dropdown .iti__flag-container:hover .iti__selected-flag {
17
+ background-color: $focus_input_light;
18
+ }
19
+
20
+ .iti__flag-container:hover + .text_input {
21
+ background-color: $focus_input_light;
22
+ }
23
+
24
+ .iti__flag {
25
+ background-image: url("https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.19/img/flags.png");
26
+ }
27
+
28
+ .iti--separate-dial-code .iti__selected-flag {
29
+ background-color: rgba(0, 0, 0, 0);
30
+ }
31
+
32
+ .iti__arrow {
33
+ border-left: unset;
34
+ border-right: unset;
35
+ border-top: unset;
36
+ width: unset;
37
+ height: unset;
38
+ margin-bottom: 4px;
39
+ }
40
+
41
+ .iti__arrow--up {
42
+ border-bottom: unset;
43
+ }
44
+
45
+ .iti__arrow::before {
46
+ display: inline-block;
47
+ text-rendering: auto;
48
+ -webkit-font-smoothing: antialiased;
49
+ font: var(--fa-font-regular);
50
+ font-size: 0.5em;
51
+ content: "\f078";
52
+ }
53
+
54
+ .iti__arrow.iti__arrow--up::before {
55
+ font: var(--fa-font-regular);
56
+ font-size: 0.5em;
57
+ content: "\f077";
58
+ }
59
+
60
+ .iti__active::after {
61
+ float: right;
62
+ text-rendering: auto;
63
+ -webkit-font-smoothing: antialiased;
64
+ font: var(--fa-font-regular);
65
+ content: "\f00c";
66
+ font-size: 1em;
67
+ margin-top: 5px;
68
+ }
69
+
70
+ .iti__country-list {
71
+ min-width: 340px;
72
+ border-radius: $border_radius_md;
73
+ border: 1px solid $border_light;
74
+ box-shadow: $shadow_deep;
75
+ }
76
+
77
+ @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
78
+ .iti__flag {
79
+ background-image: url("https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.19/img/flags@2x.png");
80
+ }
81
+ }
82
+ }
@@ -0,0 +1,155 @@
1
+ /* @flow */
2
+ import React, { useEffect, useRef, useState } from 'react'
3
+ import classnames from 'classnames'
4
+ import { buildAriaProps, buildCss, buildDataProps } from '../utilities/props'
5
+ import { globalProps } from '../utilities/globalProps'
6
+ import intlTelInput from 'intl-tel-input'
7
+ import 'intl-tel-input/build/css/intlTelInput.css'
8
+ import TextInput from '../pb_text_input/_text_input'
9
+
10
+ declare global {
11
+ interface Window {
12
+ intlTelInputGlobals: any,
13
+ }
14
+ }
15
+
16
+ type PhoneNumberInputProps = {
17
+ aria?: { [key: string]: string },
18
+ className?: string,
19
+ data?: { [key: string]: string },
20
+ disabled?: boolean,
21
+ id?: string,
22
+ initialCountry?: string,
23
+ isValid?: (valid: boolean) => void,
24
+ label?: string,
25
+ name?: string,
26
+ onChange?: (e: React.FormEvent<HTMLInputElement>) => void,
27
+ onlyCountries: string[],
28
+ preferredCountries?: string[],
29
+ value?: string,
30
+ }
31
+
32
+ enum ValidationError {
33
+ TooShort = 2,
34
+ TooLong = 3,
35
+ }
36
+
37
+ const formatToGlobalCountryName = (countryName: string) => {
38
+ return countryName.split('(')[0].trim()
39
+ }
40
+
41
+ const formatAllCountries = () => {
42
+ let countryData = window.intlTelInputGlobals.getCountryData()
43
+ for (let i = 0; i < countryData.length; i++) {
44
+ let country = countryData[i]
45
+ country.name = formatToGlobalCountryName(country.name)
46
+ }
47
+ }
48
+
49
+ const containOnlyNumbers = (value: string) => {
50
+ return /^(\++)*(\d+)$/.test(value)
51
+ }
52
+
53
+ const PhoneNumberInput = (props: PhoneNumberInputProps) => {
54
+ const {
55
+ aria = {},
56
+ className,
57
+ data = {},
58
+ disabled = false,
59
+ id = '',
60
+ initialCountry = '',
61
+ isValid = () => {void 0 },
62
+ label = '',
63
+ name = '',
64
+ onChange = () => { void 0 },
65
+ onlyCountries = [],
66
+ preferredCountries = [],
67
+ value = '',
68
+ } = props
69
+
70
+ const ariaProps = buildAriaProps(aria)
71
+ const dataProps = buildDataProps(data)
72
+ const classes = classnames(buildCss('pb_phone_number_input'), globalProps(props), className)
73
+
74
+ const inputRef = useRef<HTMLInputElement>()
75
+ const [inputValue, setInputValue] = useState(value)
76
+ const [itiInit, setItiInit] = useState<any>()
77
+ const [error, setError] = useState('')
78
+
79
+ const validateTooLongNumber = (itiInit: any) => {
80
+ const error = itiInit.getValidationError()
81
+
82
+ if (error === ValidationError.TooLong) {
83
+ const countryName = itiInit.getSelectedCountryData().name
84
+ setError(`Invalid ${countryName} phone number (too long)`)
85
+ } else {
86
+ setError('')
87
+ }
88
+ }
89
+
90
+ const validateTooShortNumber = () => {
91
+ const error = itiInit.getValidationError()
92
+
93
+ if (error === ValidationError.TooShort) {
94
+ const countryName = itiInit.getSelectedCountryData().name
95
+ setError(`Invalid ${countryName} phone number (too short)`)
96
+ }
97
+ }
98
+
99
+ const validateOnlyNumbers = () => {
100
+ if (inputValue && !containOnlyNumbers(inputValue)) {
101
+ setError('Invalid phone number. Enter numbers only.')
102
+ }
103
+ }
104
+
105
+ const validateErrors = () => {
106
+ validateTooShortNumber()
107
+ validateOnlyNumbers()
108
+ }
109
+
110
+ const handleOnChange = (evt: React.ChangeEvent<HTMLInputElement>) => {
111
+ setInputValue(evt.target.value)
112
+ validateTooLongNumber(itiInit)
113
+ onChange(evt)
114
+ isValid(itiInit.isValidNumber())
115
+ }
116
+
117
+ useEffect(() => {
118
+ formatAllCountries()
119
+
120
+ const telInputInit = new intlTelInput(inputRef.current, {
121
+ utilsScript: 'https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.19/js/utils.js',
122
+ separateDialCode: true,
123
+ preferredCountries,
124
+ allowDropdown: !disabled,
125
+ initialCountry,
126
+ onlyCountries,
127
+ }
128
+ )
129
+
130
+ inputRef.current.addEventListener("countrychange", () => validateTooLongNumber(telInputInit))
131
+ setItiInit(telInputInit)
132
+ }, [])
133
+
134
+ return (
135
+ <div
136
+ {...ariaProps}
137
+ {...dataProps}
138
+ className={classes}
139
+ >
140
+ <TextInput
141
+ disabled={disabled}
142
+ error={error}
143
+ id={id}
144
+ label={label}
145
+ name={name}
146
+ onBlur={() => validateErrors()}
147
+ onChange={handleOnChange}
148
+ ref={inputRef}
149
+ value={inputValue}
150
+ />
151
+ </div>
152
+ )
153
+ }
154
+
155
+ export default PhoneNumberInput
@@ -0,0 +1,10 @@
1
+ import React from 'react'
2
+ import { PhoneNumberInput } from '../../'
3
+
4
+ const PhoneNumberInputDefault = () => (
5
+ <>
6
+ <PhoneNumberInput id='default' />
7
+ </>
8
+ )
9
+
10
+ export default PhoneNumberInputDefault
@@ -0,0 +1,12 @@
1
+ import React from 'react'
2
+ import { PhoneNumberInput } from '../../'
3
+
4
+ const PhoneNumberInitialCountry = () => (
5
+ <>
6
+ <PhoneNumberInput id='initial'
7
+ initialCountry='br'
8
+ />
9
+ </>
10
+ )
11
+
12
+ export default PhoneNumberInitialCountry
@@ -0,0 +1 @@
1
+ You can set an initial country, which will preselect that country within the input as well as the country dropdown.
@@ -0,0 +1,13 @@
1
+ import React from 'react'
2
+ import { PhoneNumberInput } from '../../'
3
+
4
+ const PhoneNumberInputOnlyCountries = () => (
5
+ <>
6
+ <PhoneNumberInput
7
+ id='only'
8
+ onlyCountries={['us', 'br']}
9
+ />
10
+ </>
11
+ )
12
+
13
+ export default PhoneNumberInputOnlyCountries
@@ -0,0 +1 @@
1
+ You can limit the country options, which will remove all countries except your selections from the dropdown.
@@ -0,0 +1,13 @@
1
+ import React from 'react'
2
+ import { PhoneNumberInput } from '../../'
3
+
4
+ const PhoneNumberInputPreferredCountries = () => (
5
+ <>
6
+ <PhoneNumberInput
7
+ id='preferred'
8
+ preferredCountries={['us', 'br', 'ph', 'gb']}
9
+ />
10
+ </>
11
+ )
12
+
13
+ export default PhoneNumberInputPreferredCountries
@@ -0,0 +1 @@
1
+ You can set preferred countries to group at the top of your country selector dropdown. The countries you select will display in the order you list them, with the first country preselected and all other countries listed alphabetically below a section separator within the remaining dropdown.
@@ -0,0 +1,7 @@
1
+ examples:
2
+
3
+ react:
4
+ - phone_number_input_default: Default
5
+ - phone_number_input_preferred_countries: Preferred Countries
6
+ - phone_number_input_initial_country: Initial Country
7
+ - phone_number_input_only_countries: Limited Countries
@@ -0,0 +1,4 @@
1
+ export { default as PhoneNumberInputDefault } from './_phone_number_input_default'
2
+ export { default as PhoneNumberInputPreferredCountries } from './_phone_number_input_preferred_countries'
3
+ export { default as PhoneNumberInputInitialCountry } from './_phone_number_input_initial_country'
4
+ export { default as PhoneNumberInputOnlyCountries } from './_phone_number_input_only_countries'
@@ -0,0 +1,74 @@
1
+ import React from 'react'
2
+ import { render, screen } from '../utilities/test-utils'
3
+ import PhoneNumberInput from './_phone_number_input'
4
+
5
+ const testId = "phoneNumberInput"
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
+ }
70
+
71
+ render(<PhoneNumberInput {...props} />)
72
+ const kit = screen.getByRole("textbox")
73
+ expect(kit).toHaveDisplayValue(value)
74
+ })
@@ -0,0 +1 @@
1
+ declare module 'intl-tel-input'
@@ -65,6 +65,7 @@ import * as Passphrase from 'pb_passphrase/docs'
65
65
  import * as PbReactPopover from 'pb_popover/docs'
66
66
  import * as Person from 'pb_person/docs'
67
67
  import * as PersonContact from 'pb_person_contact/docs'
68
+ import * as PhoneNumberInput from 'pb_phone_number_input/docs'
68
69
  import * as Pill from 'pb_pill/docs'
69
70
  import * as ProgressPills from 'pb_progress_pills/docs'
70
71
  import * as ProgressSimple from 'pb_progress_simple/docs'
@@ -163,6 +164,7 @@ WebpackerReact.setup({
163
164
  ...PbReactPopover,
164
165
  ...Person,
165
166
  ...PersonContact,
167
+ ...PhoneNumberInput,
166
168
  ...Pill,
167
169
  ...ProgressPills,
168
170
  ...ProgressSimple,
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Playbook
4
- PREVIOUS_VERSION = "12.3.0"
5
- VERSION = "12.3.0.pre.alpha.patchtest1"
4
+ PREVIOUS_VERSION = "12.3.1"
5
+ VERSION = "12.3.1.pre.alpha.phone1"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: playbook_ui
3
3
  version: !ruby/object:Gem::Version
4
- version: 12.3.0.pre.alpha.patchtest1
4
+ version: 12.3.1.pre.alpha.phone1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Power UX
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-02-14 00:00:00.000000000 Z
12
+ date: 2023-02-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: actionpack
@@ -1552,6 +1552,19 @@ files:
1552
1552
  - app/pb_kits/playbook/pb_person_contact/docs/index.js
1553
1553
  - app/pb_kits/playbook/pb_person_contact/person_contact.html.erb
1554
1554
  - app/pb_kits/playbook/pb_person_contact/person_contact.rb
1555
+ - app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.scss
1556
+ - app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx
1557
+ - app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_default.jsx
1558
+ - app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_initial_country.jsx
1559
+ - app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_initial_country.md
1560
+ - app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_only_countries.jsx
1561
+ - app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_only_countries.md
1562
+ - app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_preferred_countries.jsx
1563
+ - app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_preferred_countries.md
1564
+ - app/pb_kits/playbook/pb_phone_number_input/docs/example.yml
1565
+ - app/pb_kits/playbook/pb_phone_number_input/docs/index.js
1566
+ - app/pb_kits/playbook/pb_phone_number_input/phone_number_input.test.js
1567
+ - app/pb_kits/playbook/pb_phone_number_input/types.d.ts
1555
1568
  - app/pb_kits/playbook/pb_pill/_pill.scss
1556
1569
  - app/pb_kits/playbook/pb_pill/_pill.tsx
1557
1570
  - app/pb_kits/playbook/pb_pill/docs/_description.md