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

Sign up to get free protection for your applications and to get access to all the features.
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) => {