playbook_ui 14.18.0.pre.alpha.PLAY20397473 → 14.18.0.pre.alpha.dropdownautocomplete7493

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +304 -21
  3. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +5 -1
  4. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.rb +6 -1
  5. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_border_color.jsx +80 -0
  6. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_border_color.md +3 -0
  7. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_border_color_rails.html.erb +58 -0
  8. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_border_color_rails.md +3 -0
  9. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +3 -2
  10. data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +2 -1
  11. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_error.html.erb +2 -2
  12. data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +5 -0
  13. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +5 -16
  14. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_error.html.erb +5 -2
  15. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_autocomplete.html.erb +28 -0
  16. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_autocomplete.jsx +17 -64
  17. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_autocomplete_with_subcomponents.html.erb +58 -0
  18. data/app/pb_kits/playbook/pb_dropdown/docs/{_dropdown_with_autocomplete_and_custom_display.jsx → _dropdown_with_autocomplete_with_subcomponents.jsx} +11 -25
  19. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_autocomplete_with_subcomponents.md +1 -0
  20. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_external_control.md +1 -0
  21. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_search.jsx +61 -0
  22. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_search.md +2 -0
  23. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_search_rails.html.erb +52 -0
  24. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_search_rails.md +2 -0
  25. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +7 -4
  26. data/app/pb_kits/playbook/pb_dropdown/docs/index.js +2 -2
  27. data/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +2 -2
  28. data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +4 -0
  29. data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +45 -1
  30. data/app/pb_kits/playbook/pb_dropdown/dropdown_container.html.erb +10 -0
  31. data/app/pb_kits/playbook/pb_dropdown/dropdown_container.rb +3 -0
  32. data/app/pb_kits/playbook/pb_dropdown/dropdown_trigger.html.erb +12 -2
  33. data/app/pb_kits/playbook/pb_dropdown/dropdown_trigger.rb +3 -1
  34. data/app/pb_kits/playbook/pb_dropdown/index.js +57 -0
  35. data/app/pb_kits/playbook/pb_dropdown/keyboard_accessibility.js +26 -0
  36. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownContainer.tsx +1 -2
  37. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +4 -4
  38. data/app/pb_kits/playbook/pb_select/docs/_select_error.html.erb +1 -1
  39. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_error.html.erb +1 -1
  40. data/app/pb_kits/playbook/pb_textarea/docs/_textarea_error.html.erb +5 -1
  41. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_error_state.html.erb +8 -1
  42. data/dist/chunks/_weekday_stacked-Da8b-KUp.js +45 -0
  43. data/dist/chunks/vendor.js +1 -1
  44. data/dist/playbook-doc.js +1 -1
  45. data/dist/playbook-rails.js +1 -1
  46. data/dist/playbook.css +1 -1
  47. data/lib/playbook/version.rb +1 -1
  48. metadata +16 -6
  49. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_autocomplete_and_custom_display.md +0 -1
  50. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_hook.jsx +0 -79
  51. data/dist/chunks/_weekday_stacked-CHQsoCdP.js +0 -45
@@ -8,7 +8,7 @@
8
8
  align: "center",
9
9
  border_radius:"lg",
10
10
  classname: object.trigger_wrapper_classes,
11
- cursor: "pointer",
11
+ cursor: object.autocomplete ? "text" : "pointer",
12
12
  justify: "between",
13
13
  padding_x:"sm",
14
14
  padding_y:"xs",
@@ -24,7 +24,17 @@
24
24
  <%= pb_rails("body", props: {text: object.default_display_placeholder, id: "dropdown_trigger_display"}) %>
25
25
  <% end %>
26
26
  <% else %>
27
- <%= pb_rails("body", props: {text: object.default_display_placeholder, id: "dropdown_trigger_display"}) %>
27
+ <% if object.autocomplete %>
28
+ <input
29
+ data-dropdown-autocomplete
30
+ class="dropdown_input"
31
+ type="text"
32
+ placeholder="<%= object.placeholder || 'Select…' %>"
33
+ autocomplete="off"
34
+ />
35
+ <% else %>
36
+ <%= pb_rails("body", props: {text: object.default_display_placeholder, id: "dropdown_trigger_display"}) %>
37
+ <% end %>
28
38
  <% end %>
29
39
  <% end %>
30
40
  <% end %>
@@ -9,6 +9,8 @@ module Playbook
9
9
  default: ""
10
10
  prop :placeholder, type: Playbook::Props::String
11
11
  prop :custom_display
12
+ prop :autocomplete, type: Playbook::Props::Boolean,
13
+ default: false
12
14
 
13
15
  def data
14
16
  Hash(prop(:data)).merge(dropdown_trigger: true, dropdown_placeholder: default_display_placeholder)
@@ -23,7 +25,7 @@ module Playbook
23
25
  end
24
26
 
25
27
  def trigger_wrapper_classes
26
- generate_classname("dropdown_trigger_wrapper", "select_only")
28
+ generate_classname("dropdown_trigger_wrapper", ("select_only" unless autocomplete))
27
29
  end
28
30
  end
29
31
  end
@@ -11,6 +11,8 @@ const CUSTOM_DISPLAY_SELECTOR = "[data-dropdown-custom-trigger]";
11
11
  const DROPDOWN_TRIGGER_DISPLAY = "#dropdown_trigger_display";
12
12
  const DROPDOWN_PLACEHOLDER = "[data-dropdown-placeholder]";
13
13
  const DROPDOWN_INPUT = "#dropdown-selected-option";
14
+ const SEARCH_INPUT_SELECTOR = "[data-dropdown-autocomplete]";
15
+ const SEARCH_BAR_SELECTOR = "[data-dropdown-search]";
14
16
 
15
17
  export default class PbDropdown extends PbEnhancedElement {
16
18
  static get selector() {
@@ -25,9 +27,11 @@ export default class PbDropdown extends PbEnhancedElement {
25
27
  this.keyboardHandler = new PbDropdownKeyboard(this);
26
28
  this.setDefaultValue();
27
29
  this.bindEventListeners();
30
+ this.bindSearchInput();
28
31
  this.updateArrowDisplay(false);
29
32
  this.handleFormValidation();
30
33
  this.handleFormReset();
34
+ this.bindSearchBar();
31
35
  }
32
36
 
33
37
  bindEventListeners() {
@@ -45,6 +49,53 @@ export default class PbDropdown extends PbEnhancedElement {
45
49
  );
46
50
  }
47
51
 
52
+ bindSearchBar() {
53
+ this.searchBar = this.element.querySelector(SEARCH_BAR_SELECTOR);
54
+ if (!this.searchBar) return;
55
+
56
+ this.searchBar.addEventListener("input", (e) =>
57
+ this.handleSearch(e.target.value)
58
+ );
59
+ }
60
+
61
+ bindSearchInput() {
62
+ this.searchInput = this.element.querySelector(SEARCH_INPUT_SELECTOR);
63
+ if (!this.searchInput) return;
64
+
65
+ // Focus the input when anyone clicks the wrapper
66
+ this.element
67
+ .querySelector(TRIGGER_SELECTOR)
68
+ ?.addEventListener("click", () => this.searchInput.focus());
69
+
70
+ // Live filter
71
+ this.searchInput.addEventListener("input", (e) =>
72
+ this.handleSearch(e.target.value)
73
+ );
74
+ }
75
+
76
+ handleSearch(term = "") {
77
+ const lcTerm = term.toLowerCase();
78
+ this.element.querySelectorAll(OPTION_SELECTOR).forEach((opt) => {
79
+ const label = JSON.parse(opt.dataset.dropdownOptionLabel).label
80
+ .toString()
81
+ .toLowerCase();
82
+
83
+ // hide or show option
84
+ const match = label.includes(lcTerm);
85
+ opt.style.display = match ? "" : "none";
86
+ });
87
+
88
+ if (this.target.classList.contains("open")) {
89
+ const el = this.target;
90
+ el.style.height = "auto";
91
+ requestAnimationFrame(() => {
92
+ const newHeight = el.scrollHeight + "px";
93
+ el.offsetHeight; // force reflow
94
+ el.style.height = newHeight;
95
+ });
96
+ }
97
+ }
98
+
48
99
  handleOptionClick(event) {
49
100
  const option = event.target.closest(OPTION_SELECTOR);
50
101
  const hiddenInput = this.element.querySelector(DROPDOWN_INPUT);
@@ -59,6 +110,7 @@ export default class PbDropdown extends PbEnhancedElement {
59
110
  }
60
111
 
61
112
  handleDocumentClick(event) {
113
+ if (event.target.closest(SEARCH_BAR_SELECTOR)) return;
62
114
  if (this.isClickOutside(event) && this.target.classList.contains("open")) {
63
115
  this.hideElement(this.target);
64
116
  this.updateArrowDisplay(false);
@@ -99,6 +151,11 @@ export default class PbDropdown extends PbEnhancedElement {
99
151
  }
100
152
  }
101
153
 
154
+ const autocompleteInput = this.element.querySelector(SEARCH_INPUT_SELECTOR);
155
+ if (autocompleteInput){
156
+ autocompleteInput.value = JSON.parse(value).label;
157
+ }
158
+
102
159
  const customTrigger = this.element.querySelector(CUSTOM_DISPLAY_SELECTOR);
103
160
  if (customTrigger) {
104
161
  if (this.target.classList.contains("open")) {
@@ -1,4 +1,6 @@
1
1
  const OPTION_SELECTOR = "[data-dropdown-option-label]";
2
+ const SEARCH_INPUT_SELECTOR = "[data-dropdown-autocomplete]";
3
+
2
4
  export class PbDropdownKeyboard {
3
5
  constructor(dropdown) {
4
6
  this.dropdown = dropdown;
@@ -7,6 +9,9 @@ export class PbDropdownKeyboard {
7
9
  this.dropdownElement.querySelectorAll(OPTION_SELECTOR)
8
10
  );
9
11
  this.focusedOptionIndex = -1;
12
+ this.searchInput = this.dropdownElement.querySelector(
13
+ SEARCH_INPUT_SELECTOR
14
+ );
10
15
  this.init();
11
16
  }
12
17
 
@@ -15,6 +20,18 @@ export class PbDropdownKeyboard {
15
20
  "keydown",
16
21
  this.handleKeyDown.bind(this)
17
22
  );
23
+ if (this.searchInput) {
24
+ this.searchInput.addEventListener("input", () =>
25
+ this.openDropdownIfClosed()
26
+ );
27
+ }
28
+ }
29
+
30
+ openDropdownIfClosed() {
31
+ if (!this.dropdown.target.classList.contains("open")) {
32
+ this.dropdown.showElement(this.dropdown.target);
33
+ this.dropdown.updateArrowDisplay(true);
34
+ }
18
35
  }
19
36
 
20
37
  handleKeyDown(event) {
@@ -50,6 +67,15 @@ export class PbDropdownKeyboard {
50
67
  this.dropdown.updateArrowDisplay(false);
51
68
  this.resetFocus();
52
69
  break;
70
+ case "Backspace":
71
+ if (this.searchInput) {
72
+ setTimeout(() => {
73
+ if (this.searchInput.value.trim() === "") {
74
+ this.dropdown.resetDropdownValue();
75
+ }
76
+ }, 0);
77
+ }
78
+ break;
53
79
  default:
54
80
  break;
55
81
  }
@@ -46,7 +46,6 @@ const DropdownContainer = (props: DropdownContainerProps) => {
46
46
  inputRef,
47
47
  isDropDownClosed,
48
48
  setFocusedOptionIndex,
49
- triggerRef
50
49
  } = useContext(DropdownContext);
51
50
 
52
51
  const ariaProps = buildAriaProps(aria);
@@ -67,7 +66,7 @@ const DropdownContainer = (props: DropdownContainerProps) => {
67
66
  id={id}
68
67
  onMouseEnter={() => setFocusedOptionIndex(-1)}
69
68
  ref={dropdownContainerRef}
70
- style={triggerRef ? {} : { position: "absolute"}}
69
+ style={{ position: "absolute"}}
71
70
  >
72
71
  {searchbar && (
73
72
  <TextInput dark={dark}
@@ -53,7 +53,6 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
53
53
  selected,
54
54
  setIsInputFocused,
55
55
  toggleDropdown,
56
- triggerRef,
57
56
  } = useContext(DropdownContext);
58
57
 
59
58
  const handleKeyDown = useHandleOnKeyDown();
@@ -99,7 +98,6 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
99
98
  id={id}
100
99
  >
101
100
  {
102
- !triggerRef && (
103
101
  children ? (
104
102
  <div
105
103
  onClick={() => toggleDropdown()}
@@ -147,7 +145,10 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
147
145
  <input
148
146
  className="dropdown_input"
149
147
  onChange={handleChange}
150
- onClick={() => toggleDropdown()}
148
+ onClick={(e) => {
149
+ e.stopPropagation();// keep the wrapper’s handler from firing
150
+ toggleDropdown();
151
+ }}
151
152
  onFocus={() => setIsInputFocused(true)}
152
153
  onKeyDown={handleKeyDown}
153
154
  placeholder={
@@ -181,7 +182,6 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
181
182
  </Flex>
182
183
  </>
183
184
  )
184
- )
185
185
  }
186
186
  </div>
187
187
  );
@@ -1,5 +1,5 @@
1
1
  <%= pb_rails("select", props: {
2
- error: "Please make a valid selection",
2
+ error: raw(pb_rails("icon", props: { icon: "warning" }) + " Please make a valid selection"),
3
3
  label: "Favorite Food",
4
4
  name: "food",
5
5
  options: [
@@ -4,7 +4,7 @@
4
4
  border: true,
5
5
  icon: "user"
6
6
  },
7
- error: "Please enter a valid email address",
7
+ error: raw(pb_rails("icon", props: { icon: "warning" }) + " Please enter a valid email address"),
8
8
  label: "Email Address",
9
9
  placeholder: "Enter email address",
10
10
  type: "email"
@@ -1 +1,5 @@
1
- <%= pb_rails("textarea", props: { error: "This field has an error!", label: "Label", rows: 4}) %>
1
+ <%= pb_rails("textarea", props: {
2
+ error: raw(pb_rails("icon", props: { icon: "warning" }) + " This field has an error!"),
3
+ label: "Label",
4
+ rows: 4
5
+ }) %>
@@ -8,7 +8,14 @@
8
8
 
9
9
  %>
10
10
 
11
- <%= pb_rails("typeahead", props: { id: "typeahead-error-example", options: options, error: "Please make a valid selection", label: "Products", name: :foo, is_multi: false }) %>
11
+ <%= pb_rails("typeahead", props: {
12
+ id: "typeahead-error-example",
13
+ options: options,
14
+ error: "Please make a valid selection",
15
+ label: "Products",
16
+ name: :foo,
17
+ is_multi: false
18
+ }) %>
12
19
 
13
20
  <!-- This section is an example of the available JavaScript event hooks -->
14
21
  <%= javascript_tag defer: "defer" do %>