playbook_ui 16.2.0.pre.rc.0 → 16.2.0.pre.rc.2
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_advanced_table/Components/RegularTableView.tsx +12 -2
- data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +33 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_background_custom.jsx +71 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_background_custom.md +4 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +1 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +2 -1
- data/app/pb_kits/playbook/pb_checkbox/_checkbox.scss +1 -1
- data/app/pb_kits/playbook/pb_checkbox/_checkbox.tsx +17 -0
- data/app/pb_kits/playbook/pb_checkbox/checkbox.html.erb +10 -1
- data/app/pb_kits/playbook/pb_checkbox/checkbox.rb +2 -0
- data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_required_indicator.html.erb +6 -0
- data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_required_indicator.jsx +17 -0
- data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_required_indicator.md +3 -0
- data/app/pb_kits/playbook/pb_checkbox/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_checkbox/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_date_picker/_date_picker.tsx +14 -5
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_default.md +1 -0
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +46 -11
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_label.html.erb +6 -3
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_label.jsx +1 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_label.md +3 -1
- data/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +10 -4
- data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +9 -0
- data/app/pb_kits/playbook/pb_dropdown/dropdown_trigger.html.erb +7 -2
- data/app/pb_kits/playbook/pb_dropdown/dropdown_trigger.rb +4 -0
- data/app/pb_kits/playbook/pb_dropdown/index.js +125 -73
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +16 -0
- data/app/pb_kits/playbook/pb_dropdown/utilities/clickOutsideHelper.tsx +6 -0
- data/app/pb_kits/playbook/pb_form/docs/_form_with_required_indicator.html.erb +6 -3
- data/app/pb_kits/playbook/pb_form/pb_form_validation.js +9 -2
- data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.scss +7 -0
- data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +638 -549
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.html.erb +3 -3
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.jsx +4 -7
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.md +3 -0
- data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.test.jsx +4 -4
- data/app/pb_kits/playbook/pb_passphrase/_passphrase.tsx +20 -2
- data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.tsx +51 -16
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_label.jsx +44 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_label.md +1 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_required_indicator.jsx +1 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_label.jsx +28 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_label.md +1 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_required_indicator.jsx +1 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/index.js +2 -0
- data/app/pb_kits/playbook/pb_table/index.ts +29 -27
- data/app/pb_kits/playbook/pb_text_input/text_input.html.erb +10 -10
- data/app/pb_kits/playbook/pb_textarea/_textarea.tsx +10 -0
- data/app/pb_kits/playbook/pb_textarea/docs/_textarea_default.html.erb +3 -3
- data/app/pb_kits/playbook/pb_textarea/docs/_textarea_default.jsx +3 -0
- data/app/pb_kits/playbook/pb_textarea/docs/_textarea_default.md +1 -0
- data/app/pb_kits/playbook/pb_textarea/textarea.html.erb +25 -9
- data/app/pb_kits/playbook/pb_textarea/textarea.rb +7 -1
- data/app/pb_kits/playbook/pb_time_picker/_time_picker.tsx +97 -11
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_on_handler.jsx +5 -2
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_required_indicator.html.erb +6 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_required_indicator.jsx +16 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_required_indicator.md +3 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_time_picker/time_picker.rb +3 -0
- data/app/pb_kits/playbook/pb_time_picker/time_picker.test.jsx +47 -1
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +410 -323
- data/app/pb_kits/playbook/pb_typeahead/components/Control.tsx +2 -0
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_required_indicator.html.erb +16 -0
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_required_indicator.jsx +23 -0
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_required_indicator.md +3 -0
- data/app/pb_kits/playbook/pb_typeahead/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_typeahead/docs/index.js +22 -21
- data/app/pb_kits/playbook/pb_typeahead/typeahead.html.erb +3 -2
- data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +3 -1
- data/dist/chunks/{_pb_line_graph-BgKF_zz1.js → _pb_line_graph-DuJNCf7N.js} +1 -1
- data/dist/chunks/_typeahead-BKSzddAX.js +1 -0
- data/dist/chunks/{globalProps-BhVYCqRf.js → globalProps-Bc-FVsRt.js} +1 -1
- data/dist/chunks/lib-BwX82vim.js +29 -0
- data/dist/chunks/vendor.js +3 -3
- data/dist/menu.yml +2 -2
- 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/forms/builder/form_field_builder.rb +1 -1
- data/lib/playbook/forms/builder/typeahead_field.rb +15 -1
- data/lib/playbook/forms/builder.rb +2 -2
- data/lib/playbook/version.rb +1 -1
- metadata +24 -6
- data/dist/chunks/_typeahead-Bfy-4mll.js +0 -1
- data/dist/chunks/lib-DD34ZrWL.js +0 -29
|
@@ -14,6 +14,7 @@ const DROPDOWN_INPUT = "#dropdown-selected-option";
|
|
|
14
14
|
const SEARCH_INPUT_SELECTOR = "[data-dropdown-autocomplete]";
|
|
15
15
|
const SEARCH_BAR_SELECTOR = "[data-dropdown-search]";
|
|
16
16
|
const CLEAR_ICON_SELECTOR = "#dropdown_clear_icon";
|
|
17
|
+
const LABEL_SELECTOR = '[data-dropdown="pb-dropdown-label"]';
|
|
17
18
|
|
|
18
19
|
export default class PbDropdown extends PbEnhancedElement {
|
|
19
20
|
static get selector() {
|
|
@@ -30,14 +31,15 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
30
31
|
connect() {
|
|
31
32
|
// Store instance on element for DatePicker sync
|
|
32
33
|
this.element._pbDropdownInstance = this;
|
|
33
|
-
|
|
34
|
+
|
|
34
35
|
this.keyboardHandler = new PbDropdownKeyboard(this);
|
|
35
36
|
this.isMultiSelect = this.element.dataset.pbDropdownMultiSelect === "true";
|
|
36
37
|
this.formPillProps = this.element.dataset.formPillProps
|
|
37
38
|
? JSON.parse(this.element.dataset.formPillProps)
|
|
38
39
|
: {};
|
|
39
40
|
const baseInput = this.element.querySelector(DROPDOWN_INPUT);
|
|
40
|
-
this.wasOriginallyRequired =
|
|
41
|
+
this.wasOriginallyRequired =
|
|
42
|
+
baseInput && baseInput.hasAttribute("required");
|
|
41
43
|
this.setDefaultValue();
|
|
42
44
|
this.bindEventListeners();
|
|
43
45
|
this.bindSearchInput();
|
|
@@ -75,15 +77,24 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
75
77
|
bindEventListeners() {
|
|
76
78
|
const customTrigger =
|
|
77
79
|
this.element.querySelector(CUSTOM_DISPLAY_SELECTOR) || this.element;
|
|
78
|
-
customTrigger.addEventListener("click", () =>
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
customTrigger.addEventListener("click", (e) => {
|
|
81
|
+
const label = e.target.closest(LABEL_SELECTOR);
|
|
82
|
+
if (label && label.htmlFor) {
|
|
83
|
+
const trigger = this.element.querySelector(
|
|
84
|
+
`#${CSS.escape(label.htmlFor)}`,
|
|
85
|
+
);
|
|
86
|
+
if (trigger) {
|
|
87
|
+
trigger.focus();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
this.toggleElement(this.target);
|
|
91
|
+
});
|
|
81
92
|
|
|
82
93
|
this.target.addEventListener("click", this.handleOptionClick.bind(this));
|
|
83
94
|
document.addEventListener(
|
|
84
95
|
"click",
|
|
85
96
|
this.handleDocumentClick.bind(this),
|
|
86
|
-
true
|
|
97
|
+
true,
|
|
87
98
|
);
|
|
88
99
|
}
|
|
89
100
|
|
|
@@ -92,7 +103,7 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
92
103
|
if (!this.searchBar) return;
|
|
93
104
|
|
|
94
105
|
this.searchBar.addEventListener("input", (e) =>
|
|
95
|
-
this.handleSearch(e.target.value)
|
|
106
|
+
this.handleSearch(e.target.value),
|
|
96
107
|
);
|
|
97
108
|
}
|
|
98
109
|
|
|
@@ -107,7 +118,7 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
107
118
|
|
|
108
119
|
// Live filter
|
|
109
120
|
this.searchInput.addEventListener("input", (e) =>
|
|
110
|
-
this.handleSearch(e.target.value)
|
|
121
|
+
this.handleSearch(e.target.value),
|
|
111
122
|
);
|
|
112
123
|
}
|
|
113
124
|
|
|
@@ -161,28 +172,31 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
161
172
|
|
|
162
173
|
handleSearch(term = "") {
|
|
163
174
|
const lcTerm = term.toLowerCase();
|
|
164
|
-
let hasMatch = false
|
|
175
|
+
let hasMatch = false;
|
|
165
176
|
this.element.querySelectorAll(OPTION_SELECTOR).forEach((opt) => {
|
|
166
177
|
//make it so that if the option is selected, it will not show up in the search results
|
|
167
|
-
if (
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
178
|
+
if (
|
|
179
|
+
this.isMultiSelect &&
|
|
180
|
+
this.selectedOptions.has(opt.dataset.dropdownOptionLabel)
|
|
181
|
+
) {
|
|
182
|
+
opt.style.display = "none";
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
171
185
|
const label = JSON.parse(opt.dataset.dropdownOptionLabel)
|
|
172
186
|
.label.toString()
|
|
173
187
|
.toLowerCase();
|
|
174
188
|
|
|
175
|
-
|
|
189
|
+
// hide or show option
|
|
176
190
|
const match = label.includes(lcTerm);
|
|
177
191
|
opt.style.display = match ? "" : "none";
|
|
178
|
-
if (match) hasMatch = true
|
|
192
|
+
if (match) hasMatch = true;
|
|
179
193
|
});
|
|
180
194
|
|
|
181
195
|
this.adjustDropdownHeight();
|
|
182
196
|
|
|
183
|
-
this.removeNoOptionsMessage()
|
|
197
|
+
this.removeNoOptionsMessage();
|
|
184
198
|
if (!hasMatch) {
|
|
185
|
-
this.showNoOptionsMessage()
|
|
199
|
+
this.showNoOptionsMessage();
|
|
186
200
|
}
|
|
187
201
|
}
|
|
188
202
|
|
|
@@ -190,7 +204,8 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
190
204
|
if (this.element.querySelector(".dropdown_no_options")) return;
|
|
191
205
|
|
|
192
206
|
const noOptionElement = document.createElement("div");
|
|
193
|
-
noOptionElement.className =
|
|
207
|
+
noOptionElement.className =
|
|
208
|
+
"pb_body_kit_light dropdown_no_options pb_item_kit p_xs display_flex justify_content_center";
|
|
194
209
|
noOptionElement.textContent = "no option";
|
|
195
210
|
|
|
196
211
|
this.target.appendChild(noOptionElement);
|
|
@@ -241,6 +256,8 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
241
256
|
}
|
|
242
257
|
|
|
243
258
|
isClickOutside(event) {
|
|
259
|
+
const label = event.target.closest(LABEL_SELECTOR);
|
|
260
|
+
if (label && this.element.contains(label)) return false;
|
|
244
261
|
const customTrigger = this.element.querySelector(CUSTOM_DISPLAY_SELECTOR);
|
|
245
262
|
if (customTrigger) {
|
|
246
263
|
return !customTrigger.contains(event.target);
|
|
@@ -271,8 +288,8 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
271
288
|
? JSON.parse(
|
|
272
289
|
this.element.querySelector(
|
|
273
290
|
OPTION_SELECTOR +
|
|
274
|
-
`[data-dropdown-option-label*='"id":"${hiddenInput.value}"']
|
|
275
|
-
).dataset.dropdownOptionLabel
|
|
291
|
+
`[data-dropdown-option-label*='"id":"${hiddenInput.value}"']`,
|
|
292
|
+
).dataset.dropdownOptionLabel,
|
|
276
293
|
)
|
|
277
294
|
: null;
|
|
278
295
|
}
|
|
@@ -281,14 +298,14 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
281
298
|
new CustomEvent("pb:dropdown:selected", {
|
|
282
299
|
detail,
|
|
283
300
|
bubbles: true,
|
|
284
|
-
})
|
|
301
|
+
}),
|
|
285
302
|
);
|
|
286
303
|
}
|
|
287
304
|
|
|
288
305
|
onOptionSelected(value, selectedOption) {
|
|
289
306
|
const triggerElement = this.element.querySelector(DROPDOWN_TRIGGER_DISPLAY);
|
|
290
307
|
const customDisplayElement = this.element.querySelector(
|
|
291
|
-
"#dropdown_trigger_custom_display"
|
|
308
|
+
"#dropdown_trigger_custom_display",
|
|
292
309
|
);
|
|
293
310
|
|
|
294
311
|
if (triggerElement) {
|
|
@@ -296,36 +313,46 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
296
313
|
const selectedLabel = JSON.parse(value).label;
|
|
297
314
|
triggerElement.textContent = selectedLabel;
|
|
298
315
|
this.emitSelectionChange();
|
|
299
|
-
|
|
316
|
+
|
|
300
317
|
// Handle quickpick variant: populate start/end date hidden inputs
|
|
301
318
|
const optionData = JSON.parse(value);
|
|
302
319
|
const startDateId = this.element.dataset.startDateId;
|
|
303
320
|
const endDateId = this.element.dataset.endDateId;
|
|
304
321
|
const controlsStartId = this.element.dataset.controlsStartId;
|
|
305
322
|
const controlsEndId = this.element.dataset.controlsEndId;
|
|
306
|
-
|
|
323
|
+
|
|
307
324
|
if (optionData.formatted_start_date && optionData.formatted_end_date) {
|
|
308
325
|
// Populate date inputs when option has date fields
|
|
309
326
|
if (startDateId) {
|
|
310
327
|
const startDateInput = document.getElementById(startDateId);
|
|
311
|
-
if (startDateInput)
|
|
328
|
+
if (startDateInput)
|
|
329
|
+
startDateInput.value = optionData.formatted_start_date;
|
|
312
330
|
}
|
|
313
|
-
|
|
331
|
+
|
|
314
332
|
if (endDateId) {
|
|
315
333
|
const endDateInput = document.getElementById(endDateId);
|
|
316
|
-
if (endDateInput)
|
|
334
|
+
if (endDateInput)
|
|
335
|
+
endDateInput.value = optionData.formatted_end_date;
|
|
317
336
|
}
|
|
318
|
-
|
|
337
|
+
|
|
319
338
|
// Sync with DatePickers if controlsStartId/controlsEndId are present
|
|
320
339
|
if (controlsStartId) {
|
|
321
|
-
const startPicker = document.querySelector(
|
|
340
|
+
const startPicker = document.querySelector(
|
|
341
|
+
`#${controlsStartId}`,
|
|
342
|
+
)?._flatpickr;
|
|
322
343
|
if (startPicker) {
|
|
323
|
-
startPicker.setDate(
|
|
344
|
+
startPicker.setDate(
|
|
345
|
+
optionData.formatted_start_date,
|
|
346
|
+
true,
|
|
347
|
+
"m/d/Y",
|
|
348
|
+
);
|
|
324
349
|
}
|
|
325
350
|
}
|
|
326
|
-
|
|
351
|
+
|
|
327
352
|
if (controlsEndId) {
|
|
328
|
-
const endPicker = document.querySelector(
|
|
353
|
+
const endPicker = document.querySelector(
|
|
354
|
+
`#${controlsEndId}`,
|
|
355
|
+
)?._flatpickr;
|
|
329
356
|
if (endPicker) {
|
|
330
357
|
endPicker.setDate(optionData.formatted_end_date, true, "m/d/Y");
|
|
331
358
|
}
|
|
@@ -336,22 +363,26 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
336
363
|
const startDateInput = document.getElementById(startDateId);
|
|
337
364
|
if (startDateInput) startDateInput.value = "";
|
|
338
365
|
}
|
|
339
|
-
|
|
366
|
+
|
|
340
367
|
if (endDateId) {
|
|
341
368
|
const endDateInput = document.getElementById(endDateId);
|
|
342
369
|
if (endDateInput) endDateInput.value = "";
|
|
343
370
|
}
|
|
344
|
-
|
|
371
|
+
|
|
345
372
|
// Clear DatePickers as well
|
|
346
373
|
if (controlsStartId) {
|
|
347
|
-
const startPicker = document.querySelector(
|
|
374
|
+
const startPicker = document.querySelector(
|
|
375
|
+
`#${controlsStartId}`,
|
|
376
|
+
)?._flatpickr;
|
|
348
377
|
if (startPicker) {
|
|
349
378
|
startPicker.clear();
|
|
350
379
|
}
|
|
351
380
|
}
|
|
352
|
-
|
|
381
|
+
|
|
353
382
|
if (controlsEndId) {
|
|
354
|
-
const endPicker = document.querySelector(
|
|
383
|
+
const endPicker = document.querySelector(
|
|
384
|
+
`#${controlsEndId}`,
|
|
385
|
+
)?._flatpickr;
|
|
355
386
|
if (endPicker) {
|
|
356
387
|
endPicker.clear();
|
|
357
388
|
}
|
|
@@ -391,7 +422,9 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
391
422
|
this.adjustDropdownHeight();
|
|
392
423
|
}
|
|
393
424
|
});
|
|
394
|
-
this.element.querySelector(DROPDOWN_INPUT).value = Array.from(
|
|
425
|
+
this.element.querySelector(DROPDOWN_INPUT).value = Array.from(
|
|
426
|
+
this.selectedOptions,
|
|
427
|
+
)
|
|
395
428
|
.map((opt) => JSON.parse(opt).id)
|
|
396
429
|
.join(",");
|
|
397
430
|
} else {
|
|
@@ -437,7 +470,7 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
437
470
|
this.keyboardHandler.focusedOptionIndex = -1;
|
|
438
471
|
const options = this.element.querySelectorAll(OPTION_SELECTOR);
|
|
439
472
|
options.forEach((option) =>
|
|
440
|
-
option.classList.remove("pb_dropdown_option_focused")
|
|
473
|
+
option.classList.remove("pb_dropdown_option_focused"),
|
|
441
474
|
);
|
|
442
475
|
}
|
|
443
476
|
}
|
|
@@ -472,7 +505,7 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
472
505
|
hiddenInput.closest(".dropdown_wrapper").classList.add("error");
|
|
473
506
|
}
|
|
474
507
|
},
|
|
475
|
-
true
|
|
508
|
+
true,
|
|
476
509
|
);
|
|
477
510
|
}
|
|
478
511
|
|
|
@@ -482,7 +515,7 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
482
515
|
const dropdownWrapperElement = input.closest(".dropdown_wrapper");
|
|
483
516
|
dropdownWrapperElement.classList.remove("error");
|
|
484
517
|
const errorLabelElement = dropdownWrapperElement.querySelector(
|
|
485
|
-
".pb_body_kit_negative"
|
|
518
|
+
".pb_body_kit_negative",
|
|
486
519
|
);
|
|
487
520
|
if (errorLabelElement) {
|
|
488
521
|
errorLabelElement.remove();
|
|
@@ -490,13 +523,13 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
490
523
|
return;
|
|
491
524
|
}
|
|
492
525
|
}
|
|
493
|
-
|
|
526
|
+
|
|
494
527
|
if (input.checkValidity()) {
|
|
495
528
|
const dropdownWrapperElement = input.closest(".dropdown_wrapper");
|
|
496
529
|
dropdownWrapperElement.classList.remove("error");
|
|
497
530
|
|
|
498
531
|
const errorLabelElement = dropdownWrapperElement.querySelector(
|
|
499
|
-
".pb_body_kit_negative"
|
|
532
|
+
".pb_body_kit_negative",
|
|
500
533
|
);
|
|
501
534
|
if (errorLabelElement) {
|
|
502
535
|
errorLabelElement.remove();
|
|
@@ -507,7 +540,7 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
507
540
|
setDefaultValue() {
|
|
508
541
|
const hiddenInput = this.element.querySelector(DROPDOWN_INPUT);
|
|
509
542
|
const optionEls = Array.from(
|
|
510
|
-
this.element.querySelectorAll(OPTION_SELECTOR)
|
|
543
|
+
this.element.querySelectorAll(OPTION_SELECTOR),
|
|
511
544
|
);
|
|
512
545
|
const defaultValue = hiddenInput.dataset.defaultValue || "";
|
|
513
546
|
if (!defaultValue) return;
|
|
@@ -553,44 +586,53 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
553
586
|
selectedOption.classList.add("pb_dropdown_option_selected");
|
|
554
587
|
const optionData = JSON.parse(selectedOption.dataset.dropdownOptionLabel);
|
|
555
588
|
this.setTriggerElementText(optionData.label);
|
|
556
|
-
|
|
589
|
+
|
|
557
590
|
// Handle quickpick variant: populate start/end date hidden inputs and sync DatePickers
|
|
558
591
|
if (optionData.formatted_start_date && optionData.formatted_end_date) {
|
|
559
592
|
const startDateId = this.element.dataset.startDateId;
|
|
560
593
|
const endDateId = this.element.dataset.endDateId;
|
|
561
594
|
const controlsStartId = this.element.dataset.controlsStartId;
|
|
562
595
|
const controlsEndId = this.element.dataset.controlsEndId;
|
|
563
|
-
|
|
596
|
+
|
|
564
597
|
if (startDateId) {
|
|
565
598
|
const startDateInput = document.getElementById(startDateId);
|
|
566
|
-
if (startDateInput)
|
|
599
|
+
if (startDateInput)
|
|
600
|
+
startDateInput.value = optionData.formatted_start_date;
|
|
567
601
|
}
|
|
568
|
-
|
|
602
|
+
|
|
569
603
|
if (endDateId) {
|
|
570
604
|
const endDateInput = document.getElementById(endDateId);
|
|
571
605
|
if (endDateInput) endDateInput.value = optionData.formatted_end_date;
|
|
572
606
|
}
|
|
573
|
-
|
|
607
|
+
|
|
574
608
|
// Sync with DatePickers - retry with delays to ensure DatePickers are initialized
|
|
575
609
|
const syncDatePickers = () => {
|
|
576
610
|
if (controlsStartId) {
|
|
577
|
-
const startPicker = document.querySelector(
|
|
611
|
+
const startPicker = document.querySelector(
|
|
612
|
+
`#${controlsStartId}`,
|
|
613
|
+
)?._flatpickr;
|
|
578
614
|
if (startPicker) {
|
|
579
|
-
startPicker.setDate(
|
|
615
|
+
startPicker.setDate(
|
|
616
|
+
optionData.formatted_start_date,
|
|
617
|
+
true,
|
|
618
|
+
"m/d/Y",
|
|
619
|
+
);
|
|
580
620
|
}
|
|
581
621
|
}
|
|
582
|
-
|
|
622
|
+
|
|
583
623
|
if (controlsEndId) {
|
|
584
|
-
const endPicker = document.querySelector(
|
|
624
|
+
const endPicker = document.querySelector(
|
|
625
|
+
`#${controlsEndId}`,
|
|
626
|
+
)?._flatpickr;
|
|
585
627
|
if (endPicker) {
|
|
586
628
|
endPicker.setDate(optionData.formatted_end_date, true, "m/d/Y");
|
|
587
629
|
}
|
|
588
630
|
}
|
|
589
631
|
};
|
|
590
|
-
|
|
632
|
+
|
|
591
633
|
// Try immediately
|
|
592
634
|
syncDatePickers();
|
|
593
|
-
|
|
635
|
+
|
|
594
636
|
// Retry after short delay in case DatePickers aren't ready yet
|
|
595
637
|
setTimeout(syncDatePickers, 100);
|
|
596
638
|
setTimeout(syncDatePickers, 300);
|
|
@@ -653,7 +695,7 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
653
695
|
|
|
654
696
|
const wrapper = this.element.querySelector("#dropdown_pills_wrapper");
|
|
655
697
|
const placeholder = this.element.querySelector(
|
|
656
|
-
"#dropdown_trigger_display_multi_select"
|
|
698
|
+
"#dropdown_trigger_display_multi_select",
|
|
657
699
|
);
|
|
658
700
|
if (!wrapper) return;
|
|
659
701
|
|
|
@@ -671,7 +713,12 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
671
713
|
// Create a form pill for each selected option
|
|
672
714
|
const pill = document.createElement("div");
|
|
673
715
|
const color = this.formPillProps.color || "primary";
|
|
674
|
-
pill.classList.add(
|
|
716
|
+
pill.classList.add(
|
|
717
|
+
"pb_form_pill_kit",
|
|
718
|
+
`pb_form_pill_${color}`,
|
|
719
|
+
"pb_form_pill_none",
|
|
720
|
+
"mr_xs",
|
|
721
|
+
);
|
|
675
722
|
if (this.formPillProps.size === "small") {
|
|
676
723
|
pill.classList.add("pb_form_pill_small");
|
|
677
724
|
}
|
|
@@ -696,8 +743,8 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
696
743
|
|
|
697
744
|
const optEl = this.element.querySelector(
|
|
698
745
|
`${OPTION_SELECTOR}[data-dropdown-option-label*='"id":${JSON.stringify(
|
|
699
|
-
id
|
|
700
|
-
)}']
|
|
746
|
+
id,
|
|
747
|
+
)}']`,
|
|
701
748
|
);
|
|
702
749
|
if (optEl) {
|
|
703
750
|
optEl.style.display = "";
|
|
@@ -726,18 +773,18 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
726
773
|
}
|
|
727
774
|
}
|
|
728
775
|
const customDisplay = this.element.querySelector(
|
|
729
|
-
"#dropdown_trigger_custom_display"
|
|
776
|
+
"#dropdown_trigger_custom_display",
|
|
730
777
|
);
|
|
731
778
|
if (customDisplay) {
|
|
732
779
|
customDisplay.style.display = "none";
|
|
733
780
|
}
|
|
734
|
-
|
|
781
|
+
|
|
735
782
|
// Clear quickpick hidden inputs
|
|
736
783
|
const startDateId = this.element.dataset.startDateId;
|
|
737
784
|
const endDateId = this.element.dataset.endDateId;
|
|
738
785
|
const controlsStartId = this.element.dataset.controlsStartId;
|
|
739
786
|
const controlsEndId = this.element.dataset.controlsEndId;
|
|
740
|
-
|
|
787
|
+
|
|
741
788
|
if (startDateId) {
|
|
742
789
|
const startDateInput = document.getElementById(startDateId);
|
|
743
790
|
if (startDateInput) startDateInput.value = "";
|
|
@@ -746,22 +793,24 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
746
793
|
const endDateInput = document.getElementById(endDateId);
|
|
747
794
|
if (endDateInput) endDateInput.value = "";
|
|
748
795
|
}
|
|
749
|
-
|
|
796
|
+
|
|
750
797
|
// Clear linked DatePickers if controlsStartId/controlsEndId are present
|
|
751
798
|
if (controlsStartId) {
|
|
752
|
-
const startPicker = document.querySelector(
|
|
799
|
+
const startPicker = document.querySelector(
|
|
800
|
+
`#${controlsStartId}`,
|
|
801
|
+
)?._flatpickr;
|
|
753
802
|
if (startPicker) {
|
|
754
803
|
startPicker.clear();
|
|
755
804
|
}
|
|
756
805
|
}
|
|
757
|
-
|
|
806
|
+
|
|
758
807
|
if (controlsEndId) {
|
|
759
808
|
const endPicker = document.querySelector(`#${controlsEndId}`)?._flatpickr;
|
|
760
809
|
if (endPicker) {
|
|
761
810
|
endPicker.clear();
|
|
762
811
|
}
|
|
763
812
|
}
|
|
764
|
-
|
|
813
|
+
|
|
765
814
|
this.resetDropdownValue();
|
|
766
815
|
this.updatePills();
|
|
767
816
|
this.updateClearButton();
|
|
@@ -772,21 +821,24 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
772
821
|
// Method for DatePicker sync - only clears the dropdown, not the DatePickers
|
|
773
822
|
clearSelected() {
|
|
774
823
|
// Only clear if this is a single-select quickpick variant
|
|
775
|
-
if (
|
|
824
|
+
if (
|
|
825
|
+
this.element.dataset.pbDropdownVariant !== "quickpick" ||
|
|
826
|
+
this.isMultiSelect
|
|
827
|
+
) {
|
|
776
828
|
return;
|
|
777
829
|
}
|
|
778
|
-
|
|
830
|
+
|
|
779
831
|
const customDisplay = this.element.querySelector(
|
|
780
|
-
"#dropdown_trigger_custom_display"
|
|
832
|
+
"#dropdown_trigger_custom_display",
|
|
781
833
|
);
|
|
782
834
|
if (customDisplay) {
|
|
783
835
|
customDisplay.style.display = "none";
|
|
784
836
|
}
|
|
785
|
-
|
|
837
|
+
|
|
786
838
|
// Clear quickpick hidden inputs only (not the DatePickers)
|
|
787
839
|
const startDateId = this.element.dataset.startDateId;
|
|
788
840
|
const endDateId = this.element.dataset.endDateId;
|
|
789
|
-
|
|
841
|
+
|
|
790
842
|
if (startDateId) {
|
|
791
843
|
const startDateInput = document.getElementById(startDateId);
|
|
792
844
|
if (startDateInput) startDateInput.value = "";
|
|
@@ -795,7 +847,7 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
795
847
|
const endDateInput = document.getElementById(endDateId);
|
|
796
848
|
if (endDateInput) endDateInput.value = "";
|
|
797
849
|
}
|
|
798
|
-
|
|
850
|
+
|
|
799
851
|
this.resetDropdownValue();
|
|
800
852
|
this.updateClearButton();
|
|
801
853
|
this.emitSelectionChange();
|
|
@@ -822,7 +874,7 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
822
874
|
inp.dataset.generated = "true";
|
|
823
875
|
baseInput.insertAdjacentElement("afterend", inp);
|
|
824
876
|
});
|
|
825
|
-
|
|
877
|
+
|
|
826
878
|
// For multi-select, remove required from base input when there are selections
|
|
827
879
|
// The generated inputs handle the form submission with actual values
|
|
828
880
|
// Restore required attribute when there are no selections (if it was originally required)
|
|
@@ -45,6 +45,8 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
|
45
45
|
const {
|
|
46
46
|
autocomplete,
|
|
47
47
|
clearable,
|
|
48
|
+
error,
|
|
49
|
+
errorId,
|
|
48
50
|
filterItem,
|
|
49
51
|
handleBackspace,
|
|
50
52
|
handleChange,
|
|
@@ -53,8 +55,10 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
|
53
55
|
inputWrapperRef,
|
|
54
56
|
isDropDownClosed,
|
|
55
57
|
isInputFocused,
|
|
58
|
+
label: contextLabel,
|
|
56
59
|
multiSelect,
|
|
57
60
|
selected,
|
|
61
|
+
selectId,
|
|
58
62
|
setIsInputFocused,
|
|
59
63
|
toggleDropdown,
|
|
60
64
|
} = useContext(DropdownContext);
|
|
@@ -104,6 +108,10 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
|
104
108
|
? placeholder
|
|
105
109
|
: "Select...";
|
|
106
110
|
|
|
111
|
+
const triggerAriaLabel = contextLabel
|
|
112
|
+
? (children ? contextLabel : `${contextLabel}, ${defaultDisplayPlaceholder}`)
|
|
113
|
+
: undefined;
|
|
114
|
+
|
|
107
115
|
return (
|
|
108
116
|
<div {...ariaProps}
|
|
109
117
|
{...dataProps}
|
|
@@ -114,6 +122,10 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
|
114
122
|
{
|
|
115
123
|
children ? (
|
|
116
124
|
<div
|
|
125
|
+
aria-describedby={errorId}
|
|
126
|
+
aria-invalid={!!error}
|
|
127
|
+
aria-label={triggerAriaLabel}
|
|
128
|
+
id={selectId}
|
|
117
129
|
onClick={() => toggleDropdown()}
|
|
118
130
|
onKeyDown= {handleKeyDown}
|
|
119
131
|
ref={inputWrapperRef}
|
|
@@ -130,6 +142,10 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
|
130
142
|
className={triggerWrapperClasses}
|
|
131
143
|
cursor={`${autocomplete ? "text" : "pointer"}`}
|
|
132
144
|
htmlOptions={{
|
|
145
|
+
"aria-describedby": errorId,
|
|
146
|
+
"aria-invalid": !!error,
|
|
147
|
+
"aria-label": triggerAriaLabel,
|
|
148
|
+
id: selectId,
|
|
133
149
|
onClick: () => handleWrapperClick(),
|
|
134
150
|
onKeyDown: handleKeyDown,
|
|
135
151
|
tabIndex: "0",
|
|
@@ -25,6 +25,12 @@ export const handleClickOutside =
|
|
|
25
25
|
) {
|
|
26
26
|
shouldClose = false;
|
|
27
27
|
}
|
|
28
|
+
// Target dropdown container to open dropdown
|
|
29
|
+
if (
|
|
30
|
+
targetElement.getAttribute("data-dropdown") === "pb-dropdown-label"
|
|
31
|
+
) {
|
|
32
|
+
shouldClose = false;
|
|
33
|
+
}
|
|
28
34
|
targetElement = targetElement.parentElement as HTMLElement;
|
|
29
35
|
}
|
|
30
36
|
if (
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
<%= pb_form_with(scope: :example, url: "", method: :get, validate: true) do |form| %>
|
|
2
|
+
<%= form.typeahead :example_typeahead_field, props: { label: true, required: true, required_indicator: true } %>
|
|
2
3
|
<%= form.text_field :example_text_field, props: { label: true, required: true, required_indicator: true } %>
|
|
3
4
|
<%= form.text_field :example_text_field_2, props: { label: "Text Field Custom Label", required: true, required_indicator: true } %>
|
|
4
|
-
<%= form.
|
|
5
|
-
<%= form.text_area :example_text_area_2, props: { label: "Textarea Custom Label", required: true, required_indicator: true } %>
|
|
5
|
+
<%= form.phone_number_field :example_phone_number_field, props: { label: true, required: true, required_indicator: true } %>
|
|
6
6
|
<%= form.email_field :example_email_field, props: { label: true, required: true, required_indicator: true } %>
|
|
7
7
|
<%= form.number_field :example_number_field, props: { label: true, required: true, required_indicator: true } %>
|
|
8
8
|
<%= form.search_field :example_search_field, props: { label: true, required: true, required_indicator: true } %>
|
|
9
9
|
<%= form.password_field :example_password_field, props: { label: true, required: true, required_indicator: true } %>
|
|
10
10
|
<%= form.url_field :example_url_field, props: { label: true, required: true, required_indicator: true } %>
|
|
11
|
-
<%= form.
|
|
11
|
+
<%= form.text_area :example_text_area, props: { label: true, required: true, required_indicator: true } %>
|
|
12
|
+
<%= form.text_area :example_text_area_2, props: { label: "Textarea Custom Label", required: true, required_indicator: true } %>
|
|
13
|
+
<%# <%= form.check_box :example_checkbox, props: { label: true, text: "Checkbox Label", required: true, required_indicator: true } %>
|
|
14
|
+
<%= form.time_picker :example_time_picker_required_indicator, props: { label: true, required: true, required_indicator: true } %>
|
|
12
15
|
|
|
13
16
|
<%= form.actions do |action| %>
|
|
14
17
|
<%= action.submit %>
|
|
@@ -26,6 +26,10 @@ class PbFormValidation extends PbEnhancedElement {
|
|
|
26
26
|
const isPhoneNumberInput = field.closest('.pb_phone_number_input')
|
|
27
27
|
if (isPhoneNumberInput) return
|
|
28
28
|
|
|
29
|
+
// Skip TimePicker inputs - they handle their own validation
|
|
30
|
+
const isTimePickerInput = field.closest('.pb_time_picker')
|
|
31
|
+
if (isTimePickerInput) return
|
|
32
|
+
|
|
29
33
|
FIELD_EVENTS.forEach((e) => {
|
|
30
34
|
field.addEventListener(e, debounce((event) => {
|
|
31
35
|
this.validateFormField(event)
|
|
@@ -67,13 +71,16 @@ class PbFormValidation extends PbEnhancedElement {
|
|
|
67
71
|
|
|
68
72
|
// Check if this is a phone number input
|
|
69
73
|
const isPhoneNumberInput = kitElement.classList.contains('pb_phone_number_input')
|
|
74
|
+
|
|
75
|
+
// Check if this is a TimePicker input
|
|
76
|
+
const isTimePickerInput = kitElement.classList.contains('pb_time_picker')
|
|
70
77
|
|
|
71
78
|
// ensure clean error message state
|
|
72
79
|
this.clearError(target)
|
|
73
80
|
kitElement.classList.add('error')
|
|
74
81
|
|
|
75
|
-
// Only add error message if it's NOT a phone number input
|
|
76
|
-
if (!isPhoneNumberInput) {
|
|
82
|
+
// Only add error message if it's NOT a phone number input or TimePicker input
|
|
83
|
+
if (!isPhoneNumberInput && !isTimePickerInput) {
|
|
77
84
|
// set the error message element
|
|
78
85
|
const errorMessageContainer = this.errorMessageContainer
|
|
79
86
|
|
|
@@ -39,6 +39,13 @@
|
|
|
39
39
|
display: flex;
|
|
40
40
|
align-items: center;
|
|
41
41
|
justify-content: space-between;
|
|
42
|
+
@include transition_default;
|
|
43
|
+
|
|
44
|
+
&:focus-within {
|
|
45
|
+
border-color: $primary;
|
|
46
|
+
background-color: $focus_input_light;
|
|
47
|
+
}
|
|
48
|
+
|
|
42
49
|
.input_inner_container {
|
|
43
50
|
width: 100%;
|
|
44
51
|
}
|