playbook_ui 14.8.0.pre.alpha.play1648heightglobalprops4559 → 14.8.0.pre.alpha.revert3916revert3893PBNTR667railstypeaheadformintegration4567

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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/_playbook.scss +0 -1
  3. data/app/pb_kits/playbook/pb_form/docs/_form_form_with.html.erb +2 -2
  4. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_loading.html.erb +1 -1
  5. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +63 -12
  6. data/app/pb_kits/playbook/pb_selectable_card/docs/_selectable_card_default.html.erb +1 -2
  7. data/app/pb_kits/playbook/pb_typeahead/index.ts +29 -3
  8. data/app/pb_kits/playbook/pb_typeahead/typeahead.html.erb +5 -2
  9. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +4 -0
  10. data/app/pb_kits/playbook/utilities/globalPropNames.mjs +1 -0
  11. data/app/pb_kits/playbook/utilities/globalProps.ts +0 -15
  12. data/dist/chunks/{_typeahead-ZkBp8QRa.js → _typeahead-IoHUnHeF.js} +2 -2
  13. data/dist/chunks/{_weekday_stacked-BmqMRu1B.js → _weekday_stacked-BuaqHM9z.js} +1 -1
  14. data/dist/chunks/{lib-BC6ESsxG.js → lib-SyD3buPZ.js} +1 -1
  15. data/dist/chunks/{pb_form_validation-B_Z9rEbg.js → pb_form_validation-Dt8UJgrJ.js} +1 -1
  16. data/dist/chunks/vendor.js +1 -1
  17. data/dist/playbook-doc.js +1 -1
  18. data/dist/playbook-rails-react-bindings.js +1 -1
  19. data/dist/playbook-rails.js +1 -1
  20. data/dist/playbook.css +1 -1
  21. data/lib/playbook/classnames.rb +0 -3
  22. data/lib/playbook/forms/builder/typeahead_field.rb +13 -0
  23. data/lib/playbook/kit_base.rb +0 -6
  24. data/lib/playbook/version.rb +1 -1
  25. metadata +5 -11
  26. data/app/pb_kits/playbook/tokens/_height.scss +0 -19
  27. data/app/pb_kits/playbook/tokens/exports/_height.module.scss +0 -37
  28. data/app/pb_kits/playbook/utilities/_height.scss +0 -29
  29. data/lib/playbook/height.rb +0 -29
  30. data/lib/playbook/max_height.rb +0 -29
  31. data/lib/playbook/min_height.rb +0 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ba9b61505396d83dda5a66f647455f7300ffc61aa1568378549a848cf0a3e52d
4
- data.tar.gz: 9c7a4c59e621094cf9f5c8115529bfcd202615811b3f08e2757b20204951a514
3
+ metadata.gz: ab3091b250c9adb5894b9b3e5a3c7abc8d8bffbe5c150aec1c8e604276aada12
4
+ data.tar.gz: 9c91c2f685a8ceb56496ec4d8e433a887e6656af1bd90b09dff999a2cc22dc85
5
5
  SHA512:
6
- metadata.gz: f0e0bad5bbfc8b77967a3c379768451e525e1472e6bd64039070493bd045e1fe836f778f04d3b5a5b18b1af6b871c4182bbe3a3d9f3e1d493972a7394747a038
7
- data.tar.gz: f6814ca019c793c26280102244bf5c65a4c9cd44c08f50ca55c8e6b57330714d66972766a6cbdbf29c7ed514e67ee9461158b53be9ab1c41d9e618765e772227
6
+ metadata.gz: 14654ce385ecebb0507c9b12e46ea562d135c6a9775d39e16bb7bc3aa4c81367ee54931f88219aae03513e3e79f14df0c3ba100f5576aac744625afa710f6475
7
+ data.tar.gz: cef726720b79b6a97302ee8d8dbe8332d0dc176c2242bcb37258a094fb5e65465f2d9019f29a98d38e348d22b70ed859c2243f8c64d80820b567a8c178a6e7a1
@@ -126,4 +126,3 @@
126
126
  @import 'utilities/overflow';
127
127
  @import 'utilities/truncate';
128
128
  @import 'utilities/vertical_align';
129
- @import 'utilities/height';
@@ -23,7 +23,7 @@
23
23
  %>
24
24
 
25
25
  <%= pb_form_with(scope: :example, url: "", method: :get) do |form| %>
26
- <%= form.typeahead :example_user, props: { data: { typeahead_example1: true, user: {} }, placeholder: "Search for a user" } %>
26
+ <%= form.typeahead :example_typeahead, props: { data: { typeahead_example1: true, user: {} }, label: true, placeholder: "Search for a user" } %>
27
27
  <%= form.text_field :example_text_field, props: { label: true } %>
28
28
  <%= form.phone_number_field :example_phone_number_field, props: { label: "Example phone field" } %>
29
29
  <%= form.email_field :example_email_field, props: { label: true } %>
@@ -92,7 +92,7 @@
92
92
  const selectedUserData = JSON.parse(selectedUserJSON)
93
93
 
94
94
  // set the input field's value
95
- event.target.querySelector('input[name=example_user]').value = selectedUserData.login
95
+ event.target.querySelector('input[name=example_typeahead]').value = selectedUserData.login
96
96
 
97
97
  // log the selected option's dataset
98
98
  console.log('The selected user data:')
@@ -1,5 +1,5 @@
1
1
  <%= pb_form_with(scope: :example, url: "", method: :get, loading: true) do |form| %>
2
- <%= form.text_field :example_text_field, props: { label: true } %>
2
+ <%= form.text_field :example_text_field_loading, props: { label: true } %>
3
3
 
4
4
  <%= form.actions do |action| %>
5
5
  <%= action.submit %>
@@ -22,23 +22,74 @@
22
22
  %>
23
23
 
24
24
  <%= pb_form_with(scope: :example, method: :get, url: "", validate: true) do |form| %>
25
- <%= form.text_field :example_text_field, props: { label: true, required: true } %>
26
- <%= form.phone_number_field :example_phone_number_field, props: { label: "Example phone field" } %>
27
- <%= form.email_field :example_email_field, props: { label: true, required: true } %>
28
- <%= form.number_field :example_number_field, props: { label: true, required: true } %>
29
- <%= form.search_field :example_project_number, props: { label: true, required: true, validation: { pattern: "[0-9]{2}-[0-9]{5}", message: "Please enter a valid project number (example: 33-12345)." } } %>
30
- <%= form.password_field :example_password_field, props: { label: true, required: true } %>
31
- <%= form.url_field :example_url_field, props: { label: true, required: true } %>
32
- <%= form.text_area :example_text_area, props: { label: true, required: true } %>
33
- <%= form.dropdown_field :example_dropdown, props: { label: true, options: example_dropdown_options, required: true } %>
34
- <%= form.select :example_select, [ ["Yes", 1], ["No", 2] ], props: { label: true, blank_selection: "Select One...", required: true } %>
35
- <%= form.collection_select :example_collection_select, example_collection, :value, :name, props: { label: true, blank_selection: "Select One...", required: true } %>
25
+ <%= form.typeahead :example_typeahead_validation, props: { data: { typeahead_example2: true, user: {} }, label: true, placeholder: "Search for a user", required: true, validation: { message: "Please select a user." } } %>
26
+ <%= form.text_field :example_text_field_validation, props: { label: true, required: true } %>
27
+ <%= form.phone_number_field :example_phone_number_field_validation, props: { label: "Example phone field" } %>
28
+ <%= form.email_field :example_email_field_validation, props: { label: true, required: true } %>
29
+ <%= form.number_field :example_number_field_validation, props: { label: true, required: true } %>
30
+ <%= form.search_field :example_project_number_validation, props: { label: true, required: true, validation: { pattern: "[0-9]{2}-[0-9]{5}", message: "Please enter a valid project number (example: 33-12345)." } } %>
31
+ <%= form.password_field :example_password_field_validation, props: { label: true, required: true } %>
32
+ <%= form.url_field :example_url_field_validation, props: { label: true, required: true } %>
33
+ <%= form.text_area :example_text_area_validation, props: { label: true, required: true } %>
34
+ <%= form.dropdown_field :example_dropdown_validation, props: { label: true, options: example_dropdown_options, required: true } %>
35
+ <%= form.select :example_select_validation, [ ["Yes", 1], ["No", 2] ], props: { label: true, blank_selection: "Select One...", required: true } %>
36
+ <%= form.collection_select :example_collection_select_validation, example_collection, :value, :name, props: { label: true, blank_selection: "Select One...", required: true } %>
36
37
  <%= form.check_box :example_checkbox, props: { text: "Example Checkbox", label: true, required: true } %>
37
38
  <%= form.date_picker :example_date_picker_2, props: { label: true, required: true } %>
38
- <%= form.star_rating_field :example_star_rating, props: { variant: "interactive", label: true, required: true } %>
39
+ <%= form.star_rating_field :example_star_rating_validation, props: { variant: "interactive", label: true, required: true } %>
39
40
 
40
41
  <%= form.actions do |action| %>
41
42
  <%= action.submit %>
42
43
  <%= action.button props: { type: "reset", text: "Cancel", variant: "secondary" } %>
43
44
  <% end %>
44
45
  <% end %>
46
+
47
+ <!-- form.typeahead user results example template -->
48
+ <template data-typeahead-example-result-option>
49
+ <%= pb_rails("user", props: {
50
+ name: tag(:slot, name: "name"),
51
+ orientation: "horizontal",
52
+ align: "left",
53
+ avatar_url: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGP6zwAAAgcBApocMXEAAAAASUVORK5CYII=",
54
+ avatar: true
55
+ }) %>
56
+ </template>
57
+
58
+ <!-- form.typeahead JS example implementation -->
59
+ <%= javascript_tag defer: "defer" do %>
60
+ document.addEventListener("pb-typeahead-kit-search", function(event) {
61
+ if (!event.target.dataset || !event.target.dataset.typeaheadExample2) return
62
+
63
+ fetch(`https://api.github.com/search/users?q=${encodeURIComponent(event.detail.searchingFor)}`)
64
+ .then(response => response.json())
65
+ .then((result) => {
66
+ const resultOptionTemplate = document.querySelector("[data-typeahead-example-result-option]")
67
+
68
+ event.detail.setResults((result.items || []).map((user) => {
69
+ const wrapper = resultOptionTemplate.content.cloneNode(true)
70
+ wrapper.children[0].dataset.user = JSON.stringify(user)
71
+ wrapper.querySelector('slot[name="name"]').replaceWith(user.login)
72
+ wrapper.querySelector('img').dataset.src = user.avatar_url
73
+ return wrapper
74
+ }))
75
+ })
76
+ })
77
+
78
+
79
+ document.addEventListener("pb-typeahead-kit-result-option-selected", function(event) {
80
+ if (!event.target.dataset.typeaheadExample2) return
81
+
82
+ const selectedUserJSON = event.detail.selected.firstElementChild.dataset.user
83
+ const selectedUserData = JSON.parse(selectedUserJSON)
84
+
85
+ // set the input field's value
86
+ event.target.querySelector('input[name=example_typeahead_validation]').value = selectedUserData.login
87
+
88
+ // log the selected option's dataset
89
+ console.log('The selected user data:')
90
+ console.dir(selectedUserData)
91
+
92
+ // do even more with the data later - TBD
93
+ event.target.dataset.user = selectedUserJSON
94
+ })
95
+ <% end %>
@@ -6,7 +6,6 @@
6
6
  value: "selected_with_icon",
7
7
  checked: true,
8
8
  icon: true,
9
-
10
9
  }) do %>
11
10
  Selected, with icon
12
11
  <% end %>
@@ -37,4 +36,4 @@
37
36
  Disabled
38
37
  <% end %>
39
38
 
40
- </div>
39
+ </div>
@@ -4,11 +4,12 @@ import { debounce } from 'lodash'
4
4
  export default class PbTypeahead extends PbEnhancedElement {
5
5
  _searchInput: HTMLInputElement
6
6
  _resultsElement: HTMLElement
7
- _debouncedSearch: Function
7
+ _debouncedSearch: () => void
8
8
  _resultsLoadingIndicator: HTMLElement
9
9
  _resultOptionTemplate: HTMLElement
10
10
  _resultsOptionCache: Map<string, Array<DocumentFragment>>
11
11
  _searchContext: string
12
+ _validSelection: boolean
12
13
 
13
14
  static get selector() {
14
15
  return '[data-pb-typeahead-kit]'
@@ -86,6 +87,9 @@ export default class PbTypeahead extends PbEnhancedElement {
86
87
  const resultOption = (event.target as Element).closest('[data-result-option-item]')
87
88
  if (!resultOption) return
88
89
 
90
+ this._validSelection = true
91
+ this.removeValidationError()
92
+
89
93
  this.resultsCacheClear()
90
94
  this.searchInputClear()
91
95
  this.clearResults()
@@ -93,6 +97,28 @@ export default class PbTypeahead extends PbEnhancedElement {
93
97
  this.element.dispatchEvent(new CustomEvent('pb-typeahead-kit-result-option-selected', { bubbles: true, detail: { selected: resultOption, typeahead: this } }))
94
98
  }
95
99
 
100
+ removeValidationError() {
101
+ const inputWrapper = this.searchInput.closest('.text_input_wrapper')
102
+ if (inputWrapper) {
103
+ const errorMessage = inputWrapper.querySelector('.pb_body_kit_negative') as HTMLElement
104
+ if (errorMessage) {
105
+ errorMessage.style.display = 'none'
106
+ }
107
+ this.searchInput.classList.remove('error')
108
+ }
109
+ }
110
+
111
+ showValidationError() {
112
+ const inputWrapper = this.searchInput.closest('.text_input_wrapper')
113
+ if (inputWrapper) {
114
+ const errorMessage = inputWrapper.querySelector('.pb_body_kit_negative') as HTMLElement
115
+ if (errorMessage) {
116
+ errorMessage.style.display = 'block'
117
+ }
118
+ this.searchInput.classList.add('error')
119
+ }
120
+ }
121
+
96
122
  clearResults() {
97
123
  this.resultsElement.innerHTML = ''
98
124
  }
@@ -201,8 +227,8 @@ export default class PbTypeahead extends PbEnhancedElement {
201
227
  }
202
228
 
203
229
  toggleResultsLoadingIndicator(visible: boolean) {
204
- var visibilityProperty = '0'
230
+ let visibilityProperty = '0'
205
231
  if (visible) visibilityProperty = '1'
206
232
  this.resultsLoadingIndicator.style.opacity = visibilityProperty
207
233
  }
208
- }
234
+ }
@@ -17,11 +17,14 @@
17
17
  <%= pb_rails("text_input", props: {
18
18
  type: "search",
19
19
  input_options: object.input_options,
20
- label: object.label,
21
20
  name: object.name,
22
21
  value: object.value,
23
22
  placeholder: object.placeholder,
24
23
  margin_bottom: "none",
24
+ required: object.required,
25
+ validation: object.validation,
26
+ label: object.label,
27
+ id: object.input_options[:id],
25
28
  }) %>
26
29
  <%= pb_rails("list", props: { ordered: false, borderless: false, xpadding: true, role: "status", aria: { live: "polite" }, data: { pb_typeahead_kit_results: true } }) do %>
27
30
  <% end %>
@@ -33,4 +36,4 @@
33
36
  <% end %>
34
37
  </template>
35
38
  <% end %>
36
- <% end %>
39
+ <% end %>
@@ -40,6 +40,10 @@ module Playbook
40
40
  prop :pill_color, type: Playbook::Props::Enum,
41
41
  values: %w[primary neutral success warning error info data_1 data_2 data_3 data_4 data_5 data_6 data_7 data_8 windows siding roofing doors gutters solar insulation accessories],
42
42
  default: "primary"
43
+ prop :required, type: Playbook::Props::Boolean,
44
+ default: false
45
+ prop :validation, type: Playbook::Props::HashProp,
46
+ default: {}
43
47
 
44
48
  def classname
45
49
  default_margin_bottom = margin_bottom.present? ? "" : " mb_sm"
@@ -7,6 +7,7 @@ export default [
7
7
  "right",
8
8
  "top",
9
9
  "hover",
10
+ "groupHover",
10
11
  "zIndex",
11
12
  "verticalAlign",
12
13
  "truncate",
@@ -361,21 +361,6 @@ const PROP_CATEGORIES: {[key:string]: (props: {[key: string]: any}) => string} =
361
361
  css += maxWidth ? `max_width_${filterClassName(maxWidth)} ` : ''
362
362
  return css.trimEnd()
363
363
  },
364
- minHeightProps: ({ minHeight }: MinHeight) => {
365
- let css = ''
366
- css += minHeight ? `min_height_${filterClassName(minHeight)} ` : ''
367
- return css.trimEnd()
368
- },
369
- maxHeightProps: ({ maxHeight }: MaxHeight) => {
370
- let css = ''
371
- css += maxHeight ? `max_height_${filterClassName(maxHeight)} ` : ''
372
- return css.trimEnd()
373
- },
374
- heightProps: ({ height }: Height) => {
375
- let css = ''
376
- css += height ? `height_${filterClassName(height)} ` : ''
377
- return css.trimEnd()
378
- },
379
364
  zIndexProps: (zIndex: ZIndex) => {
380
365
  let css = ''
381
366
  Object.entries(zIndex).forEach((zIndexEntry) => {