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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_date_picker/date_picker.test.js +79 -0
  3. data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.ts +41 -0
  4. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.html.erb +7 -17
  5. data/app/pb_kits/playbook/pb_phone_number_input/index.ts +515 -0
  6. data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.html.erb +15 -1
  7. data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.rb +17 -8
  8. data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.test.js +298 -0
  9. data/app/pb_kits/playbook/pb_pill/_pill.scss +49 -0
  10. data/app/pb_kits/playbook/pb_pill/_pill.test.jsx +71 -0
  11. data/app/pb_kits/playbook/pb_pill/_pill.tsx +4 -1
  12. data/app/pb_kits/playbook/pb_pill/docs/_pill_notification.html.erb +14 -0
  13. data/app/pb_kits/playbook/pb_pill/docs/_pill_notification.jsx +57 -0
  14. data/app/pb_kits/playbook/pb_pill/docs/_pill_notification.md +1 -0
  15. data/app/pb_kits/playbook/pb_pill/docs/_playground.json +26 -0
  16. data/app/pb_kits/playbook/pb_pill/docs/_playground.overrides.json +25 -1
  17. data/app/pb_kits/playbook/pb_pill/docs/example.yml +2 -0
  18. data/app/pb_kits/playbook/pb_pill/docs/index.js +1 -0
  19. data/app/pb_kits/playbook/pb_pill/kit.schema.json +8 -0
  20. data/app/pb_kits/playbook/pb_pill/pill.rb +13 -1
  21. data/app/pb_kits/playbook/pb_select/docs/_playground.json +13 -1
  22. data/app/pb_kits/playbook/pb_select/docs/_playground.overrides.json +9 -0
  23. data/dist/chunks/{_detail-eMZN1lvq.js → _detail-B4-co8rv.js} +1 -1
  24. data/dist/chunks/{_pb_line_graph-CiTPfwy6.js → _pb_line_graph-BGyCJjgG.js} +1 -1
  25. data/dist/chunks/{_table-D5ErUJ1X.js → _table-SGw4kUye.js} +1 -1
  26. data/dist/chunks/_typeahead-sT6HFKNd.js +1 -0
  27. data/dist/chunks/{globalProps-DXDSpbRD.js → globalProps-K1DCn3il.js} +1 -1
  28. data/dist/chunks/{lib-DJySPcHy.js → lib-7vm9BMqp.js} +1 -1
  29. data/dist/chunks/time_picker_helper-CN-3p6mx.js +1 -0
  30. data/dist/chunks/vendor.js +2 -2
  31. data/dist/menu.yml +8 -8
  32. data/dist/playbook-rails-react-bindings.js +1 -1
  33. data/dist/playbook-rails.js +1 -1
  34. data/dist/playbook.css +1 -1
  35. data/lib/playbook/version.rb +1 -1
  36. metadata +13 -9
  37. data/dist/chunks/_typeahead-kaYZtVWA.js +0 -1
  38. 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: d83635cbc918a181e0ef4f93f6125558c127215cde8da52d2ad73730d45f9850
4
- data.tar.gz: 76b6a74dcaa17a95ccc4b8dbea5962b6d83efec85096ac0fa12917548160aad7
3
+ metadata.gz: 02b1ea72561f3498a21c3cf0595c72212fd2e04ba63539688d7239ba7261d6f9
4
+ data.tar.gz: 412dec4b2daa3b7371785dd21701dec9872a188e36418ac13c969025f7c49812
5
5
  SHA512:
6
- metadata.gz: fd2450ff3000b7fcba6606b00144302dde10ccd0a785c8618da1fe023dac56701af9b6baced5a53f4ffb192edf73597d32ffd6c49a622b7693e96cd3c3b1b977
7
- data.tar.gz: 8e636e6d6b2f439bc68991cac04f7f066974b5bc9c001a40d9e1eaa4b4d8b40c2f86a16912b7a1d4e68ede24814178e4c00610e86f46bd467f19fda18ce16347
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(
@@ -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
- // Wait for React component to mount
16
- function waitForComponent() {
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
- }, 500);
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 (e.target.querySelectorAll('[error]:not([error=""])').length > 0) {
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
  });