playbook_ui 17.1.0.pre.rc.2 → 17.1.0.pre.rc.3
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.test.js +79 -0
- data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.ts +41 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.html.erb +7 -17
- data/app/pb_kits/playbook/pb_phone_number_input/index.ts +515 -0
- data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.html.erb +15 -1
- data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.rb +17 -8
- data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.test.js +298 -0
- data/app/pb_kits/playbook/pb_pill/_pill.scss +49 -0
- data/app/pb_kits/playbook/pb_pill/_pill.test.jsx +71 -0
- data/app/pb_kits/playbook/pb_pill/_pill.tsx +4 -1
- data/app/pb_kits/playbook/pb_pill/docs/_pill_notification.html.erb +14 -0
- data/app/pb_kits/playbook/pb_pill/docs/_pill_notification.jsx +57 -0
- data/app/pb_kits/playbook/pb_pill/docs/_pill_notification.md +1 -0
- data/app/pb_kits/playbook/pb_pill/docs/_playground.json +26 -0
- data/app/pb_kits/playbook/pb_pill/docs/_playground.overrides.json +25 -1
- data/app/pb_kits/playbook/pb_pill/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_pill/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_pill/kit.schema.json +8 -0
- data/app/pb_kits/playbook/pb_pill/pill.rb +13 -1
- data/app/pb_kits/playbook/pb_select/docs/_playground.json +13 -1
- data/app/pb_kits/playbook/pb_select/docs/_playground.overrides.json +9 -0
- data/dist/chunks/{_detail-eMZN1lvq.js → _detail-B4-co8rv.js} +1 -1
- data/dist/chunks/{_pb_line_graph-CiTPfwy6.js → _pb_line_graph-BGyCJjgG.js} +1 -1
- data/dist/chunks/{_table-D5ErUJ1X.js → _table-SGw4kUye.js} +1 -1
- data/dist/chunks/_typeahead-sT6HFKNd.js +1 -0
- data/dist/chunks/{globalProps-DXDSpbRD.js → globalProps-K1DCn3il.js} +1 -1
- data/dist/chunks/{lib-DJySPcHy.js → lib-7vm9BMqp.js} +1 -1
- data/dist/chunks/time_picker_helper-CN-3p6mx.js +1 -0
- data/dist/chunks/vendor.js +2 -2
- data/dist/menu.yml +8 -8
- 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 +13 -9
- data/dist/chunks/_typeahead-kaYZtVWA.js +0 -1
- data/dist/chunks/time_picker_helper-Bx8nzyM8.js +0 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 02b1ea72561f3498a21c3cf0595c72212fd2e04ba63539688d7239ba7261d6f9
|
|
4
|
+
data.tar.gz: 412dec4b2daa3b7371785dd21701dec9872a188e36418ac13c969025f7c49812
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b4ba91e8ba28694803157c76d2cf93d1e204de9bebe3cd02c470889a38e6e38f78f57f8d0f9019c1e864f4d20fefd0873a051b7e6e90bf0124e1a93125b98d24
|
|
7
|
+
data.tar.gz: d1a34a60dbb2f1b2ce9125e08773d9176d23a4208d57dfbd0a35a1983df2920702fe17343cfed9510b82d1e7ceaf2d2207b91c9d6cb4a10945c364e7578405d4
|
|
@@ -100,6 +100,85 @@ describe('DatePicker Kit', () => {
|
|
|
100
100
|
expect(kit).not.toHaveAttribute('data-default-value')
|
|
101
101
|
})
|
|
102
102
|
|
|
103
|
+
test.each([
|
|
104
|
+
['no-separators', '08152026', '08/15/2026'],
|
|
105
|
+
['all-separators', '08/15/2026', '08/15/2026'],
|
|
106
|
+
['year-end', '12312026', '12/31/2026'],
|
|
107
|
+
['separator-before-year-only', '0326/2026', '03/26/2026'],
|
|
108
|
+
['separator-after-month-only', '03/262026', '03/26/2026'],
|
|
109
|
+
])('allowInput saves the typed date %s as %s', async (caseName, typedValue, expectedValue) => {
|
|
110
|
+
const testId = `datepicker-allow-input-${caseName}`
|
|
111
|
+
render(
|
|
112
|
+
<DatePicker
|
|
113
|
+
allowInput
|
|
114
|
+
data={{ testid: testId }}
|
|
115
|
+
format="m/d/Y"
|
|
116
|
+
pickerId={`date-picker-allow-input-${caseName}`}
|
|
117
|
+
placeholder="mm/dd/yyyy"
|
|
118
|
+
/>
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
const kit = screen.getByTestId(testId)
|
|
122
|
+
const input = within(kit).getByPlaceholderText('mm/dd/yyyy')
|
|
123
|
+
|
|
124
|
+
fireEvent.change(input, { target: { value: typedValue } })
|
|
125
|
+
fireEvent.blur(input)
|
|
126
|
+
|
|
127
|
+
await waitFor(() => {
|
|
128
|
+
expect(input).toHaveValue(expectedValue)
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
test.each([
|
|
133
|
+
['no-slashes', '08122026 at 12:00 AM', '08/12/2026 at 12:00 AM'],
|
|
134
|
+
['with-slashes', '08/12/2026 at 12:00 AM', '08/12/2026 at 12:00 AM'],
|
|
135
|
+
])('allowInput with enableTime saves %s as %s', async (caseName, typedValue, expectedValue) => {
|
|
136
|
+
const testId = `datepicker-allow-input-time-${caseName}`
|
|
137
|
+
render(
|
|
138
|
+
<DatePicker
|
|
139
|
+
allowInput
|
|
140
|
+
data={{ testid: testId }}
|
|
141
|
+
enableTime
|
|
142
|
+
format="m/d/Y"
|
|
143
|
+
pickerId={`date-picker-allow-input-time-${caseName}`}
|
|
144
|
+
placeholder="mm/dd/yyyy"
|
|
145
|
+
/>
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
const kit = screen.getByTestId(testId)
|
|
149
|
+
const input = within(kit).getByPlaceholderText('mm/dd/yyyy')
|
|
150
|
+
|
|
151
|
+
fireEvent.change(input, { target: { value: typedValue } })
|
|
152
|
+
fireEvent.blur(input)
|
|
153
|
+
|
|
154
|
+
await waitFor(() => {
|
|
155
|
+
expect(input).toHaveValue(expectedValue)
|
|
156
|
+
})
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
test('allowInput leaves ambiguous digit-only input to flatpickr', async () => {
|
|
160
|
+
const testId = 'datepicker-allow-input-unpadded'
|
|
161
|
+
render(
|
|
162
|
+
<DatePicker
|
|
163
|
+
allowInput
|
|
164
|
+
data={{ testid: testId }}
|
|
165
|
+
format="m/d/Y"
|
|
166
|
+
pickerId="date-picker-allow-input-unpadded"
|
|
167
|
+
placeholder="mm/dd/yyyy"
|
|
168
|
+
/>
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
const kit = screen.getByTestId(testId)
|
|
172
|
+
const input = within(kit).getByPlaceholderText('mm/dd/yyyy')
|
|
173
|
+
|
|
174
|
+
fireEvent.change(input, { target: { value: '0815' } })
|
|
175
|
+
fireEvent.blur(input)
|
|
176
|
+
|
|
177
|
+
await waitFor(() => {
|
|
178
|
+
expect(input).not.toHaveValue('08/15')
|
|
179
|
+
})
|
|
180
|
+
})
|
|
181
|
+
|
|
103
182
|
test('inLine alone adds inline-date-picker class and inline control icons, not the calendar icon', () => {
|
|
104
183
|
const testId = 'datepicker-inline-only'
|
|
105
184
|
render(
|
|
@@ -28,6 +28,44 @@ const getPositionElement = (element: string | Element) => {
|
|
|
28
28
|
return (typeof element === 'string') ? document.querySelectorAll(element)[0] : element
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
// For allowInput: digits a zero-padded value occupies for each flatpickr date token
|
|
32
|
+
const dateTokenWidths: { [key: string]: number } = { Y: 4, d: 2, j: 2, m: 2, n: 2, y: 2 }
|
|
33
|
+
|
|
34
|
+
const escapeForCharacterClass = (char: string) => char.replace(/[\\\]^-]/g, '\\$&')
|
|
35
|
+
|
|
36
|
+
// Flatpickr parses format separators as "any character" and re-matches a growing regex per token,
|
|
37
|
+
// so allowInput values with missing separators get mis-grouped: "08152026" (m/d/Y) reads day "52"
|
|
38
|
+
// and "0326/2026" reads day "6". Rebuild the leading date run with the format's own separators
|
|
39
|
+
// whenever it holds exactly the digits the format expects, leaving any suffix (enableTime's
|
|
40
|
+
// "at 12:00 AM") untouched. Partial ("0326/2026") and unseparated ("03262026") input both normalize;
|
|
41
|
+
const withDateSeparators = (dateStr: string, format: string) => {
|
|
42
|
+
if (typeof dateStr !== 'string') return dateStr
|
|
43
|
+
|
|
44
|
+
const tokens = [...(format || '').split(' ')[0]]
|
|
45
|
+
const expectedLength = tokens.reduce((total, token) => total + (dateTokenWidths[token] || 0), 0)
|
|
46
|
+
const hasUnsupportedToken = tokens.some((token) => /[a-z]/i.test(token) && !dateTokenWidths[token])
|
|
47
|
+
if (hasUnsupportedToken || expectedLength === 0) return dateStr
|
|
48
|
+
|
|
49
|
+
const separators = [...new Set(tokens.filter((token) => !dateTokenWidths[token]))]
|
|
50
|
+
const separatorClass = separators.map(escapeForCharacterClass).join('')
|
|
51
|
+
const match = dateStr.match(new RegExp(`^(\\s*)([\\d${separatorClass}]*\\d)([\\s\\S]*)$`))
|
|
52
|
+
if (!match) return dateStr
|
|
53
|
+
|
|
54
|
+
const [, leadingSpace, dateRun, rest] = match
|
|
55
|
+
const digits = separatorClass ? dateRun.replace(new RegExp(`[${separatorClass}]`, 'g'), '') : dateRun
|
|
56
|
+
if (digits.length !== expectedLength) return dateStr
|
|
57
|
+
|
|
58
|
+
let position = 0
|
|
59
|
+
const dated = tokens.map((token) => {
|
|
60
|
+
const width = dateTokenWidths[token]
|
|
61
|
+
if (!width) return token
|
|
62
|
+
position += width
|
|
63
|
+
return digits.slice(position - width, position)
|
|
64
|
+
}).join('')
|
|
65
|
+
|
|
66
|
+
return `${leadingSpace}${dated}${rest}`
|
|
67
|
+
}
|
|
68
|
+
|
|
31
69
|
type DatePickerConfig = {
|
|
32
70
|
closeOnSelect?: boolean,
|
|
33
71
|
customQuickPickDates: { override: boolean, dates: any[] },
|
|
@@ -576,6 +614,9 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
|
|
|
576
614
|
minDate: effectiveMinDate,
|
|
577
615
|
mode,
|
|
578
616
|
nextArrow: `<div style="height: 14px;">${angleRightString}</div>`,
|
|
617
|
+
...(allowInput && {
|
|
618
|
+
parseDate: (datestr, format) => flatpickr.parseDate(withDateSeparators(datestr, format), format),
|
|
619
|
+
}),
|
|
579
620
|
onOpen: [(_selectedDates, _dateStr, fp) => {
|
|
580
621
|
activePortalZIndex = portalHost
|
|
581
622
|
? resolvePortaledFloatingZIndex(
|
data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.html.erb
CHANGED
|
@@ -11,30 +11,20 @@
|
|
|
11
11
|
<%= javascript_tag do %>
|
|
12
12
|
document.addEventListener('DOMContentLoaded', function () {
|
|
13
13
|
const form = document.querySelector('#example-form-validation');
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const phoneInput = form.querySelector('#validation');
|
|
18
|
-
|
|
19
|
-
if (!phoneInput) {
|
|
20
|
-
setTimeout(waitForComponent, 100);
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Wait for intl-tel-input to initialize, then focus and blur to trigger validation
|
|
14
|
+
const phoneInput = form.querySelector('#validation');
|
|
15
|
+
|
|
16
|
+
if (phoneInput) {
|
|
25
17
|
setTimeout(function() {
|
|
26
18
|
phoneInput.focus({ preventScroll: true });
|
|
27
19
|
setTimeout(function() {
|
|
28
20
|
phoneInput.blur();
|
|
29
21
|
}, 100);
|
|
30
|
-
},
|
|
22
|
+
}, 100);
|
|
31
23
|
}
|
|
32
|
-
|
|
33
|
-
waitForComponent();
|
|
34
|
-
|
|
35
|
-
// Prevent form submission if there are validation errors
|
|
24
|
+
|
|
36
25
|
form.addEventListener('submit', function (e) {
|
|
37
|
-
if (
|
|
26
|
+
if (form.querySelector('[data-pb-phone-validation-error="true"]') ||
|
|
27
|
+
form.querySelector('.pb_body_kit_negative')) {
|
|
38
28
|
e.preventDefault();
|
|
39
29
|
}
|
|
40
30
|
});
|