playbook_ui 14.15.0.pre.rc.4 → 14.15.0

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 (95) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/Components/RegularTableView.tsx +127 -0
  3. data/app/pb_kits/playbook/pb_advanced_table/Components/TableActionBar.tsx +55 -0
  4. data/app/pb_kits/playbook/pb_advanced_table/Components/TablePagination.tsx +33 -0
  5. data/app/pb_kits/playbook/pb_advanced_table/Components/VirtualizedTableView.tsx +275 -0
  6. data/app/pb_kits/playbook/pb_advanced_table/Context/AdvancedTableContext.tsx +143 -3
  7. data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableActions.ts +66 -0
  8. data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableState.ts +195 -0
  9. data/app/pb_kits/playbook/pb_advanced_table/SubKits/TableBody.tsx +45 -99
  10. data/app/pb_kits/playbook/pb_advanced_table/Utilities/CellRendererUtils.tsx +73 -0
  11. data/app/pb_kits/playbook/pb_advanced_table/Utilities/RowUtils.ts +52 -0
  12. data/app/pb_kits/playbook/pb_advanced_table/Utilities/TableContainerStyles.ts +80 -0
  13. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +123 -7
  14. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +153 -299
  15. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_infinite_scroll.jsx +50 -0
  16. data/app/pb_kits/playbook/pb_advanced_table/docs/advanced_table_mock_data_infinite_scroll.json +152002 -0
  17. data/app/pb_kits/playbook/pb_card/_card.tsx +2 -1
  18. data/app/pb_kits/playbook/pb_date_picker/date_picker.html.erb +4 -1
  19. data/app/pb_kits/playbook/pb_date_picker/date_picker.rb +2 -0
  20. data/app/pb_kits/playbook/pb_date_picker/index.ts +38 -0
  21. data/app/pb_kits/playbook/pb_drawer/_drawer.scss +19 -3
  22. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_borders.jsx +3 -3
  23. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_breakpoints.jsx +20 -37
  24. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_menu.jsx +6 -6
  25. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_overlay.jsx +1 -0
  26. data/app/pb_kits/playbook/pb_drawer/docs/example.yml +1 -0
  27. data/app/pb_kits/playbook/pb_filter/Filter/CurrentFilters.tsx +5 -4
  28. data/app/pb_kits/playbook/pb_filter/Filter/FilterSingle.tsx +2 -2
  29. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +1 -1
  30. data/app/pb_kits/playbook/pb_form_pill/_form_pill.scss +9 -2
  31. data/app/pb_kits/playbook/pb_form_pill/_form_pill.tsx +4 -0
  32. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_wrapped.html.erb +40 -0
  33. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_wrapped.jsx +50 -0
  34. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_wrapped.md +3 -0
  35. data/app/pb_kits/playbook/pb_form_pill/docs/example.yml +2 -0
  36. data/app/pb_kits/playbook/pb_form_pill/docs/index.js +1 -0
  37. data/app/pb_kits/playbook/pb_form_pill/form_pill.rb +7 -1
  38. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.scss +7 -0
  39. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +13 -3
  40. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled.html.erb +72 -0
  41. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled.jsx +91 -0
  42. data/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +2 -1
  43. data/app/pb_kits/playbook/pb_multi_level_select/docs/index.js +1 -0
  44. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.rb +6 -0
  45. data/app/pb_kits/playbook/pb_popover/_popover.tsx +1 -1
  46. data/app/pb_kits/playbook/pb_radio/_radio.tsx +85 -74
  47. data/app/pb_kits/playbook/pb_radio/docs/_radio_react_hook.jsx +60 -0
  48. data/app/pb_kits/playbook/pb_radio/docs/_radio_react_hook.md +1 -0
  49. data/app/pb_kits/playbook/pb_radio/docs/example.yml +2 -1
  50. data/app/pb_kits/playbook/pb_radio/docs/index.js +1 -0
  51. data/app/pb_kits/playbook/pb_radio/radio.test.js +16 -0
  52. data/app/pb_kits/playbook/pb_select/docs/_select_react_hook.jsx +58 -0
  53. data/app/pb_kits/playbook/pb_select/docs/_select_react_hook.md +1 -0
  54. data/app/pb_kits/playbook/pb_select/docs/example.yml +1 -0
  55. data/app/pb_kits/playbook/pb_select/docs/index.js +1 -0
  56. data/app/pb_kits/playbook/pb_select/select.html.erb +3 -5
  57. data/app/pb_kits/playbook/pb_selectable_card/selectable_card.html.erb +1 -5
  58. data/app/pb_kits/playbook/pb_selectable_card_icon/selectable_card_icon.html.erb +1 -4
  59. data/app/pb_kits/playbook/pb_selectable_icon/selectable_icon.html.erb +1 -5
  60. data/app/pb_kits/playbook/pb_timeline/_timeline.scss +2 -2
  61. data/app/pb_kits/playbook/pb_title/_title.scss +32 -0
  62. data/app/pb_kits/playbook/pb_title/_title.tsx +10 -1
  63. data/app/pb_kits/playbook/pb_title/docs/_title_default.html.erb +1 -2
  64. data/app/pb_kits/playbook/pb_title/docs/_title_default.jsx +1 -1
  65. data/app/pb_kits/playbook/pb_title/docs/_title_display_size.html.erb +7 -0
  66. data/app/pb_kits/playbook/pb_title/docs/_title_display_size.jsx +54 -0
  67. data/app/pb_kits/playbook/pb_title/docs/_title_display_size.md +1 -0
  68. data/app/pb_kits/playbook/pb_title/docs/example.yml +2 -0
  69. data/app/pb_kits/playbook/pb_title/docs/index.js +1 -0
  70. data/app/pb_kits/playbook/pb_title/title.rb +10 -1
  71. data/app/pb_kits/playbook/pb_tooltip/_tooltip.tsx +25 -0
  72. data/app/pb_kits/playbook/pb_tooltip/docs/_tooltip_sizing.jsx +69 -0
  73. data/app/pb_kits/playbook/pb_tooltip/docs/_tooltip_sizing.md +3 -0
  74. data/app/pb_kits/playbook/pb_tooltip/docs/example.yml +1 -1
  75. data/app/pb_kits/playbook/pb_tooltip/docs/index.js +1 -0
  76. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +2 -1
  77. data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +5 -1
  78. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +4 -1
  79. data/app/pb_kits/playbook/utilities/object.test.js +99 -0
  80. data/app/pb_kits/playbook/utilities/object.ts +29 -1
  81. data/dist/chunks/_typeahead-BhfaW1J9.js +36 -0
  82. data/dist/chunks/_weekday_stacked-CKRIELiF.js +45 -0
  83. data/dist/chunks/lazysizes-DHz07jlL.js +1 -0
  84. data/dist/chunks/{lib-Dmay5Z6U.js → lib-5OzNgeeu.js} +2 -2
  85. data/dist/chunks/{pb_form_validation-DdP7BnVX.js → pb_form_validation-DGhKbZtO.js} +1 -1
  86. data/dist/chunks/vendor.js +1 -1
  87. data/dist/playbook-doc.js +1 -1
  88. data/dist/playbook-rails-react-bindings.js +1 -1
  89. data/dist/playbook-rails.js +1 -1
  90. data/dist/playbook.css +1 -1
  91. data/lib/playbook/version.rb +1 -1
  92. metadata +34 -7
  93. data/dist/chunks/_typeahead-NXKDTf__.js +0 -36
  94. data/dist/chunks/_weekday_stacked-DtCYkCXM.js +0 -45
  95. data/dist/chunks/lazysizes-B7xYodB-.js +0 -1
@@ -243,7 +243,7 @@ $gap_lg: $height_from_top + $space_lg;
243
243
  }
244
244
  [class=pb_timeline_item_left_block] {
245
245
  margin-bottom: $space_lg;
246
- width: $space_lg;
246
+ min-width: $space_lg;
247
247
  }
248
248
  [class=pb_timeline_item_right_block] {
249
249
  @include flex_wrapper(column);
@@ -263,7 +263,7 @@ $gap_lg: $height_from_top + $space_lg;
263
263
  }
264
264
  [class=pb_timeline_item_left_block] {
265
265
  margin-bottom: $space_lg;
266
- width: $space_lg;
266
+ min-width: $space_lg;
267
267
  }
268
268
  [class=pb_timeline_item_right_block] {
269
269
  @include flex_wrapper(column);
@@ -31,6 +31,38 @@
31
31
  @include pb_title_thin;
32
32
  }
33
33
 
34
+ &[class*=_display] {
35
+ font-size: clamp(24px, calc(1.25vw + 5.3vw), 128px);
36
+ }
37
+
38
+ &[class*=_dynamic] {
39
+ &[class*=_xs] {
40
+ font-size: min(2vw, 80vw / 16);
41
+ }
42
+
43
+ &[class*=_sm] {
44
+ font-size: min(2.5vw, 96vw / 16);
45
+ }
46
+
47
+ &[class*=_md] {
48
+ font-size: min(4vw, 160vw / 16);
49
+ }
50
+
51
+ &[class*=_lg] {
52
+ font-size: min(5vw, 192vw / 16);
53
+ }
54
+
55
+ &[class*=_xl] {
56
+ font-size: min(6vw, 224vw / 16);
57
+ }
58
+
59
+ &[class*=_xxl] {
60
+ font-size: min(8vw, 256vw / 16);
61
+ }
62
+ }
63
+
64
+
65
+
34
66
  @each $size, $size_value in $breakpoints_grid {
35
67
  @for $title_size_value from 1 through 4 {
36
68
  $min_size: map-get($size_value, "min");
@@ -3,7 +3,7 @@ import classnames from 'classnames'
3
3
  import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../utilities/props'
4
4
  import { deprecatedProps, GlobalProps, globalProps } from '../utilities/globalProps'
5
5
 
6
- type SizeType = 1 | 2 | 3 | 4 | "1" | "2" | "3" | "4"
6
+ type SizeType = 1 | 2 | 3 | 4 | "1" | "2" | "3" | "4" | "display"
7
7
  type SizeResponsiveType = {[key: string]: SizeType}
8
8
 
9
9
  type TitleProps = {
@@ -16,6 +16,7 @@ type TitleProps = {
16
16
  htmlOptions?: {[key: string]: string | number | boolean | (() => void)},
17
17
  id?: string,
18
18
  size?: SizeType | SizeResponsiveType,
19
+ displaySize?: null | "xs" | "sm" | "md" | "lg" | "xl" | "xxl",
19
20
  tag?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "div" | "span",
20
21
  text?: string,
21
22
  variant?: null | "link",
@@ -32,6 +33,7 @@ const Title = (props: TitleProps): React.ReactElement => {
32
33
  htmlOptions = {},
33
34
  id,
34
35
  size = 3,
36
+ displaySize = null,
35
37
  bold = true,
36
38
  tag = 'h3',
37
39
  text,
@@ -56,9 +58,16 @@ const Title = (props: TitleProps): React.ReactElement => {
56
58
  return css.trim()
57
59
  }
58
60
 
61
+ const buildDisplaySize = () => {
62
+ if (displaySize) {
63
+ return `pb_title_kit_dynamic_${displaySize}`
64
+ }
65
+ }
66
+
59
67
  const classes = classnames(
60
68
  buildCss('pb_title_kit', isSizeNumberOrString ? `size_${size}` : "", variant, color, getBold),
61
69
  globalProps(props),
70
+ buildDisplaySize(),
62
71
  buildResponsiveSizeCss(),
63
72
  className
64
73
  )
@@ -1,10 +1,9 @@
1
1
  <%= pb_rails("title", props: {
2
+ margin_bottom: "md"
2
3
  }) do %>
3
4
  Default Title
4
5
  <% end %>
5
6
 
6
- <br/>
7
-
8
7
  <%= pb_rails("title", props: { text: "Title 1", tag: "h1", size: 1 }) %>
9
8
  <%= pb_rails("title", props: { text: "Title 2", tag: "h2", size: 2 }) %>
10
9
  <%= pb_rails("title", props: { text: "Title 3", tag: "h3", size: 3 }) %>
@@ -6,10 +6,10 @@ const TitleDefault = (props) => {
6
6
  return (
7
7
  <div>
8
8
  <Title
9
+ marginBottom='md'
9
10
  text="Default Title"
10
11
  {...props}
11
12
  />
12
- <br />
13
13
  <Title
14
14
  size={1}
15
15
  tag="h1"
@@ -0,0 +1,7 @@
1
+ <%= pb_rails("title", props: { text: "Display Size xs", tag: "h1", display_size: "xs" }) %>
2
+ <%= pb_rails("title", props: { text: "Display Size sm", tag: "h1", display_size: "sm" }) %>
3
+ <%= pb_rails("title", props: { text: "Display Size md", tag: "h1", display_size: "md" }) %>
4
+ <%= pb_rails("title", props: { text: "Display Size lg", tag: "h1", display_size: "lg" }) %>
5
+ <%= pb_rails("title", props: { text: "Display Size xl", tag: "h1", display_size: "xl" }) %>
6
+ <%= pb_rails("title", props: { text: "Display Size xxl", tag: "h1", display_size: "xxl" }) %>
7
+ <%= pb_rails("title", props: { text: "This is a size of display", tag: "h1", size: "display"}) %>
@@ -0,0 +1,54 @@
1
+ import React from 'react'
2
+
3
+ import Title from '../_title'
4
+
5
+ const TitleDisplaySize = (props) => {
6
+ return (
7
+ <div>
8
+ <Title
9
+ displaySize="xs"
10
+ tag="h1"
11
+ text="Display Size xs"
12
+ {...props}
13
+ />
14
+ <Title
15
+ displaySize="sm"
16
+ tag="h1"
17
+ text="Display Size sm"
18
+ {...props}
19
+ />
20
+ <Title
21
+ displaySize="md"
22
+ tag="h1"
23
+ text="Display Size md"
24
+ {...props}
25
+ />
26
+ <Title
27
+ displaySize="lg"
28
+ tag="h1"
29
+ text="Display Size lg"
30
+ {...props}
31
+ />
32
+ <Title
33
+ displaySize="xl"
34
+ tag="h1"
35
+ text="Display Size xl"
36
+ {...props}
37
+ />
38
+ <Title
39
+ displaySize="xxl"
40
+ tag="h1"
41
+ text="Display Size xxl"
42
+ {...props}
43
+ />
44
+ <Title
45
+ size="display"
46
+ tag="h1"
47
+ text="This is a size of display"
48
+ {...props}
49
+ />
50
+ </div>
51
+ )
52
+ }
53
+
54
+ export default TitleDisplaySize
@@ -0,0 +1 @@
1
+ Responsive sizes for large screens and tablets, perfect for digital signage.
@@ -5,6 +5,7 @@ examples:
5
5
  - title_colors: Colors
6
6
  - title_responsive: Responsive
7
7
  - title_truncate: Truncate
8
+ - title_display_size: Display Size
8
9
 
9
10
  react:
10
11
  - title_default: Default UI
@@ -12,3 +13,4 @@ examples:
12
13
  - title_colors: Colors
13
14
  - title_responsive: Responsive
14
15
  - title_truncate: Truncate
16
+ - title_display_size: Display Size
@@ -3,3 +3,4 @@ export { default as TitleLightWeight } from './_title_light_weight.jsx'
3
3
  export { default as TitleColors } from './_title_colors.jsx'
4
4
  export { default as TitleResponsive } from './_title_responsive.jsx'
5
5
  export { default as TitleTruncate } from './_title_truncate.jsx'
6
+ export { default as TitleDisplaySize } from './_title_display_size.jsx'
@@ -16,12 +16,15 @@ module Playbook
16
16
  default: nil,
17
17
  deprecated: true
18
18
  prop :bold, type: Playbook::Props::Boolean, default: true
19
+ prop :display_size, type: Playbook::Props::Enum,
20
+ values: [nil, "xs", "sm", "md", "lg", "xl", "xxl"],
21
+ default: nil
19
22
 
20
23
  def classname
21
24
  if is_size_responsive
22
25
  generate_classname("pb_title_kit", variant, color, is_bold) + generate_responsive_size_classname
23
26
  else
24
- generate_classname("pb_title_kit", size, variant, color, is_bold)
27
+ generate_classname("pb_title_kit", size, variant, color, is_bold) + generate_display_size
25
28
  end
26
29
  end
27
30
 
@@ -29,6 +32,12 @@ module Playbook
29
32
  bold ? nil : "thin"
30
33
  end
31
34
 
35
+ def generate_display_size
36
+ return "" if display_size.nil?
37
+
38
+ " pb_title_kit_dynamic_#{display_size}"
39
+ end
40
+
32
41
  def is_size_responsive
33
42
  try(:size).is_a?(::Hash)
34
43
  end
@@ -25,12 +25,18 @@ type TooltipProps = {
25
25
  children: JSX.Element,
26
26
  data?: { [key: string]: string },
27
27
  delay?: number | Partial<{open: number; close: number}>,
28
+ height?: string,
28
29
  htmlOptions?: {[key: string]: string | number | boolean | (() => void)},
29
30
  icon?: string,
30
31
  interaction?: boolean,
32
+ maxHeight?: string,
33
+ maxWidth?: string,
34
+ minHeight?: string,
35
+ minWidth?: string,
31
36
  placement?: Placement,
32
37
  position?: "absolute" | "fixed";
33
38
  text: string,
39
+ width: string,
34
40
  showTooltip?: boolean,
35
41
  forceOpenTooltip?: boolean,
36
42
  } & GlobalProps
@@ -42,13 +48,19 @@ const Tooltip = forwardRef((props: TooltipProps, ref: ForwardedRef<unknown>): Re
42
48
  children,
43
49
  data = {},
44
50
  delay = 0,
51
+ height,
45
52
  htmlOptions = {},
46
53
  icon = null,
47
54
  interaction = false,
55
+ maxHeight,
56
+ maxWidth,
57
+ minHeight,
58
+ minWidth,
48
59
  placement: preferredPlacement = "top",
49
60
  position = "absolute",
50
61
  text,
51
62
  showTooltip = true,
63
+ width,
52
64
  zIndex,
53
65
  forceOpenTooltip = false,
54
66
  ...rest
@@ -115,6 +127,18 @@ const Tooltip = forwardRef((props: TooltipProps, ref: ForwardedRef<unknown>): Re
115
127
  top: "bottom",
116
128
  }[placement.split("-")[0]]
117
129
 
130
+ const tooltipSizing = () => {
131
+ return Object.assign(
132
+ {},
133
+ height ? { height: height } : {},
134
+ maxHeight ? { maxHeight: maxHeight } : {},
135
+ maxWidth ? { maxWidth: maxWidth } : {},
136
+ minHeight ? { minHeight: minHeight } : {},
137
+ minWidth ? { minWidth: minWidth } : {},
138
+ width ? { width: width } : {}
139
+ );
140
+ };
141
+
118
142
  return (
119
143
  <>
120
144
  <div
@@ -144,6 +168,7 @@ const Tooltip = forwardRef((props: TooltipProps, ref: ForwardedRef<unknown>): Re
144
168
  ref: refs.setFloating,
145
169
  role: "tooltip",
146
170
  style: {
171
+ ...tooltipSizing(),
147
172
  position: strategy,
148
173
  top: y ?? 0,
149
174
  left: x ?? 0,
@@ -0,0 +1,69 @@
1
+ import React from 'react'
2
+ import { Button, Tooltip, Flex, FlexItem } from 'playbook-ui';
3
+
4
+ const TooltipSizing = (props) => {
5
+
6
+ return (
7
+ <Flex
8
+ flexDirection='row'
9
+ gap='md'
10
+ wrap
11
+ >
12
+ <FlexItem>
13
+ <Tooltip
14
+ height='150px'
15
+ placement='top'
16
+ text="I'm 150px high and 100px wide!"
17
+ width='100px'
18
+ {...props}
19
+ >
20
+ <Button text="Height and Width"/>
21
+ </Tooltip>
22
+ </FlexItem>
23
+ <FlexItem>
24
+ <Tooltip
25
+ maxHeight='100px'
26
+ placement='top'
27
+ text="I have a maxHeight of 100px! Lorem ipsum dolor sit amet consectetur adipisicing elit."
28
+ width='250px'
29
+ {...props}
30
+ >
31
+ <Button text="maxHeight"/>
32
+ </Tooltip>
33
+ </FlexItem>
34
+ <FlexItem>
35
+ <Tooltip
36
+ maxWidth='150px'
37
+ placement='top'
38
+ text="I have a maxWidth of 150px! Lorem ipsum dolor sit amet consectetur adipisicing elit."
39
+ {...props}
40
+ >
41
+ <Button text="maxWidth"/>
42
+ </Tooltip>
43
+ </FlexItem>
44
+ <FlexItem>
45
+ <Tooltip
46
+ minWidth='300px'
47
+ placement='top'
48
+ text="I have a minWidth of 300px!"
49
+ {...props}
50
+ >
51
+ <Button text="minWidth"/>
52
+ </Tooltip>
53
+ </FlexItem>
54
+ <FlexItem>
55
+ <Tooltip
56
+ maxWidth='150px'
57
+ minHeight='300px'
58
+ placement='top'
59
+ text="I have a minHeight of 300px!"
60
+ {...props}
61
+ >
62
+ <Button text="minHeight"/>
63
+ </Tooltip>
64
+ </FlexItem>
65
+ </Flex>
66
+ )
67
+ }
68
+
69
+ export default TooltipSizing
@@ -0,0 +1,3 @@
1
+ You can customize the `height` and `width` of the tooltip's popover.
2
+
3
+ When using `maxHeight`, be sure to set a `width` as well. The text needs to truncate within the `width` prop.
@@ -12,7 +12,7 @@ examples:
12
12
  - tooltip_default_react: Default
13
13
  - tooltip_interaction: Content Interaction
14
14
  - tooltip_margin: Margin
15
+ - tooltip_sizing: Tooltip Sizing
15
16
  - tooltip_icon: Tooltip with Icon
16
17
  - tooltip_delay: Delay
17
18
  - tooltip_show_tooltip_react: Show Tooltip
18
-
@@ -1,6 +1,7 @@
1
1
  export { default as TooltipDefaultReact } from './_tooltip_default_react'
2
2
  export { default as TooltipInteraction } from './_tooltip_interaction'
3
3
  export { default as TooltipMargin } from './_tooltip_margin'
4
+ export { default as TooltipSizing } from './_tooltip_sizing'
4
5
  export { default as TooltipIcon } from './_tooltip_icon'
5
6
  export { default as TooltipDelay } from './_tooltip_delay'
6
7
  export { default as TooltipShowTooltipReact } from './_tooltip_show_tooltip_react'
@@ -3,7 +3,8 @@ import Select from 'react-select'
3
3
  import AsyncSelect from 'react-select/async'
4
4
  import CreateableSelect from 'react-select/creatable'
5
5
  import AsyncCreateableSelect from 'react-select/async-creatable'
6
- import { get, isString, uniqueId } from 'lodash'
6
+ import { get, isString, uniqueId } from '../utilities/object'
7
+
7
8
  import { globalProps, GlobalProps } from '../utilities/globalProps'
8
9
  import classnames from 'classnames'
9
10
 
@@ -8,15 +8,17 @@ import { SelectValueType } from '../_typeahead'
8
8
  type Props = {
9
9
  data: SelectValueType,
10
10
  multiValueTemplate: any,
11
+ wrapped?: boolean,
11
12
  pillColor?: "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",
12
13
  removeProps: any,
13
14
  selectProps: any,
14
15
  }
15
16
 
17
+
16
18
  const MultiValue = (props: Props) => {
17
19
  const { removeProps } = props
18
20
  const { imageUrl, label } = props.data
19
- const { dark, multiKit, pillColor, truncate } = props.selectProps
21
+ const { dark, multiKit, pillColor, truncate, wrapped } = props.selectProps
20
22
 
21
23
  const formPillProps = {
22
24
  marginRight: 'xs',
@@ -52,6 +54,7 @@ const MultiValue = (props: Props) => {
52
54
  size={multiKit === 'smallPill' ? 'small' : ''}
53
55
  text=''
54
56
  truncate={truncate}
57
+ wrapped={wrapped}
55
58
  {...props}
56
59
  />
57
60
  }
@@ -66,6 +69,7 @@ const MultiValue = (props: Props) => {
66
69
  size={multiKit === 'smallPill' ? 'small' : ''}
67
70
  text={label}
68
71
  truncate={truncate}
72
+ wrapped={wrapped}
69
73
  {...props}
70
74
  />
71
75
  }
@@ -44,6 +44,8 @@ module Playbook
44
44
  default: false
45
45
  prop :validation, type: Playbook::Props::HashProp,
46
46
  default: {}
47
+ prop :wrapped, type: Playbook::Props::Boolean,
48
+ default: false
47
49
  prop :clear_on_context_change, type: Playbook::Props::Boolean,
48
50
  default: true
49
51
  prop :options_by_context, type: Playbook::Props::HashProp,
@@ -72,7 +74,7 @@ module Playbook
72
74
  end
73
75
 
74
76
  def is_react?
75
- pills || !is_multi
77
+ pills || !is_multi || wrapped
76
78
  end
77
79
 
78
80
  def typeahead_react_options
@@ -94,6 +96,7 @@ module Playbook
94
96
  placeholder: placeholder,
95
97
  plusIcon: plus_icon,
96
98
  truncate: truncate,
99
+ wrapped: wrapped,
97
100
  searchContextSelector: search_context_selector,
98
101
  optionsByContext: options_by_context,
99
102
  clearOnContextChange: clear_on_context_change,
@@ -0,0 +1,99 @@
1
+ import { isEmpty, get, isString, uniqueId, omitBy } from './object';
2
+
3
+ describe('Lodash functions', () => {
4
+ describe('isEmpty', () => {
5
+ test('returns true for empty objects', () => {
6
+ expect(isEmpty({})).toBe(true);
7
+ });
8
+ test('returns true for empty arrays', () => {
9
+ expect(isEmpty([])).toBe(true);
10
+ });
11
+ test('returns false for non-empty objects', () => {
12
+ expect(isEmpty({ a: 1 })).toBe(false);
13
+ });
14
+ test('returns false for non-empty arrays', () => {
15
+ expect(isEmpty([1])).toBe(false);
16
+ });
17
+ test('returns true for null and undefined', () => {
18
+ expect(isEmpty(null)).toBe(true);
19
+ expect(isEmpty(undefined)).toBe(true);
20
+ });
21
+ test('returns false for non-object, non-array values', () => {
22
+ expect(isEmpty("hello")).toBe(false);
23
+ expect(isEmpty(123)).toBe(false);
24
+ });
25
+ });
26
+
27
+ describe('get', () => {
28
+ const obj = { a: { b: { c: 42 } }, arr: [{ x: 1 }, { y: 2 }] };
29
+
30
+ test('retrieves nested properties by dot path', () => {
31
+ expect(get(obj, 'a.b.c')).toBe(42);
32
+ });
33
+ test('returns default value if path does not exist', () => {
34
+ expect(get(obj, 'a.b.d', 'default')).toBe('default');
35
+ });
36
+ test('retrieves array values using bracket notation', () => {
37
+ expect(get(obj, 'arr[1].y')).toBe(2);
38
+ });
39
+ test('returns default for non-existent paths', () => {
40
+ expect(get(obj, 'non.existent.path', null)).toBe(null);
41
+ });
42
+ });
43
+
44
+ describe('isString', () => {
45
+ test('returns true for string literal', () => {
46
+ expect(isString('test')).toBe(true);
47
+ });
48
+ test('returns true for String object', () => {
49
+ expect(isString(new String('test'))).toBe(true);
50
+ });
51
+ test('returns false for non-string values', () => {
52
+ expect(isString(123)).toBe(false);
53
+ expect(isString(null)).toBe(false);
54
+ expect(isString({ a: 1 })).toBe(false);
55
+ expect(isString([ 'a','b','c' ])).toBe(false);
56
+ });
57
+ });
58
+
59
+ describe('uniqueId', () => {
60
+ test('generates unique ids without prefix', () => {
61
+ const id1 = uniqueId();
62
+ const id2 = uniqueId();
63
+ expect(id1).not.toBe(id2);
64
+ expect(Number(id1)).toBeLessThan(Number(id2));
65
+ });
66
+ test('generates unique ids with prefix', () => {
67
+ const id1 = uniqueId('id_');
68
+ const id2 = uniqueId('id_');
69
+ const id3 = uniqueId('id_');
70
+ expect(id1).not.toBe(id2);
71
+ expect(id1).not.toBe(id3);
72
+ expect(id2).not.toBe(id3);
73
+ expect(id1.startsWith('id_')).toBe(true);
74
+ expect(id2.startsWith('id_')).toBe(true);
75
+ expect(id3.startsWith('id_')).toBe(true);
76
+ });
77
+ });
78
+
79
+ describe('omitBy', () => {
80
+ test('omits keys for which predicate returns true', () => {
81
+ const obj = { a: 1, b: 2, c: 3 };
82
+ const isEven = value => value % 2 === 0;
83
+ const noEvenValues = omitBy(obj, isEven);
84
+ expect(noEvenValues).toEqual({ a: 1, c: 3 });
85
+ });
86
+ test('returns empty object for null input', () => {
87
+ expect(omitBy(null, () => true)).toEqual({});
88
+ });
89
+ test('returns empty object for non-object input', () => {
90
+ expect(omitBy("string", () => true)).toEqual({});
91
+ });
92
+ test('returns original object if predicate returns false for all keys', () => {
93
+ const obj = { a: 1, b: 2 };
94
+ const isBiggerThanFive = value => value >= 4;
95
+ const objWithSmallValues = omitBy(obj, isBiggerThanFive);
96
+ expect(objWithSmallValues).toEqual(obj);
97
+ });
98
+ });
99
+ });
@@ -1,3 +1,31 @@
1
1
  /* 🛠️ Any commonly used lodash functions can be added here. 🤙 */
2
2
 
3
- export const isEmpty = (obj: Record<string, unknown>): boolean => Object.keys(obj).length < 1
3
+ export const isEmpty = (obj: any) => [Object, Array].includes((obj || {}).constructor) && !Object.entries((obj || {})).length;
4
+
5
+ export const get = <T, R = any>(obj: T, path: string, defaultValue?: R): R | any => {
6
+ const travel = (regexp: RegExp): any =>
7
+ String.prototype.split
8
+ .call(path, regexp)
9
+ .filter(Boolean)
10
+ .reduce((res: any, key: string) => (res !== null && res !== undefined ? res[key] : res), obj)
11
+ const result = travel(/[,[\]]+?/) || travel(/[,[\].]+?/)
12
+ return result === undefined || result === obj ? defaultValue : result
13
+ }
14
+
15
+ export const isString = (str: unknown): str is string =>
16
+ str != null && typeof (str as any).valueOf() === "string"
17
+
18
+ export const uniqueId: (prefix?: string) => string = (() => {
19
+ let counter = 0
20
+ return (prefix = '') => `${prefix}${++counter}`
21
+ })()
22
+
23
+ export const omitBy = (obj: Record<string, any>, predicate: (value: any, key: string) => boolean) => {
24
+ if (obj === null || typeof obj !== 'object') return {}
25
+ return Object.keys(obj).reduce((result: Record<string, any>, key: string) => {
26
+ if (!predicate(obj[key], key)) {
27
+ result[key] = obj[key];
28
+ }
29
+ return result;
30
+ }, {})
31
+ }