playbook_ui 16.1.0.pre.rc.1 → 16.1.0.pre.rc.3

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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.html.erb +2 -2
  3. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.rb +4 -0
  4. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_inline_row_loading.md +2 -2
  5. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_inline_row_loading_rails.html.erb +64 -0
  6. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_inline_row_loading_rails.md +18 -0
  7. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +1 -0
  8. data/app/pb_kits/playbook/pb_advanced_table/table_body.rb +51 -1
  9. data/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb +1 -1
  10. data/app/pb_kits/playbook/pb_advanced_table/table_header.rb +34 -0
  11. data/app/pb_kits/playbook/pb_advanced_table/table_row.html.erb +1 -1
  12. data/app/pb_kits/playbook/pb_advanced_table/table_row.rb +19 -0
  13. data/app/pb_kits/playbook/pb_background/docs/_background_responsive.jsx +30 -0
  14. data/app/pb_kits/playbook/pb_background/docs/_background_responsive.md +1 -0
  15. data/app/pb_kits/playbook/pb_background/docs/example.yml +1 -0
  16. data/app/pb_kits/playbook/pb_background/docs/index.js +1 -0
  17. data/app/pb_kits/playbook/pb_form/docs/_form_with_required_indicator.html.erb +3 -1
  18. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_input_display.html.erb +74 -0
  19. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_input_display.jsx +87 -0
  20. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_input_display.md +3 -0
  21. data/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +35 -33
  22. data/app/pb_kits/playbook/pb_multi_level_select/docs/index.js +1 -0
  23. data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.tsx +33 -6
  24. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_required_indicator.jsx +35 -0
  25. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_required_indicator.md +3 -0
  26. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_required_indicator.html.erb +10 -0
  27. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_required_indicator.jsx +21 -0
  28. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_required_indicator.md +3 -0
  29. data/app/pb_kits/playbook/pb_rich_text_editor/docs/example.yml +3 -0
  30. data/app/pb_kits/playbook/pb_rich_text_editor/docs/index.js +2 -0
  31. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.rb +5 -0
  32. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.test.js +33 -18
  33. data/app/pb_kits/playbook/pb_table/docs/_sections.yml +68 -0
  34. data/app/pb_kits/playbook/pb_textarea/_textarea.tsx +29 -11
  35. data/app/pb_kits/playbook/pb_textarea/docs/_textarea_input_options.html.erb +39 -0
  36. data/app/pb_kits/playbook/pb_textarea/docs/_textarea_input_options.md +3 -0
  37. data/app/pb_kits/playbook/pb_textarea/docs/_textarea_required_indicator.html.erb +5 -0
  38. data/app/pb_kits/playbook/pb_textarea/docs/_textarea_required_indicator.jsx +25 -0
  39. data/app/pb_kits/playbook/pb_textarea/docs/_textarea_required_indicator.md +3 -0
  40. data/app/pb_kits/playbook/pb_textarea/docs/example.yml +4 -1
  41. data/app/pb_kits/playbook/pb_textarea/docs/index.js +1 -0
  42. data/app/pb_kits/playbook/pb_textarea/index.ts +12 -5
  43. data/app/pb_kits/playbook/pb_textarea/textarea.html.erb +10 -10
  44. data/app/pb_kits/playbook/pb_textarea/textarea.rb +30 -0
  45. data/app/pb_kits/playbook/pb_textarea/textarea.test.js +18 -1
  46. data/app/pb_kits/playbook/utilities/test/globalProps/borderRadius.test.js +33 -0
  47. data/app/pb_kits/playbook/utilities/test/globalProps/bottom.test.js +60 -0
  48. data/app/pb_kits/playbook/utilities/test/globalProps/cursor.test.js +42 -0
  49. data/app/pb_kits/playbook/utilities/test/globalProps/dark.test.js +33 -0
  50. data/app/pb_kits/playbook/utilities/test/globalProps/gap.test.js +87 -0
  51. data/app/pb_kits/playbook/utilities/test/globalProps/globalProps.integration.test.js +936 -0
  52. data/app/pb_kits/playbook/utilities/test/globalProps/height.test.js +68 -0
  53. data/app/pb_kits/playbook/utilities/test/globalProps/htmlOptions.test.js +510 -0
  54. data/app/pb_kits/playbook/utilities/test/globalProps/left.test.js +60 -0
  55. data/app/pb_kits/playbook/utilities/test/globalProps/lineHeight.test.js +33 -0
  56. data/app/pb_kits/playbook/utilities/test/globalProps/margin.test.js +95 -0
  57. data/app/pb_kits/playbook/utilities/test/globalProps/numberSpacing.test.js +33 -0
  58. data/app/pb_kits/playbook/utilities/test/globalProps/overflow.test.js +68 -0
  59. data/app/pb_kits/playbook/utilities/test/globalProps/padding.test.js +95 -0
  60. data/app/pb_kits/playbook/utilities/test/globalProps/position.test.js +33 -0
  61. data/app/pb_kits/playbook/utilities/test/globalProps/right.test.js +60 -0
  62. data/app/pb_kits/playbook/utilities/test/globalProps/shadow.test.js +33 -0
  63. data/app/pb_kits/playbook/utilities/test/globalProps/textAlign.test.js +41 -0
  64. data/app/pb_kits/playbook/utilities/test/globalProps/top.test.js +60 -0
  65. data/app/pb_kits/playbook/utilities/test/globalProps/verticalAlign.test.js +40 -0
  66. data/app/pb_kits/playbook/utilities/test/globalProps/width.test.js +66 -0
  67. data/app/pb_kits/playbook/utilities/test/globalProps/zIndex.test.js +50 -0
  68. data/dist/chunks/{_pb_line_graph-hxi01lk7.js → _pb_line_graph-BgKF_zz1.js} +1 -1
  69. data/dist/chunks/{_typeahead-BgLnlhzP.js → _typeahead-B9a6ZsEP.js} +1 -1
  70. data/dist/chunks/{globalProps-DgYwLYNx.js → globalProps-BhVYCqRf.js} +1 -1
  71. data/dist/chunks/{lib-NLxTo8OB.js → lib-DD34ZrWL.js} +1 -1
  72. data/dist/chunks/vendor.js +2 -2
  73. data/dist/playbook-rails-react-bindings.js +1 -1
  74. data/dist/playbook-rails.js +1 -1
  75. data/lib/playbook/version.rb +1 -1
  76. metadata +46 -6
@@ -11,18 +11,21 @@ export default class PbTextarea extends PbEnhancedElement {
11
11
  }
12
12
 
13
13
  hasEmojiMask(): boolean {
14
+ if (!this.element) return false
14
15
  return (this.element as HTMLElement).dataset.pbEmojiMask === "true"
15
16
  }
16
17
 
17
- onInput(): void {
18
+ onInput = (): void => {
19
+ if (!this.element) return
20
+
18
21
  if ((this.element as HTMLElement).closest('.resize_auto')) {
19
- this.style.height = 'auto'
20
- this.style.height = (this.scrollHeight) + 'px'
22
+ (this.element as HTMLTextAreaElement).style.height = 'auto';
23
+ (this.element as HTMLTextAreaElement).style.height = (this.element as HTMLTextAreaElement).scrollHeight + 'px'
21
24
  }
22
25
  }
23
26
 
24
27
  handleEmojiInput = (): void => {
25
- if (!this.hasEmojiMask()) return
28
+ if (!this.element || !this.hasEmojiMask()) return
26
29
 
27
30
  if (this.skipNextEmojiFilter) {
28
31
  this.skipNextEmojiFilter = false
@@ -33,7 +36,7 @@ export default class PbTextarea extends PbEnhancedElement {
33
36
  }
34
37
 
35
38
  handleEmojiPaste = (event: ClipboardEvent): void => {
36
- if (!this.hasEmojiMask()) return
39
+ if (!this.element || !this.hasEmojiMask()) return
37
40
 
38
41
  const pastedText = event.clipboardData?.getData('text') || ''
39
42
  const filteredText = stripEmojisForPaste(pastedText)
@@ -57,6 +60,8 @@ export default class PbTextarea extends PbEnhancedElement {
57
60
  }
58
61
 
59
62
  connect(): void {
63
+ if (!this.element) return
64
+
60
65
  if ((this.element as HTMLElement).closest('.resize_auto')) {
61
66
  this.element.setAttribute('style', 'height:' + (this.element as HTMLTextAreaElement).scrollHeight + 'px;overflow-y:hidden;')
62
67
  this.element.addEventListener('input', this.onInput, false)
@@ -69,6 +74,8 @@ export default class PbTextarea extends PbEnhancedElement {
69
74
  }
70
75
 
71
76
  disconnect(): void {
77
+ if (!this.element) return
78
+
72
79
  this.element.removeEventListener('input', this.onInput, false)
73
80
  this.element.removeEventListener('input', this.handleEmojiInput, false)
74
81
  this.element.removeEventListener('paste', this.handleEmojiPaste as EventListener, false)
@@ -1,6 +1,12 @@
1
1
  <%= pb_content_tag do %>
2
2
  <% if object.label.present? %>
3
+ <% if object.required_indicator %>
4
+ <%= pb_rails("caption", props: { text: object.label, dark: object.dark }) do %>
5
+ <%= object.label %><span style="color: #DA0014;"> *</span>
6
+ <% end %>
7
+ <% else %>
3
8
  <%= pb_rails("caption", props: {text: object.label, dark: object.dark}) %>
9
+ <% end %>
4
10
  <% end %>
5
11
  <% if content.present? %>
6
12
  <%= content %>
@@ -8,16 +14,10 @@
8
14
  <%= pb_rails("body", props: { dark: object.dark, status: "negative", text: object.error }) %>
9
15
  <% end %>
10
16
  <% else %>
11
- <%= text_area(
12
- :object,
13
- :method,
14
- :data => object.textarea_options[:data],
15
- :max_characters => object.max_characters,
16
- :name => object.name,
17
- :onkeyup => object.onkeyup,
18
- :placeholder => object.placeholder,
19
- :rows => object.rows,
20
- :value => object.value) %>
17
+ <%= text_area_tag(
18
+ object.name,
19
+ object.value,
20
+ object.all_textarea_attributes) %>
21
21
  <% if object.error %>
22
22
  <% if object.character_count %>
23
23
  <%= pb_rails("flex", props: { spacing: "between", vertical: "center" }) do %>
@@ -8,6 +8,8 @@ module Playbook
8
8
  prop :error
9
9
  prop :inline, type: Playbook::Props::Boolean,
10
10
  default: false
11
+ prop :input_options, type: Playbook::Props::HashProp,
12
+ default: {}
11
13
  prop :label
12
14
  prop :method
13
15
  prop :name
@@ -21,6 +23,8 @@ module Playbook
21
23
  prop :character_count
22
24
  prop :onkeyup
23
25
  prop :max_characters
26
+ prop :required_indicator, type: Playbook::Props::Boolean,
27
+ default: false
24
28
 
25
29
  def classname
26
30
  generate_classname("pb_textarea_kit") + error_class + resize_class + inline_class
@@ -36,6 +40,32 @@ module Playbook
36
40
  }
37
41
  end
38
42
 
43
+ def all_textarea_attributes
44
+ # Merge data attributes: emoji_mask data + input_options data
45
+ data_attrs = textarea_options[:data] || {}
46
+ input_data = input_options[:data] || {}
47
+ merged_data = data_attrs.merge(input_data)
48
+
49
+ base_attributes = {
50
+ id: input_options[:id] || "object_method",
51
+ max_characters: max_characters,
52
+ name: name,
53
+ onkeyup: onkeyup,
54
+ placeholder: placeholder,
55
+ rows: rows,
56
+ value: value,
57
+ }
58
+
59
+ # Merge input_options (excluding data to handle separately)
60
+ input_options_without_data = input_options.except(:data)
61
+ result = base_attributes.merge(input_options_without_data)
62
+
63
+ # Add merged data if present (input_options data takes precedence over emoji_mask data)
64
+ result[:data] = merged_data unless merged_data.empty?
65
+
66
+ result
67
+ end
68
+
39
69
  private
40
70
 
41
71
  def error_class
@@ -1,5 +1,5 @@
1
1
  import React, { useState } from "react"
2
- import { render, screen, fireEvent } from "../utilities/test-utils"
2
+ import { render, screen, fireEvent, within } from "../utilities/test-utils"
3
3
 
4
4
  import Textarea from "./_textarea"
5
5
 
@@ -265,4 +265,21 @@ describe("Textarea Emoji Mask", () => {
265
265
  fireEvent.change(textarea, { target: { value: 'àëǒüñ' } })
266
266
  expect(textarea.value).toBe('àëǒüñ')
267
267
  })
268
+
269
+ test('renders required indicator asterisk when requiredIndicator is true', () => {
270
+ render(
271
+ <Textarea
272
+ data={{ testid: testId }}
273
+ label="Name"
274
+ required
275
+ requiredIndicator
276
+ />
277
+ )
278
+
279
+ const kit = screen.getByTestId(testId)
280
+ const label = within(kit).getByText(/Name/)
281
+
282
+ expect(label).toBeInTheDocument()
283
+ expect(kit).toHaveTextContent('*')
284
+ })
268
285
  })
@@ -0,0 +1,33 @@
1
+ import { testGlobalProp, testGlobalPropAbsence, testGlobalPropInvalidValues } from './globalPropsTestHelper'
2
+ import Body from '../../../pb_body/_body'
3
+ import Button from '../../../pb_button/_button'
4
+ import Card from '../../../pb_card/_card'
5
+ import Title from '../../../pb_title/_title'
6
+ import Flex from '../../../pb_flex/_flex'
7
+ import Link from '../../../pb_link/_link'
8
+ import Badge from '../../../pb_badge/_badge'
9
+
10
+ // NOTE: TextInput excluded - borderRadius is not a valid prop for input elements
11
+ testGlobalProp(
12
+ 'borderRadius',
13
+ ['none', 'xs', 'sm', 'md', 'lg', 'xl', 'rounded'],
14
+ (v) => `border_radius_${v}`,
15
+ null,
16
+ [Body, Button, Card, Title, Flex, Link, Badge]
17
+ )
18
+
19
+ testGlobalPropAbsence(
20
+ 'borderRadius',
21
+ ['border_radius_none', 'border_radius_xs', 'border_radius_sm', 'border_radius_md', 'border_radius_lg', 'border_radius_xl', 'border_radius_rounded'],
22
+ undefined,
23
+ { skipNull: true }
24
+ )
25
+
26
+ // NOTE: Currently using skipKnownIssues: true because globalProps.ts generates classes for invalid values
27
+ testGlobalPropInvalidValues(
28
+ 'borderRadius',
29
+ ['invalid', 'bad_value', 'not_a_radius', 'special-chars!@#'],
30
+ ['border_radius_invalid', 'border_radius_bad_value', 'border_radius_not_a_radius', 'border_radius_special-chars!@#'],
31
+ undefined,
32
+ { skipKnownIssues: true }
33
+ )
@@ -0,0 +1,60 @@
1
+ import React from 'react'
2
+ import { testGlobalProp, testGlobalPropAbsence, testGlobalPropInvalidValues } from './globalPropsTestHelper'
3
+ import { render, screen } from '../../test-utils'
4
+ import Body from '../../../pb_body/_body'
5
+ import Button from '../../../pb_button/_button'
6
+ import Card from '../../../pb_card/_card'
7
+ import Title from '../../../pb_title/_title'
8
+ import Flex from '../../../pb_flex/_flex'
9
+ import Link from '../../../pb_link/_link'
10
+ import Badge from '../../../pb_badge/_badge'
11
+
12
+ const validSizes = ['xs', 'sm', 'md', 'lg', 'xl']
13
+
14
+ // NOTE: TextInput excluded - positioning props are not valid for input elements
15
+ // Test bottom prop with string values
16
+ testGlobalProp(
17
+ 'bottom',
18
+ validSizes,
19
+ (v) => `bottom_${v}`,
20
+ null,
21
+ [Body, Button, Card, Title, Flex, Link, Badge]
22
+ )
23
+
24
+ // Test bottom prop with object values (inset) - tested separately due to object value complexity
25
+ test('Global Props: bottom returns proper class name with object values (inset)', () => {
26
+ const testCases = [
27
+ { value: { value: 'md', inset: true }, expected: 'bottom_md_inset' },
28
+ { value: { value: 'lg', inset: false }, expected: 'bottom_lg' },
29
+ { value: { value: 'sm', inset: true }, expected: 'bottom_sm_inset' }
30
+ ]
31
+
32
+ testCases.forEach(({ value, expected }) => {
33
+ const testId = `body-bottom-object-${value.value}-${value.inset}`
34
+ render(
35
+ <Body
36
+ bottom={value}
37
+ data={{ testid: testId }}
38
+ text="Hi"
39
+ />
40
+ )
41
+ const kit = screen.getByTestId(testId)
42
+ expect(kit).toHaveClass(expected)
43
+ })
44
+ })
45
+
46
+ testGlobalPropAbsence(
47
+ 'bottom',
48
+ ['bottom_xs', 'bottom_sm', 'bottom_md', 'bottom_lg', 'bottom_xl'],
49
+ undefined,
50
+ { skipNull: true }
51
+ )
52
+
53
+ // NOTE: Currently using skipKnownIssues: true because globalProps.ts generates classes for invalid values
54
+ testGlobalPropInvalidValues(
55
+ 'bottom',
56
+ ['invalid', 'bad_value', 'not_a_size', 'special-chars!@#'],
57
+ ['bottom_invalid', 'bottom_bad_value', 'bottom_not_a_size', 'bottom_special-chars!@#'],
58
+ undefined,
59
+ { skipKnownIssues: true }
60
+ )
@@ -0,0 +1,42 @@
1
+ import { testGlobalProp, testGlobalPropAbsence, testGlobalPropInvalidValues } from './globalPropsTestHelper'
2
+ import { camelToSnakeCase } from '../../../utilities/text'
3
+ import Body from '../../../pb_body/_body'
4
+ import Button from '../../../pb_button/_button'
5
+ import Card from '../../../pb_card/_card'
6
+ import Title from '../../../pb_title/_title'
7
+ import TextInput from '../../../pb_text_input/_text_input'
8
+ import Flex from '../../../pb_flex/_flex'
9
+ import Link from '../../../pb_link/_link'
10
+ import Badge from '../../../pb_badge/_badge'
11
+
12
+ const validValues = [
13
+ 'auto', 'default', 'none', 'contextMenu', 'help', 'pointer', 'progress', 'wait', 'cell',
14
+ 'crosshair', 'text', 'verticalText', 'alias', 'copy', 'move', 'noDrop', 'notAllowed', 'grab',
15
+ 'grabbing', 'eResize', 'nResize', 'neResize', 'nwResize', 'sResize', 'seResize', 'swResize', 'wResize',
16
+ 'ewResize', 'nsResize', 'neswResize', 'nwseResize', 'colResize', 'rowResize', 'allScroll', 'zoomIn', 'zoomOut'
17
+ ]
18
+
19
+ testGlobalProp(
20
+ 'cursor',
21
+ validValues,
22
+ (v) => `cursor_${camelToSnakeCase(v)}`,
23
+ null,
24
+ [Body, Button, Card, Title, TextInput, Flex, Link, Badge]
25
+ )
26
+
27
+ testGlobalPropAbsence(
28
+ 'cursor',
29
+ ['cursor_auto', 'cursor_pointer', 'cursor_default', 'cursor_none'],
30
+ undefined,
31
+ { skipNull: true }
32
+ )
33
+
34
+ // NOTE: Currently using skipKnownIssues: true because globalProps.ts generates classes for invalid values
35
+ // NOTE: Using allowRenderingErrors: true because invalid types (like numbers) cause rendering errors with camelToSnakeCase
36
+ testGlobalPropInvalidValues(
37
+ 'cursor',
38
+ ['invalid', 'bad_value', 'not_a_cursor', 'special-chars!@#'],
39
+ ['cursor_invalid', 'cursor_bad_value', 'cursor_not_a_cursor', 'cursor_special-chars!@#'],
40
+ undefined,
41
+ { skipKnownIssues: true, allowRenderingErrors: true }
42
+ )
@@ -0,0 +1,33 @@
1
+ import { testGlobalProp, testGlobalPropAbsence, testGlobalPropInvalidValues } from './globalPropsTestHelper'
2
+ import Body from '../../../pb_body/_body'
3
+ import Button from '../../../pb_button/_button'
4
+ import Card from '../../../pb_card/_card'
5
+ import Title from '../../../pb_title/_title'
6
+ import TextInput from '../../../pb_text_input/_text_input'
7
+ import Flex from '../../../pb_flex/_flex'
8
+ import Link from '../../../pb_link/_link'
9
+ import Badge from '../../../pb_badge/_badge'
10
+
11
+ testGlobalProp(
12
+ 'dark',
13
+ [true],
14
+ () => 'dark',
15
+ null,
16
+ [Body, Button, Card, Title, TextInput, Flex, Link, Badge]
17
+ )
18
+
19
+ testGlobalPropAbsence(
20
+ 'dark',
21
+ ['dark'],
22
+ undefined,
23
+ { skipNull: true }
24
+ )
25
+
26
+ // NOTE: Currently using skipKnownIssues: true because globalProps.ts generates classes for invalid values
27
+ testGlobalPropInvalidValues(
28
+ 'dark',
29
+ ['invalid', 'bad_value', 123, 'true', 'false'],
30
+ ['dark_invalid', 'dark_bad_value', 'dark_123', 'dark_true', 'dark_false'],
31
+ undefined,
32
+ { skipKnownIssues: true }
33
+ )
@@ -0,0 +1,87 @@
1
+ import { testGlobalProp, testGlobalPropResponsiveWithDefault, testGlobalPropAbsence, testGlobalPropInvalidValues } from './globalPropsTestHelper'
2
+ import Body from '../../../pb_body/_body'
3
+ import Button from '../../../pb_button/_button'
4
+ import Card from '../../../pb_card/_card'
5
+ import Title from '../../../pb_title/_title'
6
+ import Flex from '../../../pb_flex/_flex'
7
+ import Link from '../../../pb_link/_link'
8
+ import Badge from '../../../pb_badge/_badge'
9
+
10
+ // NOTE: TextInput excluded - gap properties are not valid props for input elements
11
+ // Test gap prop
12
+ testGlobalProp(
13
+ 'gap',
14
+ ['xs', 'sm', 'md', 'lg', 'xl'],
15
+ (v) => `gap_${v}`,
16
+ (size, v) => `gap_${size}_${v}`,
17
+ [Body, Button, Card, Title, Flex, Link, Badge]
18
+ )
19
+
20
+ testGlobalPropResponsiveWithDefault(
21
+ 'gap',
22
+ { default: 'md', xs: 'xs', sm: 'md', md: 'lg' },
23
+ (v) => `gap_${v}`,
24
+ (size, v) => `gap_${size}_${v}`
25
+ )
26
+
27
+ testGlobalPropAbsence(
28
+ 'gap',
29
+ ['gap_xs', 'gap_sm', 'gap_md', 'gap_lg', 'gap_xl'],
30
+ undefined,
31
+ { skipNull: true }
32
+ )
33
+
34
+ // Test columnGap prop
35
+ testGlobalProp(
36
+ 'columnGap',
37
+ ['xs', 'sm', 'md', 'lg', 'xl'],
38
+ (v) => `column_gap_${v}`,
39
+ (size, v) => `column_gap_${size}_${v}`,
40
+ [Body, Button, Card, Title, Flex, Link, Badge]
41
+ )
42
+
43
+ testGlobalPropResponsiveWithDefault(
44
+ 'columnGap',
45
+ { default: 'md', xs: 'xs', sm: 'md', md: 'lg' },
46
+ (v) => `column_gap_${v}`,
47
+ (size, v) => `column_gap_${size}_${v}`
48
+ )
49
+
50
+ testGlobalPropAbsence(
51
+ 'columnGap',
52
+ ['column_gap_xs', 'column_gap_sm', 'column_gap_md', 'column_gap_lg', 'column_gap_xl'],
53
+ undefined,
54
+ { skipNull: true }
55
+ )
56
+
57
+ // Test rowGap prop
58
+ testGlobalProp(
59
+ 'rowGap',
60
+ ['xs', 'sm', 'md', 'lg', 'xl'],
61
+ (v) => `row_gap_${v}`,
62
+ (size, v) => `row_gap_${size}_${v}`,
63
+ [Body, Button, Card, Title, Flex, Link, Badge]
64
+ )
65
+
66
+ testGlobalPropResponsiveWithDefault(
67
+ 'rowGap',
68
+ { default: 'md', xs: 'xs', sm: 'md', md: 'lg' },
69
+ (v) => `row_gap_${v}`,
70
+ (size, v) => `row_gap_${size}_${v}`
71
+ )
72
+
73
+ testGlobalPropAbsence(
74
+ 'rowGap',
75
+ ['row_gap_xs', 'row_gap_sm', 'row_gap_md', 'row_gap_lg', 'row_gap_xl'],
76
+ undefined,
77
+ { skipNull: true }
78
+ )
79
+
80
+ // NOTE: Currently using skipKnownIssues: true because globalProps.ts generates classes for invalid values
81
+ testGlobalPropInvalidValues(
82
+ 'gap',
83
+ ['invalid', 'bad_value', 'not_a_size', 'special-chars!@#'],
84
+ ['gap_invalid', 'gap_bad_value', 'gap_not_a_size', 'gap_special-chars!@#'],
85
+ undefined,
86
+ { skipKnownIssues: true }
87
+ )