playbook_ui 14.11.1.pre.alpha.PBNTR573datepickerinvestigation5355 → 14.11.1.pre.alpha.PLAY1720phonenumberinputformatAsYouType5377
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.html.erb +2 -13
- data/app/pb_kits/playbook/pb_date_picker/date_picker.rb +0 -2
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_default.html.erb +1 -12
- data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +30 -12
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_format.html.erb +15 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_format.jsx +24 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_format.md +1 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/example.yml +3 -1
- data/app/pb_kits/playbook/pb_phone_number_input/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.rb +3 -0
- data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.test.js +20 -1
- data/app/pb_kits/playbook/pb_table/styles/_scroll.scss +6 -5
- data/dist/chunks/{_typeahead-BNULwihE.js → _typeahead-DWVCHt1f.js} +1 -1
- data/dist/chunks/{_weekday_stacked-BKWemDAe.js → _weekday_stacked-0vGR_2wj.js} +1 -1
- data/dist/chunks/vendor.js +1 -1
- data/dist/playbook-doc.js +1 -1
- data/dist/playbook-rails-react-bindings.js +1 -1
- data/dist/playbook-rails.js +1 -1
- data/dist/playbook.css +1 -1
- data/lib/playbook/version.rb +1 -1
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f652f813944aec48c8bc973298758ac7c9cd498e72bdbe76c35d4153f23de637
|
4
|
+
data.tar.gz: aeb06568f624527493457b48c19964e1ce23add8a44dfc90f6453a4757e01063
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c730aad6b384918b56a6695814c71016baa1f8295ed5e0ba879cf9f620ecd3fb9e74c9fd9ddc3b84bd8396b84e83c4b0c90580754284671b59823bdf596ee470
|
7
|
+
data.tar.gz: f3043409e34d7a6c0b00576e83e3e6eeebb0e9d6e307f6075c157a9c8e4323261fe22c6f6372956be0bab4d9e95cb61935bcec6ce147a005008cdd3a26f1c14a
|
@@ -68,7 +68,7 @@
|
|
68
68
|
</div>
|
69
69
|
|
70
70
|
<%= javascript_tag do %>
|
71
|
-
|
71
|
+
window.addEventListener("DOMContentLoaded", () => {
|
72
72
|
datePickerHelper(<%= object.date_picker_config %>, "<%= object.scroll_container %>")
|
73
73
|
|
74
74
|
if (<%= object.selection_type == "quickpick" %>) {
|
@@ -80,17 +80,6 @@
|
|
80
80
|
endDate.value = splittedValue[1] ? splittedValue[1] : splittedValue[0]
|
81
81
|
})
|
82
82
|
}
|
83
|
-
}
|
84
|
-
|
85
|
-
window.addEventListener("DOMContentLoaded", () => {
|
86
|
-
loadDatePicker();
|
87
|
-
});
|
88
|
-
|
89
|
-
if (<%= !object.custom_event_listener.empty? %>) {
|
90
|
-
window.addEventListener("<%= object.custom_event_listener %>", () => {
|
91
|
-
console.log("Custom Event listener fired");
|
92
|
-
loadDatePicker();
|
93
|
-
})
|
94
|
-
}
|
83
|
+
})
|
95
84
|
<% end %>
|
96
85
|
<% end %>
|
@@ -73,8 +73,6 @@ module Playbook
|
|
73
73
|
default: false
|
74
74
|
prop :year_range, type: Playbook::Props::Array,
|
75
75
|
default: [1900, 2100]
|
76
|
-
prop :custom_event_listener, type: Playbook::Props::String,
|
77
|
-
default: ""
|
78
76
|
|
79
77
|
def classname
|
80
78
|
default_margin_bottom = margin_bottom.present? ? "" : " mb_sm"
|
@@ -1,12 +1 @@
|
|
1
|
-
<%= pb_rails("date_picker", props: { picker_id: "date-picker-default"
|
2
|
-
<%= pb_rails("button", props: { id: "date-picker-loader", text: "Load Custom Event" }) %>
|
3
|
-
|
4
|
-
<script>
|
5
|
-
document.getElementById("date-picker-loader").addEventListener("click", () => {
|
6
|
-
window.document.dispatchEvent(
|
7
|
-
new CustomEvent("datePickerLoader", {
|
8
|
-
bubbles: true,
|
9
|
-
})
|
10
|
-
);
|
11
|
-
});
|
12
|
-
</script>
|
1
|
+
<%= pb_rails("date_picker", props: { picker_id: "date-picker-default"}) %>
|
@@ -35,6 +35,7 @@ type PhoneNumberInputProps = {
|
|
35
35
|
preferredCountries?: string[],
|
36
36
|
required?: boolean,
|
37
37
|
value?: string,
|
38
|
+
formatAsYouType?: boolean,
|
38
39
|
}
|
39
40
|
|
40
41
|
enum ValidationError {
|
@@ -87,6 +88,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.MutableRefOb
|
|
87
88
|
required = false,
|
88
89
|
preferredCountries = [],
|
89
90
|
value = "",
|
91
|
+
formatAsYouType = false,
|
90
92
|
} = props
|
91
93
|
|
92
94
|
const ariaProps = buildAriaProps(aria)
|
@@ -99,8 +101,8 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.MutableRefOb
|
|
99
101
|
)
|
100
102
|
|
101
103
|
const inputRef = useRef<HTMLInputElement>()
|
104
|
+
const itiRef = useRef<any>(null);
|
102
105
|
const [inputValue, setInputValue] = useState(value)
|
103
|
-
const [itiInit, setItiInit] = useState<any>()
|
104
106
|
const [error, setError] = useState(props.error)
|
105
107
|
const [dropDownIsOpen, setDropDownIsOpen] = useState(false)
|
106
108
|
const [selectedData, setSelectedData] = useState()
|
@@ -130,8 +132,12 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.MutableRefOb
|
|
130
132
|
}
|
131
133
|
})
|
132
134
|
|
135
|
+
const unformatNumber = (formattedNumber: any) => {
|
136
|
+
return formattedNumber.replace(/\D/g, "")
|
137
|
+
}
|
138
|
+
|
133
139
|
const showFormattedError = (reason = '') => {
|
134
|
-
const countryName =
|
140
|
+
const countryName = itiRef.current.getSelectedCountryData().name
|
135
141
|
const reasonText = reason.length > 0 ? ` (${reason})` : ''
|
136
142
|
setError(`Invalid ${countryName} phone number${reasonText}`)
|
137
143
|
return true
|
@@ -189,12 +195,12 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.MutableRefOb
|
|
189
195
|
}
|
190
196
|
|
191
197
|
const validateErrors = () => {
|
192
|
-
if (
|
193
|
-
if (validateOnlyNumbers(
|
194
|
-
if (validateTooLongNumber(
|
195
|
-
if (validateTooShortNumber(
|
196
|
-
if (validateUnhandledError(
|
197
|
-
if (validateMissingAreaCode(
|
198
|
+
if (itiRef.current) isValid(itiRef.current.isValidNumber())
|
199
|
+
if (validateOnlyNumbers(itiRef.current)) return
|
200
|
+
if (validateTooLongNumber(itiRef.current)) return
|
201
|
+
if (validateTooShortNumber(itiRef.current)) return
|
202
|
+
if (validateUnhandledError(itiRef.current)) return
|
203
|
+
if (validateMissingAreaCode(itiRef.current)) return
|
198
204
|
}
|
199
205
|
|
200
206
|
const getCurrentSelectedData = (itiInit: any, inputValue: string) => {
|
@@ -203,10 +209,16 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.MutableRefOb
|
|
203
209
|
|
204
210
|
const handleOnChange = (evt: React.ChangeEvent<HTMLInputElement>) => {
|
205
211
|
setInputValue(evt.target.value)
|
206
|
-
|
212
|
+
let phoneNumberData
|
213
|
+
if (formatAsYouType) {
|
214
|
+
const formattedPhoneNumberData = getCurrentSelectedData(itiRef.current, evt.target.value)
|
215
|
+
phoneNumberData = {...formattedPhoneNumberData, number: unformatNumber(formattedPhoneNumberData.number)}
|
216
|
+
} else {
|
217
|
+
phoneNumberData = getCurrentSelectedData(itiRef.current, evt.target.value)
|
218
|
+
}
|
207
219
|
setSelectedData(phoneNumberData)
|
208
220
|
onChange(phoneNumberData)
|
209
|
-
isValid(
|
221
|
+
isValid(itiRef.current.isValidNumber())
|
210
222
|
}
|
211
223
|
|
212
224
|
// Separating Concerns as React Docs Recommend
|
@@ -230,9 +242,11 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.MutableRefOb
|
|
230
242
|
onlyCountries,
|
231
243
|
countrySearch: false,
|
232
244
|
fixDropdownWidth: false,
|
233
|
-
formatAsYouType:
|
245
|
+
formatAsYouType: formatAsYouType,
|
234
246
|
})
|
235
247
|
|
248
|
+
itiRef.current = telInputInit;
|
249
|
+
|
236
250
|
inputRef.current.addEventListener("countrychange", (evt: Event) => {
|
237
251
|
const phoneNumberData = getCurrentSelectedData(telInputInit, (evt.target as HTMLInputElement).value)
|
238
252
|
setSelectedData(phoneNumberData)
|
@@ -243,7 +257,11 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.MutableRefOb
|
|
243
257
|
inputRef.current.addEventListener("open:countrydropdown", () => setDropDownIsOpen(true))
|
244
258
|
inputRef.current.addEventListener("close:countrydropdown", () => setDropDownIsOpen(false))
|
245
259
|
|
246
|
-
|
260
|
+
if (formatAsYouType) {
|
261
|
+
inputRef.current?.addEventListener("input", (evt) => {
|
262
|
+
handleOnChange(evt as unknown as React.ChangeEvent<HTMLInputElement>);
|
263
|
+
});
|
264
|
+
}
|
247
265
|
}, [])
|
248
266
|
|
249
267
|
let textInputProps: {[key: string]: any} = {
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<%= pb_rails("phone_number_input", props: {
|
2
|
+
id: "phone_number_input",
|
3
|
+
format_as_you_type: true
|
4
|
+
}) %>
|
5
|
+
|
6
|
+
<%= pb_rails("button", props: {id: "clickable", text: "Save Phone Number"}) %>
|
7
|
+
|
8
|
+
<%= javascript_tag do %>
|
9
|
+
document.querySelector('#clickable').addEventListener('click', () => {
|
10
|
+
const formattedPhoneNumber = document.querySelector('#phone_number_input').value
|
11
|
+
const unformattedPhoneNumber = formattedPhoneNumber.replace(/\D/g, "")
|
12
|
+
|
13
|
+
alert(`Formatted: ${formattedPhoneNumber}. Unformatted: ${unformattedPhoneNumber}`)
|
14
|
+
})
|
15
|
+
<% end %>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import React, { useState } from "react";
|
2
|
+
import { PhoneNumberInput, Body } from "playbook-ui";
|
3
|
+
|
4
|
+
const PhoneNumberInputFormat = (props) => {
|
5
|
+
const [phoneNumber, setPhoneNumber] = useState("");
|
6
|
+
|
7
|
+
const handleOnChange = ({ number }) => {
|
8
|
+
setPhoneNumber(number);
|
9
|
+
};
|
10
|
+
|
11
|
+
return (
|
12
|
+
<>
|
13
|
+
<PhoneNumberInput
|
14
|
+
formatAsYouType
|
15
|
+
id="format"
|
16
|
+
onChange={handleOnChange}
|
17
|
+
{...props}
|
18
|
+
/>
|
19
|
+
{phoneNumber && <Body>Unformatted number: {phoneNumber}</Body>}
|
20
|
+
</>
|
21
|
+
);
|
22
|
+
};
|
23
|
+
|
24
|
+
export default PhoneNumberInputFormat;
|
@@ -0,0 +1 @@
|
|
1
|
+
NOTE: the `number` in the React `onChange` event will not include formatting (no spaces, dashes, and parentheses). For Rails, the `value` will include formatting and its value must be sanitized manually.
|
@@ -8,10 +8,12 @@ examples:
|
|
8
8
|
- phone_number_input_validation: Form Validation
|
9
9
|
- phone_number_input_clear_field: Clearing the Input Field
|
10
10
|
- phone_number_input_access_input_element: Accessing the Input Element
|
11
|
+
- phone_number_input_format: Format as You Type
|
11
12
|
|
12
13
|
rails:
|
13
14
|
- phone_number_input_default: Default
|
14
15
|
- phone_number_input_preferred_countries: Preferred Countries
|
15
16
|
- phone_number_input_initial_country: Initial Country
|
16
17
|
- phone_number_input_only_countries: Limited Countries
|
17
|
-
- phone_number_input_validation: Form Validation
|
18
|
+
- phone_number_input_validation: Form Validation
|
19
|
+
- phone_number_input_format: Format as You Type
|
@@ -5,3 +5,4 @@ export { default as PhoneNumberInputOnlyCountries } from './_phone_number_input_
|
|
5
5
|
export { default as PhoneNumberInputValidation } from './_phone_number_input_validation'
|
6
6
|
export { default as PhoneNumberInputClearField } from './_phone_number_input_clear_field'
|
7
7
|
export { default as PhoneNumberInputAccessInputElement } from './_phone_number_input_access_input_element'
|
8
|
+
export { default as PhoneNumberInputFormat } from './_phone_number_input_format'
|
@@ -21,6 +21,8 @@ module Playbook
|
|
21
21
|
default: ""
|
22
22
|
prop :value, type: Playbook::Props::String,
|
23
23
|
default: ""
|
24
|
+
prop :format_as_you_type, type: Playbook::Props::Boolean,
|
25
|
+
default: false
|
24
26
|
|
25
27
|
def classname
|
26
28
|
generate_classname("pb_phone_number_input")
|
@@ -32,6 +34,7 @@ module Playbook
|
|
32
34
|
dark: dark,
|
33
35
|
disabled: disabled,
|
34
36
|
error: error,
|
37
|
+
formatAsYouType: format_as_you_type,
|
35
38
|
initialCountry: initial_country,
|
36
39
|
label: label,
|
37
40
|
name: name,
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import React from "react";
|
2
|
-
import { render, screen } from "../utilities/test-utils";
|
2
|
+
import { render, screen, act } from "../utilities/test-utils";
|
3
3
|
import PhoneNumberInput from "./_phone_number_input";
|
4
4
|
|
5
5
|
const testId = "phoneNumberInput";
|
@@ -120,3 +120,22 @@ test("should trigger callback", () => {
|
|
120
120
|
|
121
121
|
expect(handleOnValidate).toBeCalledWith(true)
|
122
122
|
});
|
123
|
+
|
124
|
+
test("should format phone number as '555-555-5555' with formatAsYouType and 'us' country", () => {
|
125
|
+
const props = {
|
126
|
+
initialCountry: 'us',
|
127
|
+
formatAsYouType: true,
|
128
|
+
id: testId,
|
129
|
+
};
|
130
|
+
|
131
|
+
render(<PhoneNumberInput {...props} />);
|
132
|
+
|
133
|
+
const input = screen.getByRole("textbox");
|
134
|
+
|
135
|
+
act(() => {
|
136
|
+
input.value = "5555555555";
|
137
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
138
|
+
});
|
139
|
+
|
140
|
+
expect(input.value).toBe("555-555-5555");
|
141
|
+
});
|
@@ -1,7 +1,7 @@
|
|
1
1
|
@import "../../tokens/screen_sizes";
|
2
|
+
@import "../../tokens/border_radius";
|
2
3
|
|
3
4
|
.table-responsive-scroll {
|
4
|
-
display: block;
|
5
5
|
overflow-x: scroll;
|
6
6
|
|
7
7
|
// hides duplicate scroll bar for those that see two (byproduct of repeated table-responsive-scroll class
|
@@ -27,11 +27,12 @@
|
|
27
27
|
// Responsive Styles
|
28
28
|
@media (max-width: 1600px) {
|
29
29
|
&[class*="table-responsive-scroll"] {
|
30
|
-
|
31
|
-
|
30
|
+
&:has(> table.table-card) {
|
31
|
+
border-radius: $border_rad_light;
|
32
|
+
box-shadow: 1px 0 0 0px $border_light,
|
32
33
|
-1px 0 0 0px $border_light
|
33
|
-
|
34
|
-
|
34
|
+
}
|
35
|
+
}
|
35
36
|
&[class^=pb_table].table-sm.table-card thead tr th:first-child,
|
36
37
|
&[class^=pb_table].table-sm:not(.no-hover).table-card tbody tr td:first-child {
|
37
38
|
border-left-width: 0px;
|