playbook_ui 14.6.2.pre.alpha.PLAY15814384 → 14.7.0.pre.alpha.PBNTR667railstypeaheadformintegration4454

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/_playbook.scss +1 -1
  3. data/app/pb_kits/playbook/pb_advanced_table/Components/CustomCell.tsx +8 -1
  4. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +21 -28
  5. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +37 -1
  6. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_custom_cell.jsx +13 -2
  7. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +8 -7
  8. data/app/pb_kits/playbook/pb_form/docs/_form_form_with.html.erb +2 -2
  9. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_loading.html.erb +1 -1
  10. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +63 -12
  11. data/app/pb_kits/playbook/pb_skeleton_loading/_skeleton_loading.scss +37 -0
  12. data/app/pb_kits/playbook/pb_skeleton_loading/_skeleton_loading.tsx +67 -0
  13. data/app/pb_kits/playbook/pb_skeleton_loading/_skeleton_loading_mixins.scss +40 -0
  14. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_border_radius.jsx +51 -0
  15. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_border_radius.md +1 -0
  16. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_color.jsx +26 -0
  17. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_color.md +1 -0
  18. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_default.html.erb +1 -0
  19. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_default.jsx +11 -0
  20. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_height_width.jsx +59 -0
  21. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_height_width.md +3 -0
  22. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_layout.jsx +20 -0
  23. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_layout.md +3 -0
  24. data/app/pb_kits/playbook/pb_skeleton_loading/docs/example.yml +13 -0
  25. data/app/pb_kits/playbook/pb_skeleton_loading/docs/index.js +5 -0
  26. data/app/pb_kits/playbook/pb_skeleton_loading/skeleton_loading.html.erb +12 -0
  27. data/app/pb_kits/playbook/pb_skeleton_loading/skeleton_loading.rb +8 -0
  28. data/app/pb_kits/playbook/pb_skeleton_loading/skeleton_loading.test.jsx +81 -0
  29. data/app/pb_kits/playbook/pb_typeahead/index.ts +36 -2
  30. data/app/pb_kits/playbook/pb_typeahead/typeahead.html.erb +5 -2
  31. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +4 -0
  32. data/app/pb_kits/playbook/utilities/_hover.scss +46 -43
  33. data/app/pb_kits/playbook/utilities/globalPropNames.mjs +1 -0
  34. data/app/pb_kits/playbook/utilities/globalProps.ts +5 -0
  35. data/dist/chunks/{_typeahead-C-6MLSyC.js → _typeahead-B-juiSkw.js} +2 -2
  36. data/dist/chunks/_weekday_stacked-DPqQHJ1l.js +45 -0
  37. data/dist/chunks/{lib-D-mTv-kp.js → lib-DpxYMiKe.js} +1 -1
  38. data/dist/chunks/{pb_form_validation-BkWGwJsl.js → pb_form_validation-LqRlnmi6.js} +1 -1
  39. data/dist/chunks/vendor.js +1 -1
  40. data/dist/menu.yml +4 -1
  41. data/dist/playbook-doc.js +1 -1
  42. data/dist/playbook-rails-react-bindings.js +1 -1
  43. data/dist/playbook-rails.js +1 -1
  44. data/dist/playbook.css +1 -1
  45. data/lib/playbook/forms/builder/typeahead_field.rb +13 -0
  46. data/lib/playbook/hover.rb +4 -1
  47. data/lib/playbook/version.rb +2 -2
  48. metadata +24 -6
  49. data/dist/chunks/_weekday_stacked-CFhGhr9V.js +0 -45
@@ -0,0 +1,59 @@
1
+ import React from 'react'
2
+ import { Card, SkeletonLoading } from "playbook-ui"
3
+
4
+
5
+ const SkeletonLoadingHeightWidth = (props) => (
6
+ <div>
7
+ <SkeletonLoading
8
+ height="100px"
9
+ width="50%"
10
+ {...props}
11
+ />
12
+ <SkeletonLoading
13
+ gap="md"
14
+ height="20px"
15
+ marginY="md"
16
+ stack="3"
17
+ width="50px"
18
+ {...props}
19
+ />
20
+ <Card htmlOptions={{ style: { height: '200px', width: '100%' }}}
21
+ marginBottom="md"
22
+ padding="none"
23
+ >
24
+ <SkeletonLoading
25
+ borderRadius="md"
26
+ gap="xl"
27
+ height="50%"
28
+ width="300px"
29
+ {...props}
30
+ />
31
+ </Card>
32
+ <Card htmlOptions={{ style: { height: '200px', width: '100%' }}}
33
+ padding="none"
34
+ >
35
+ <SkeletonLoading
36
+ borderRadius="md"
37
+ gap="xl"
38
+ height="30%"
39
+ stack="2"
40
+ width="70%"
41
+ {...props}
42
+ />
43
+ </Card>
44
+ <SkeletonLoading
45
+ height="150px"
46
+ marginY="md"
47
+ width="150px"
48
+ {...props}
49
+ />
50
+ <SkeletonLoading
51
+ borderRadius="rounded"
52
+ height="150px"
53
+ width="150px"
54
+ {...props}
55
+ />
56
+ </div>
57
+ )
58
+
59
+ export default SkeletonLoadingHeightWidth
@@ -0,0 +1,3 @@
1
+ The `height` and `width` props accept pixel and percentage values. If using a percentage for `height`, the parent container must have a set height.
2
+
3
+ Set the `height` and `width` props to the same value to make a square. A `rounded` borderRadius will make a square a circle. If using percentages to make a square, your parent container must also be a square.
@@ -0,0 +1,20 @@
1
+ import React from 'react'
2
+ import { SkeletonLoading } from "playbook-ui"
3
+
4
+
5
+ const SkeletonLoadingLayout = (props) => (
6
+ <div>
7
+ <SkeletonLoading
8
+ stack="5"
9
+ {...props}
10
+ />
11
+ <SkeletonLoading
12
+ gap="md"
13
+ paddingTop="xl"
14
+ stack="3"
15
+ {...props}
16
+ />
17
+ </div>
18
+ )
19
+
20
+ export default SkeletonLoadingLayout
@@ -0,0 +1,3 @@
1
+ Use the `stack` and `gap` props in conjunction to layer multiple Skeleton loading bars on top of each other.
2
+
3
+ `stack` accepts a number that correlates to the number of rows (1 is default), and `gap` accepts a portion of our [spacing props](https://playbook.powerapp.cloud/visual_guidelines/spacing) (`xxs` as default, `xs`, `sm`, `md`, `lg`, `xl`, `xxl`) to set the pixel distance between each row. `gap` will not do anything if there is no corresponding `stack` prop set.
@@ -0,0 +1,13 @@
1
+ examples:
2
+
3
+ rails:
4
+ # - skeleton_loading_default: Default
5
+
6
+
7
+ react:
8
+ - skeleton_loading_default: Default
9
+ - skeleton_loading_color: Color
10
+ - skeleton_loading_layout: Layout
11
+ - skeleton_loading_border_radius: Border Radius
12
+ - skeleton_loading_height_width: Height & Width
13
+
@@ -0,0 +1,5 @@
1
+ export { default as SkeletonLoadingDefault } from './_skeleton_loading_default.jsx'
2
+ export { default as SkeletonLoadingColor } from './_skeleton_loading_color.jsx'
3
+ export { default as SkeletonLoadingLayout } from './_skeleton_loading_layout.jsx'
4
+ export { default as SkeletonLoadingBorderRadius } from './_skeleton_loading_border_radius.jsx'
5
+ export { default as SkeletonLoadingHeightWidth } from './_skeleton_loading_height_width.jsx'
@@ -0,0 +1,12 @@
1
+ <!-- Go to pb_content_tag definition in kit_base.rb for usage information. Commented out options are default (showing the default shape), and each can be deleted if not customizing that param. -->
2
+ <!-- If using nonstandard params please un-comment out and replace with your custom params. -->
3
+ <%= pb_content_tag(
4
+ # :div,
5
+ # aria: object.aria,
6
+ # class: object.classname,
7
+ # data: object.data,
8
+ # id: object.id,
9
+ # **combined_html_options
10
+ ) do %>
11
+ <span>SKELETON_LOADING CONTENT</span>
12
+ <% end %>
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Playbook
4
+ module PbSkeletonLoading
5
+ class SkeletonLoading < ::Playbook::KitBase
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,81 @@
1
+ import React from 'react'
2
+ import { render, screen } from '@testing-library/react'
3
+ import { ensureAccessible } from '../utilities/test-utils'
4
+ import { SkeletonLoading } from 'playbook-ui'
5
+
6
+ /* See these resources for more testing info:
7
+ - https://github.com/testing-library/jest-dom#usage for useage and examples
8
+ - https://jestjs.io/docs/en/using-matchers
9
+ */
10
+
11
+ describe('SkeletonLoading', () => {
12
+ const defaultProps = {
13
+ data: { testid: 'skeleton-loading' }
14
+ }
15
+
16
+ it('should be accessible', async () => {
17
+ ensureAccessible(SkeletonLoading, defaultProps)
18
+ })
19
+
20
+ it('renders with default props', () => {
21
+ const { container } = render(<SkeletonLoading {...defaultProps} />)
22
+ const skeleton = screen.getByTestId('skeleton-loading')
23
+
24
+ expect(skeleton).toBeInTheDocument()
25
+ expect(skeleton).toHaveClass('pb_skeleton_loading')
26
+ expect(container.querySelectorAll('div[class*="border_radius_"]')).toHaveLength(1)
27
+ })
28
+
29
+ it('renders multiple skeleton items based on stack prop', () => {
30
+ const props = {
31
+ ...defaultProps,
32
+ stack: 3
33
+ }
34
+ const { container } = render(<SkeletonLoading {...props} />)
35
+
36
+ expect(container.querySelectorAll('div[class*="border_radius_"]')).toHaveLength(3)
37
+ })
38
+
39
+ it('applies custom styles correctly', () => {
40
+ const props = {
41
+ ...defaultProps,
42
+ height: '24px',
43
+ width: '50%',
44
+ borderRadius: 'lg',
45
+ color: 'light',
46
+ dark: true
47
+ }
48
+ const { container } = render(<SkeletonLoading {...props} />)
49
+ const skeletonItem = container.querySelector('div[class*="border_radius_"]')
50
+
51
+ expect(skeletonItem).toHaveClass('border_radius_lg')
52
+ expect(skeletonItem).toHaveClass('dark')
53
+ })
54
+
55
+ it('applies gap class to items after first one', () => {
56
+ const props = {
57
+ ...defaultProps,
58
+ stack: 3,
59
+ gap: 'md'
60
+ }
61
+ const { container } = render(<SkeletonLoading {...props} />)
62
+ const skeletonItems = container.querySelectorAll('div[class*="border_radius_"]')
63
+
64
+ expect(skeletonItems[0]).not.toHaveClass('gap_md')
65
+ expect(skeletonItems[1]).toHaveClass('gap_md')
66
+ expect(skeletonItems[2]).toHaveClass('gap_md')
67
+ })
68
+
69
+ it('handles no gap properly', () => {
70
+ const props = {
71
+ ...defaultProps,
72
+ stack: 2,
73
+ gap: 'none'
74
+ }
75
+ const { container } = render(<SkeletonLoading {...props} />)
76
+ const skeletonItems = container.querySelectorAll('div[class*="border_radius_"]')
77
+
78
+ expect(skeletonItems[0]).not.toHaveClass('gap_none')
79
+ expect(skeletonItems[1]).not.toHaveClass('gap_none')
80
+ })
81
+ })
@@ -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]'
@@ -19,6 +20,7 @@ export default class PbTypeahead extends PbEnhancedElement {
19
20
  this.searchInput.addEventListener('focus', () => this.debouncedSearch())
20
21
  this.searchInput.addEventListener('input', () => this.debouncedSearch())
21
22
  this.resultsElement.addEventListener('click', (event: MouseEvent) => this.optionSelected(event))
23
+ this.element.closest('form')?.addEventListener('submit', (event) => this.handleFormSubmission(event))
22
24
  }
23
25
 
24
26
  handleKeydown(event: KeyboardEvent) {
@@ -86,6 +88,9 @@ export default class PbTypeahead extends PbEnhancedElement {
86
88
  const resultOption = (event.target as Element).closest('[data-result-option-item]')
87
89
  if (!resultOption) return
88
90
 
91
+ this._validSelection = true
92
+ this.removeValidationError()
93
+
89
94
  this.resultsCacheClear()
90
95
  this.searchInputClear()
91
96
  this.clearResults()
@@ -93,6 +98,35 @@ export default class PbTypeahead extends PbEnhancedElement {
93
98
  this.element.dispatchEvent(new CustomEvent('pb-typeahead-kit-result-option-selected', { bubbles: true, detail: { selected: resultOption, typeahead: this } }))
94
99
  }
95
100
 
101
+ removeValidationError() {
102
+ const inputWrapper = this.searchInput.closest('.text_input_wrapper')
103
+ if (inputWrapper) {
104
+ const errorMessage = inputWrapper.querySelector('.pb_body_kit_negative') as HTMLElement
105
+ if (errorMessage) {
106
+ errorMessage.style.display = 'none'
107
+ }
108
+ this.searchInput.classList.remove('error')
109
+ }
110
+ }
111
+
112
+ showValidationError() {
113
+ const inputWrapper = this.searchInput.closest('.text_input_wrapper')
114
+ if (inputWrapper) {
115
+ const errorMessage = inputWrapper.querySelector('.pb_body_kit_negative') as HTMLElement
116
+ if (errorMessage) {
117
+ errorMessage.style.display = 'block'
118
+ }
119
+ this.searchInput.classList.add('error')
120
+ }
121
+ }
122
+
123
+ handleFormSubmission(event: Event) {
124
+ if (!this._validSelection) {
125
+ this.showValidationError()
126
+ event.preventDefault()
127
+ }
128
+ }
129
+
96
130
  clearResults() {
97
131
  this.resultsElement.innerHTML = ''
98
132
  }
@@ -201,7 +235,7 @@ export default class PbTypeahead extends PbEnhancedElement {
201
235
  }
202
236
 
203
237
  toggleResultsLoadingIndicator(visible: boolean) {
204
- var visibilityProperty = '0'
238
+ let visibilityProperty = '0'
205
239
  if (visible) visibilityProperty = '1'
206
240
  this.resultsLoadingIndicator.style.opacity = visibilityProperty
207
241
  }
@@ -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"
@@ -1,51 +1,54 @@
1
1
  @import "../tokens/exports/scale.module";
2
2
 
3
- @mixin hover-color-classes($colors-list) {
4
- @each $name, $color in $colors-list {
5
- .hover_background_#{"" + $name}:hover {
6
- background-color: $color !important;
7
- transition: background-color $transition-speed ease;
8
- }
9
- .hover_color_#{"" + $name}:hover {
10
- color: $color !important;
11
- transition: color $transition-speed ease;
12
- }
3
+ @mixin hover-scale-classes($scales-list) {
4
+ @each $name, $scale in $scales-list {
5
+ .hover_#{"" + $name}:hover,
6
+ .group_hover:hover .group_hover.hover_#{"" + $name} {
7
+ transform: $scale;
8
+ transition: transform $transition-speed ease;
13
9
  }
14
10
  }
15
-
16
- @mixin hover-shadow-classes($shadows-list) {
17
- @each $name, $shadow in $shadows-list {
18
- .hover_#{"" + $name}:hover {
19
- box-shadow: $shadow;
20
- transition: box-shadow $transition-speed ease;
21
- }
11
+ }
12
+
13
+ @mixin hover-shadow-classes($shadows-list) {
14
+ @each $name, $shadow in $shadows-list {
15
+ .hover_#{"" + $name}:hover,
16
+ .group_hover:hover .group_hover.hover_#{"" + $name} {
17
+ box-shadow: $shadow;
18
+ transition: box-shadow $transition-speed ease;
22
19
  }
23
20
  }
24
-
25
- @mixin hover-scale-classes($scales-list) {
26
- @each $name, $scale in $scales-list {
27
- .hover_#{"" + $name}:hover {
28
- transform: $scale;
29
- transition: transform $transition-speed ease;
30
- }
21
+ }
22
+
23
+ @mixin hover-color-classes($colors-list) {
24
+ @each $name, $color in $colors-list {
25
+ .hover_background_#{"" + $name}:hover,
26
+ .group_hover:hover .group_hover.hover_background_#{"" + $name} {
27
+ background-color: $color !important;
28
+ transition: background-color $transition-speed ease;
29
+ }
30
+ .hover_color_#{"" + $name}:hover,
31
+ .group_hover:hover .group_hover.hover_color_#{"" + $name} {
32
+ color: $color !important;
33
+ transition: color $transition-speed ease;
31
34
  }
32
35
  }
33
-
34
-
35
- @include hover-scale-classes($scales);
36
- @include hover-shadow-classes($box_shadows);
37
- @include hover-color-classes($product_colors);
38
- @include hover-color-classes($status_colors);
39
- @include hover-color-classes($data_colors);
40
- @include hover-color-classes($shadow_colors);
41
- @include hover-color-classes($colors);
42
- @include hover-color-classes($interface_colors);
43
- @include hover-color-classes($main_colors);
44
- @include hover-color-classes($background_colors);
45
- @include hover-color-classes($card_colors);
46
- @include hover-color-classes($active_colors);
47
- @include hover-color-classes($action_colors);
48
- @include hover-color-classes($hover_colors);
49
- @include hover-color-classes($border_colors);
50
- @include hover-color-classes($text_colors);
51
- @include hover-color-classes($category_colors);
36
+ }
37
+
38
+ @include hover-scale-classes($scales);
39
+ @include hover-shadow-classes($box_shadows);
40
+ @include hover-color-classes($product_colors);
41
+ @include hover-color-classes($status_colors);
42
+ @include hover-color-classes($data_colors);
43
+ @include hover-color-classes($shadow_colors);
44
+ @include hover-color-classes($colors);
45
+ @include hover-color-classes($interface_colors);
46
+ @include hover-color-classes($main_colors);
47
+ @include hover-color-classes($background_colors);
48
+ @include hover-color-classes($card_colors);
49
+ @include hover-color-classes($active_colors);
50
+ @include hover-color-classes($action_colors);
51
+ @include hover-color-classes($hover_colors);
52
+ @include hover-color-classes($border_colors);
53
+ @include hover-color-classes($text_colors);
54
+ @include hover-color-classes($category_colors);
@@ -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",
@@ -66,6 +66,10 @@ type Hover = Shadow & {
66
66
  scale?: "sm" | "md" | "lg"
67
67
  }
68
68
 
69
+ type GroupHover = {
70
+ groupHover?: boolean,
71
+ }
72
+
69
73
  type JustifyContent = {
70
74
  justifyContent?: Alignment & Space
71
75
  }
@@ -221,6 +225,7 @@ const filterClassName = (value: string): string => {
221
225
  // Prop categories
222
226
  const PROP_CATEGORIES: {[key:string]: (props: {[key: string]: any}) => string} = {
223
227
 
228
+ groupHoverProps: ({ groupHover }: GroupHover ) => groupHover ? 'group_hover ' : '',
224
229
  hoverProps: ({ hover }: { hover?: Hover }) => {
225
230
  let css = '';
226
231
  if (!hover) return css;