playbook_ui 14.10.0.pre.alpha.PBNTR662stickyrightcolumnreact5160 → 14.10.0.pre.alpha.PLAY1750pbcontenttagkitbutton5308

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/_playbook.scss +1 -0
  3. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +11 -16
  4. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.html.erb +1 -1
  5. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.rb +8 -1
  6. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_beta_sort.html.erb +1 -3
  7. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_beta_subrow_headers.html.erb +1 -1
  8. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_beta_subrow_headers.md +1 -1
  9. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_headers.html.erb +43 -0
  10. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_headers_multiple.html.erb +58 -0
  11. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +3 -0
  12. data/app/pb_kits/playbook/pb_advanced_table/index.js +185 -92
  13. data/app/pb_kits/playbook/pb_advanced_table/table_body.html.erb +1 -1
  14. data/app/pb_kits/playbook/pb_advanced_table/table_body.rb +55 -8
  15. data/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb +17 -14
  16. data/app/pb_kits/playbook/pb_advanced_table/table_header.rb +78 -0
  17. data/app/pb_kits/playbook/pb_advanced_table/table_row.html.erb +4 -3
  18. data/app/pb_kits/playbook/pb_advanced_table/table_row.rb +13 -2
  19. data/app/pb_kits/playbook/pb_advanced_table/table_subrow_header.html.erb +2 -6
  20. data/app/pb_kits/playbook/pb_advanced_table/table_subrow_header.rb +6 -4
  21. data/app/pb_kits/playbook/pb_button/button.html.erb +2 -3
  22. data/app/pb_kits/playbook/pb_card/_card.scss +21 -3
  23. data/app/pb_kits/playbook/pb_card/docs/_card_header.html.erb +18 -0
  24. data/app/pb_kits/playbook/pb_card/docs/_card_header.jsx +40 -0
  25. data/app/pb_kits/playbook/pb_checkbox/checkbox.html.erb +1 -6
  26. data/app/pb_kits/playbook/pb_date_picker/date_picker.html.erb +1 -6
  27. data/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +7 -12
  28. data/app/pb_kits/playbook/pb_dropdown/dropdown_container.html.erb +9 -14
  29. data/app/pb_kits/playbook/pb_dropdown/dropdown_option.html.erb +6 -11
  30. data/app/pb_kits/playbook/pb_dropdown/dropdown_trigger.html.erb +8 -14
  31. data/app/pb_kits/playbook/pb_file_upload/_file_upload.tsx +24 -15
  32. data/app/pb_kits/playbook/pb_file_upload/docs/_file_upload_accept.jsx +3 -1
  33. data/app/pb_kits/playbook/pb_file_upload/docs/_file_upload_custom_description.jsx +4 -1
  34. data/app/pb_kits/playbook/pb_file_upload/docs/_file_upload_max_size.jsx +1 -1
  35. data/app/pb_kits/playbook/pb_icon_circle/_icon_circle.scss +1 -13
  36. data/app/pb_kits/playbook/pb_section_separator/_section_separator.scss +64 -1
  37. data/app/pb_kits/playbook/pb_section_separator/_section_separator.tsx +3 -1
  38. data/app/pb_kits/playbook/pb_section_separator/docs/_section_separator_color.html.erb +10 -0
  39. data/app/pb_kits/playbook/pb_section_separator/docs/_section_separator_color.jsx +42 -0
  40. data/app/pb_kits/playbook/pb_section_separator/docs/_section_separator_color.md +3 -0
  41. data/app/pb_kits/playbook/pb_section_separator/docs/example.yml +2 -0
  42. data/app/pb_kits/playbook/pb_section_separator/docs/index.js +1 -0
  43. data/app/pb_kits/playbook/pb_section_separator/section_separator.rb +4 -1
  44. data/app/pb_kits/playbook/pb_skeleton_loading/_skeleton_loading.scss +2 -2
  45. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_description.md +3 -0
  46. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_border_radius.html.erb +9 -0
  47. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_border_radius_rails.md +1 -0
  48. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_color.html.erb +7 -0
  49. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_default.html.erb +1 -1
  50. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_filter.html.erb +119 -0
  51. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_filter.jsx +10 -2
  52. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_height_width.html.erb +15 -0
  53. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_height_width.jsx +6 -2
  54. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_height_width_rails.md +3 -0
  55. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_layout.html.erb +3 -0
  56. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_user.html.erb +63 -0
  57. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_user.jsx +11 -3
  58. data/app/pb_kits/playbook/pb_skeleton_loading/docs/example.yml +7 -2
  59. data/app/pb_kits/playbook/pb_skeleton_loading/skeleton_loading.html.erb +8 -12
  60. data/app/pb_kits/playbook/pb_skeleton_loading/skeleton_loading.rb +48 -1
  61. data/app/pb_kits/playbook/pb_table/_table.tsx +8 -1
  62. data/app/pb_kits/playbook/pb_table/docs/example.yml +2 -0
  63. data/app/pb_kits/playbook/pb_table/docs/index.js +2 -0
  64. data/app/pb_kits/playbook/pb_table/styles/_scroll.scss +20 -0
  65. data/app/pb_kits/playbook/pb_text_input/_text_input.tsx +8 -1
  66. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_mask.jsx +18 -0
  67. data/app/pb_kits/playbook/pb_text_input/inputMask.ts +45 -1
  68. data/app/pb_kits/playbook/pb_text_input/text_input.test.js +80 -0
  69. data/app/pb_kits/playbook/pb_timeline/_timeline.tsx +5 -5
  70. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_gap.html.erb +4 -5
  71. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_gap.jsx +4 -4
  72. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_gap.md +1 -1
  73. data/app/pb_kits/playbook/pb_timeline/timeline.rb +6 -6
  74. data/app/pb_kits/playbook/utilities/_gap.scss +29 -0
  75. data/app/pb_kits/playbook/utilities/globalPropNames.mjs +1 -0
  76. data/app/pb_kits/playbook/utilities/globalProps.ts +18 -9
  77. data/dist/chunks/_typeahead-C2iCBqxQ.js +36 -0
  78. data/dist/chunks/_weekday_stacked-CWnbnW7m.js +45 -0
  79. data/dist/chunks/vendor.js +1 -1
  80. data/dist/menu.yml +1 -1
  81. data/dist/playbook-doc.js +1 -1
  82. data/dist/playbook-rails-react-bindings.js +1 -1
  83. data/dist/playbook-rails.js +1 -1
  84. data/dist/playbook.css +1 -1
  85. data/lib/playbook/classnames.rb +1 -0
  86. data/lib/playbook/spacing.rb +21 -0
  87. data/lib/playbook/version.rb +1 -1
  88. metadata +21 -6
  89. data/dist/chunks/_typeahead-aym7Ky_O.js +0 -22
  90. data/dist/chunks/_weekday_stacked-BZj1pop-.js +0 -45
  91. /data/app/pb_kits/playbook/pb_skeleton_loading/docs/{_skeleton_loading_border_radius.md → _skeleton_loading_border_radius_react.md} +0 -0
  92. /data/app/pb_kits/playbook/pb_skeleton_loading/docs/{_skeleton_loading_height_width.md → _skeleton_loading_height_width_react.md} +0 -0
@@ -0,0 +1,63 @@
1
+ <%= pb_rails("button", props: { id: "toggle-user-button", margin_bottom: "md", text: "Show User", variant: "secondary" }) %>
2
+
3
+ <div id="skeleton-loading-user-content">
4
+ <%= pb_rails("flex", props: { align_items: "center" }) do %>
5
+ <%= pb_rails("skeleton_loading", props: { border_radius: "rounded", height: "38px", padding_right: "sm", width: "38px"}) %>
6
+ <%= pb_rails("skeleton_loading", props: { gap: "xxs", height: "18px", stack: 2, width: "161px"}) %>
7
+ <% end %>
8
+ <%= pb_rails("flex", props: { align_items: "start", padding_top: "md" }) do %>
9
+ <%= pb_rails("flex", props: { align_items: "center", flex_direction: "column" }) do %>
10
+ <%= pb_rails("skeleton_loading", props: { border_radius: "rounded", height: "100px", padding_bottom: "xs", width: "100px"}) %>
11
+ <%= pb_rails("skeleton_loading", props: { height: "32px", padding_bottom: "xxs", width: "144px"}) %>
12
+ <%= pb_rails("skeleton_loading", props: { height: "21px", width: "164px"}) %>
13
+ <% end %>
14
+ <% end %>
15
+ </div>
16
+
17
+ <div id="user-content" style="display: none;">
18
+ <div>
19
+ <%= pb_rails("user", props: {
20
+ name: "Anna Black",
21
+ title: "Remodeling Consultant",
22
+ orientation: "horizontal",
23
+ align: "left",
24
+ avatar_url: "https://randomuser.me/api/portraits/women/44.jpg"
25
+ }) %>
26
+ </div>
27
+ <div>
28
+ <%= pb_rails("flex", props: { align_items: "start", padding_top: "md" }) do %>
29
+ <%= pb_rails("user", props: {
30
+ name: "Anna Black",
31
+ title: "Remodeling Consultant",
32
+ orientation: "vertical",
33
+ align: "center",
34
+ size: "lg",
35
+ avatar_url: "https://randomuser.me/api/portraits/women/44.jpg"
36
+ }) %>
37
+ <% end %>
38
+ </div>
39
+ </div>
40
+
41
+ <script>
42
+ document.addEventListener("DOMContentLoaded", function() {
43
+ const toggleButton = document.getElementById('toggle-user-button')
44
+ const skeletonUserContentDiv = document.getElementById('skeleton-loading-user-content')
45
+ const userContentDiv = document.getElementById('user-content')
46
+
47
+ let isLoading = true
48
+
49
+ toggleButton.addEventListener('click', function() {
50
+ isLoading = !isLoading
51
+
52
+ if (isLoading) {
53
+ skeletonUserContentDiv.style.display = 'block'
54
+ userContentDiv.style.display = 'none'
55
+ toggleButton.textContent = 'Show User'
56
+ } else {
57
+ skeletonUserContentDiv.style.display = 'none'
58
+ userContentDiv.style.display = 'block'
59
+ toggleButton.textContent = 'Show Skeleton Loading'
60
+ }
61
+ })
62
+ })
63
+ </script>
@@ -1,11 +1,19 @@
1
- import React from 'react';
2
- import { Flex, SkeletonLoading, User } from "playbook-ui";
1
+ import React, { useState } from 'react';
2
+ import { Button, Flex, SkeletonLoading, User } from "playbook-ui";
3
3
 
4
4
  const SkeletonLoadingUser = (props) => {
5
- const isLoading = true
5
+ const [isLoading, setIsLoading] = useState(true)
6
+ const toggleLoading = () => setIsLoading((prev) => !prev)
6
7
 
7
8
  return (
8
9
  <div>
10
+ <Button
11
+ marginBottom="md"
12
+ onClick={toggleLoading}
13
+ variant="secondary"
14
+ >
15
+ {isLoading ? "Show User" : "Show Skeleton Loading"}
16
+ </Button>
9
17
  <div>
10
18
  {isLoading ? (
11
19
  <Flex alignItems="center">
@@ -1,8 +1,13 @@
1
1
  examples:
2
2
 
3
3
  rails:
4
- # - skeleton_loading_default: Default
5
-
4
+ - skeleton_loading_default: Default
5
+ - skeleton_loading_color: Color
6
+ - skeleton_loading_layout: Layout
7
+ - skeleton_loading_border_radius: Border Radius
8
+ - skeleton_loading_height_width: Height & Width
9
+ - skeleton_loading_user: User Component Example
10
+ - skeleton_loading_filter: Filter Component Example
6
11
 
7
12
  react:
8
13
  - skeleton_loading_default: Default
@@ -1,12 +1,8 @@
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 %>
1
+ <%= pb_content_tag do %>
2
+ <% stack.times do |index| %>
3
+ <div
4
+ class="<%= item_classname(index) %>"
5
+ style="<%= item_inline_styles %>"
6
+ ></div>
7
+ <% end %>
8
+ <% end %>
@@ -2,7 +2,54 @@
2
2
 
3
3
  module Playbook
4
4
  module PbSkeletonLoading
5
- class SkeletonLoading < ::Playbook::KitBase
5
+ class SkeletonLoading < Playbook::KitBase
6
+ prop :height, type: Playbook::Props::String,
7
+ default: "16px"
8
+ prop :width, type: Playbook::Props::String,
9
+ default: "100%"
10
+ prop :border_radius, type: Playbook::Props::Enum,
11
+ values: %w[none xs sm md lg xl rounded],
12
+ default: "sm"
13
+ prop :gap, type: Playbook::Props::Enum,
14
+ values: %w[none xxs xs sm md lg xl xxl],
15
+ default: "xxs"
16
+ prop :stack, type: Playbook::Props::Number,
17
+ default: 1
18
+ prop :color, type: Playbook::Props::Enum,
19
+ values: %w[default white],
20
+ default: "default"
21
+ prop :dark, type: Playbook::Props::Boolean,
22
+ default: false
23
+
24
+ def classname
25
+ generate_classname("pb_skeleton_loading")
26
+ end
27
+
28
+ def global_inline_props
29
+ {}
30
+ end
31
+
32
+ def item_classname(index = nil)
33
+ classes = [
34
+ "pb_skeleton_loading_item",
35
+ "border_radius_#{border_radius}",
36
+ "color_#{color}",
37
+ ("dark" if dark),
38
+ gap_class(index),
39
+ ]
40
+ classes.compact.join(" ")
41
+ end
42
+
43
+ def item_inline_styles
44
+ styles = []
45
+ styles << "height: #{height}"
46
+ styles << "width: #{width}"
47
+ styles.join("; ")
48
+ end
49
+
50
+ def gap_class(index = nil)
51
+ stack > 1 && index.to_i.positive? && gap != "none" ? "gap_#{gap}" : nil
52
+ end
6
53
  end
7
54
  end
8
55
  end
@@ -69,6 +69,7 @@ const Table = (props: TableProps): React.ReactElement => {
69
69
  const outerPaddingCss = outerPadding ? `outer_padding_${spaceCssName}${outerPadding}` : ''
70
70
  const isTableTag = tag === 'table'
71
71
  const dynamicInlineProps = globalInlineProps(props)
72
+ const stickyRightColumnReversed = stickyRightColumn.reverse()
72
73
 
73
74
  const classNames = classnames(
74
75
  'pb_table',
@@ -148,7 +149,7 @@ const Table = (props: TableProps): React.ReactElement => {
148
149
  if (!stickyRightColumn.length) return;
149
150
  let accumulatedWidth = 0;
150
151
 
151
- stickyRightColumn.reverse().forEach((colId, index) => {
152
+ stickyRightColumnReversed.forEach((colId, index) => {
152
153
  const isLastColumn = index === stickyRightColumn.length - 1;
153
154
  const header = document.querySelector(`th[id="${colId}"]`);
154
155
  const cells = document.querySelectorAll(`td[id="${colId}"]`);
@@ -186,6 +187,12 @@ const Table = (props: TableProps): React.ReactElement => {
186
187
  setTimeout(() => {
187
188
  handleStickyRightColumns();
188
189
  }, 10);
190
+
191
+ window.addEventListener('resize', handleStickyRightColumns);
192
+
193
+ return () => {
194
+ window.removeEventListener('resize', handleStickyRightColumns);
195
+ };
189
196
  }, [stickyRightColumn]);
190
197
 
191
198
  useEffect(() => {
@@ -35,6 +35,8 @@ examples:
35
35
  - table_lg: Large
36
36
  - table_sticky: Sticky Header
37
37
  - table_sticky_left_columns: Sticky Left Column
38
+ - table_sticky_right_columns: Sticky Right Column
39
+ - table_sticky_columns: Sticky Left and Right Columns
38
40
  - table_alignment_row: Row Alignment
39
41
  - table_alignment_column: Cell Alignment
40
42
  - table_alignment_shift_row: Row Shift
@@ -26,6 +26,8 @@ export { default as TableWithSubcomponents } from './_table_with_subcomponents.j
26
26
  export { default as TableWithSubcomponentsAsDivs } from './_table_with_subcomponents_as_divs.jsx'
27
27
  export { default as TableOuterPadding } from './_table_outer_padding.jsx'
28
28
  export { default as TableStickyLeftColumns } from './_table_sticky_left_columns.jsx'
29
+ export { default as TableStickyRightColumns } from './_table_sticky_right_columns.jsx'
30
+ export { default as TableStickyColumns } from './_table_sticky_columns.jsx'
29
31
  export { default as TableWithCollapsible } from './_table_with_collapsible.jsx'
30
32
  export { default as TableWithCollapsibleWithCustomContent } from './_table_with_collapsible_with_custom_content.jsx'
31
33
  export { default as TableWithCollapsibleWithNestedTable } from './_table_with_collapsible_with_nested_table.jsx'
@@ -4,6 +4,26 @@
4
4
  display: block;
5
5
  overflow-x: scroll;
6
6
 
7
+ // hides duplicate scroll bar for those that see two (byproduct of repeated table-responsive-scroll class
8
+ // hides outer scroll bar in chrome and safari
9
+ &:not(.table-responsive-scroll .table-responsive-scroll) {
10
+ &::-webkit-scrollbar {
11
+ height: 0px;
12
+ }
13
+ }
14
+ // hides outer scroll bar in firefox
15
+ @supports (scrollbar-width: none) {
16
+ scrollbar-width: none;
17
+ .pb_table {
18
+ overflow-x: auto;
19
+ scrollbar-width: auto;
20
+ }
21
+ }
22
+ // ensures visible inner scroll bar can scroll
23
+ .pb_table {
24
+ overflow-x: auto;
25
+ }
26
+
7
27
  // Responsive Styles
8
28
  @media (max-width: 1600px) {
9
29
  &[class*="table-responsive-scroll"] {
@@ -123,6 +123,13 @@ const TextInput = (props: TextInputProps, ref: React.LegacyRef<HTMLInputElement>
123
123
 
124
124
  const childInput = children ? children.type === "input" : undefined
125
125
 
126
+ let formattedValue;
127
+ if (isMaskedInput && value) {
128
+ formattedValue = INPUTMASKS[mask].formatDefaultValue(value.toString())
129
+ } else {
130
+ formattedValue = value
131
+ }
132
+
126
133
  const textInput = (
127
134
  childInput ? React.cloneElement(children, { className: "text_input" }) :
128
135
  (<input
@@ -138,7 +145,7 @@ const TextInput = (props: TextInputProps, ref: React.LegacyRef<HTMLInputElement>
138
145
  ref={ref}
139
146
  required={required}
140
147
  type={type}
141
- value={value}
148
+ value={formattedValue}
142
149
  />)
143
150
  )
144
151
 
@@ -16,6 +16,8 @@ const TextInputMask = (props) => {
16
16
  zipCode: '',
17
17
  postalCode: '',
18
18
  ssn: '',
19
+ creditCard: '',
20
+ cvv: ''
19
21
  })
20
22
 
21
23
  const handleOnChangeFormField = ({ target }) => {
@@ -57,6 +59,22 @@ const TextInputMask = (props) => {
57
59
  value={formFields.ssn}
58
60
  {...props}
59
61
  />
62
+ <TextInput
63
+ label="Credit Card"
64
+ mask="creditCard"
65
+ name="creditCard"
66
+ onChange={handleOnChangeFormField}
67
+ value={formFields.creditCard}
68
+ {...props}
69
+ />
70
+ <TextInput
71
+ label="CVV"
72
+ mask="cvv"
73
+ name="cvv"
74
+ onChange={handleOnChangeFormField}
75
+ value={formFields.cvv}
76
+ {...props}
77
+ />
60
78
 
61
79
  <br />
62
80
  <br />
@@ -1,11 +1,30 @@
1
1
  type InputMask = {
2
2
  format: (value: string) => string
3
+ formatDefaultValue: (value: string) => string
3
4
  pattern: string
4
5
  placeholder: string
5
6
  }
6
7
 
7
8
  type InputMaskDictionary = {
8
- [key in 'currency' | 'zipCode' | 'postalCode' | 'ssn']: InputMask
9
+ [key in 'currency' | 'zipCode' | 'postalCode' | 'ssn' | 'creditCard' | 'cvv']: InputMask
10
+ }
11
+
12
+ const formatCurrencyDefaultValue = (value: string): string => {
13
+ // Remove non-numeric characters except for the decimal point
14
+ const numericValue = value.replace(/[^0-9.]/g, '')
15
+
16
+ if (!numericValue) return ''
17
+
18
+ // Parse the numeric value as a float to handle decimals
19
+ const dollars = parseFloat(numericValue)
20
+ if (isNaN(dollars) || dollars === 0) return ''
21
+
22
+ // Format as currency
23
+ return new Intl.NumberFormat('en-US', {
24
+ style: 'currency',
25
+ currency: 'USD',
26
+ maximumFractionDigits: 2,
27
+ }).format(dollars)
9
28
  }
10
29
 
11
30
  const formatCurrency = (value: string): string => {
@@ -39,26 +58,51 @@ const formatSSN = (value: string): string => {
39
58
  .replace(/(\d{3})(?=\d)/, '$1-')
40
59
  }
41
60
 
61
+ const formatCreditCard = (value: string): string => {
62
+ const cleaned = value.replace(/\D/g, '').slice(0, 16)
63
+ return cleaned.replace(/(\d{4})(?=\d)/g, '$1 ')
64
+ }
65
+
66
+ const formatCVV = (value: string): string => {
67
+ return value.replace(/\D/g, '').slice(0, 4)
68
+ }
69
+
42
70
  export const INPUTMASKS: InputMaskDictionary = {
43
71
  currency: {
44
72
  format: formatCurrency,
73
+ formatDefaultValue: formatCurrencyDefaultValue,
45
74
  // eslint-disable-next-line no-useless-escape
46
75
  pattern: '^\\$\\d{1,3}(?:,\\d{3})*(?:\\.\\d{2})?$',
47
76
  placeholder: '$0.00',
48
77
  },
49
78
  zipCode: {
50
79
  format: formatBasicPostal,
80
+ formatDefaultValue: formatBasicPostal,
51
81
  pattern: '\\d{5}',
52
82
  placeholder: '12345',
53
83
  },
54
84
  postalCode: {
55
85
  format: formatExtendedPostal,
86
+ formatDefaultValue: formatExtendedPostal,
56
87
  pattern: '\\d{5}-\\d{4}',
57
88
  placeholder: '12345-6789',
58
89
  },
59
90
  ssn: {
60
91
  format: formatSSN,
92
+ formatDefaultValue: formatSSN,
61
93
  pattern: '\\d{3}-\\d{2}-\\d{4}',
62
94
  placeholder: '123-45-6789',
63
95
  },
96
+ creditCard: {
97
+ format: formatCreditCard,
98
+ formatDefaultValue: formatCreditCard,
99
+ pattern: '\\d{4} \\d{4} \\d{4} \\d{4}',
100
+ placeholder: '1234 5678 9012 3456',
101
+ },
102
+ cvv: {
103
+ format: formatCVV,
104
+ formatDefaultValue: formatCVV,
105
+ pattern: '\\d{3,4}',
106
+ placeholder: '123',
107
+ },
64
108
  }
@@ -226,3 +226,83 @@ test('returns masked ssn value', () => {
226
226
 
227
227
  expect(input.value).toBe('123-45-6789')
228
228
  })
229
+
230
+ const TextInputCreditCardMask = (props) => {
231
+ const [creditCard, setValue] = useState('')
232
+ const handleOnChange = ({ target }) => {
233
+ setValue(target.value)
234
+ }
235
+
236
+ return (
237
+ <TextInput
238
+ mask="creditCard"
239
+ onChange={handleOnChange}
240
+ value={creditCard}
241
+ {...props}
242
+ />
243
+ )
244
+ }
245
+
246
+ test('returns masked credit card value', () => {
247
+ render(
248
+ <TextInputCreditCardMask
249
+ data={{ testid: testId }}
250
+ />
251
+ )
252
+
253
+ const kit = screen.getByTestId(testId)
254
+
255
+ const input = within(kit).getByRole('textbox')
256
+
257
+ fireEvent.change(input, { target: { value: '1234567890123456' } })
258
+
259
+ expect(input.value).toBe('1234 5678 9012 3456')
260
+
261
+ fireEvent.change(input, { target: { value: '1234' } })
262
+
263
+ expect(input.value).toBe('1234')
264
+
265
+ fireEvent.change(input, { target: { value: '' } })
266
+
267
+ expect(input.value).toBe('')
268
+ })
269
+
270
+ const TextInputCVVMask = (props) => {
271
+ const [cvv, setValue] = useState('')
272
+ const handleOnChange = ({ target }) => {
273
+ setValue(target.value)
274
+ }
275
+
276
+ return (
277
+ <TextInput
278
+ mask="cvv"
279
+ onChange={handleOnChange}
280
+ value={cvv}
281
+ {...props}
282
+ />
283
+ )
284
+ }
285
+
286
+ test('returns masked CVV value', () => {
287
+ render(
288
+ <TextInputCVVMask
289
+ data={{ testid: testId }}
290
+ />
291
+ )
292
+
293
+ const kit = screen.getByTestId(testId)
294
+
295
+ const input = within(kit).getByRole('textbox')
296
+
297
+ fireEvent.change(input, { target: { value: '1234' } })
298
+
299
+ expect(input.value).toBe('1234')
300
+
301
+ fireEvent.change(input, { target: { value: '123' } })
302
+
303
+ expect(input.value).toBe('123')
304
+
305
+ fireEvent.change(input, { target: { value: '' } })
306
+
307
+ expect(input.value).toBe('')
308
+ })
@@ -20,7 +20,7 @@ type TimelineProps = {
20
20
  id?: string,
21
21
  orientation?: string,
22
22
  showDate?: boolean,
23
- gap?: 'xs' | 'sm' | 'md' | 'lg' | 'none',
23
+ itemGap?: 'xs' | 'sm' | 'md' | 'lg' | 'none',
24
24
  } & GlobalProps
25
25
 
26
26
  const Timeline = ({
@@ -32,15 +32,15 @@ const Timeline = ({
32
32
  id,
33
33
  orientation = 'horizontal',
34
34
  showDate = false,
35
- gap = 'none',
35
+ itemGap = 'none',
36
36
  ...props
37
37
  }: TimelineProps): React.ReactElement => {
38
38
  const ariaProps = buildAriaProps(aria)
39
39
  const dataProps = buildDataProps(data)
40
40
  const htmlProps = buildHtmlProps(htmlOptions)
41
41
  const dateStyle = showDate === true ? '_with_date' : ''
42
- const gapStyle = gap === 'none' ? '' : `gap_${gap}`
43
- const timelineCss = buildCss('pb_timeline_kit', `${orientation}`, dateStyle, gapStyle)
42
+ const itemGapStyle = itemGap === 'none' ? '' : `gap_${itemGap}`
43
+ const timelineCss = buildCss('pb_timeline_kit', `${orientation}`, dateStyle, itemGapStyle)
44
44
 
45
45
  return (
46
46
  <div
@@ -60,4 +60,4 @@ Timeline.Step = TimelineStep
60
60
  Timeline.Label = TimelineLabel
61
61
  Timeline.Detail = TimelineDetail
62
62
 
63
- export default Timeline
63
+ export default Timeline
@@ -1,6 +1,6 @@
1
1
  <%= pb_rails("flex", props: { justify: "evenly" }) do %>
2
2
  <%= pb_rails("flex/flex_item") do %>
3
- <%= pb_rails("timeline", props: {orientation: "vertical", gap: "xs"}) do %>
3
+ <%= pb_rails("timeline", props: {orientation: "vertical", item_gap: "xs"}) do %>
4
4
  <%= pb_rails("timeline/item", props: {icon: "user", icon_color: "royal"}) do %>
5
5
  <%= pb_rails("body", props: {
6
6
  text: "Conversation started",
@@ -23,7 +23,7 @@
23
23
  <% end %>
24
24
 
25
25
  <%= pb_rails("flex/flex_item") do %>
26
- <%= pb_rails("timeline", props: {orientation: "vertical", gap: "sm"}) do %>
26
+ <%= pb_rails("timeline", props: {orientation: "vertical", item_gap: "sm"}) do %>
27
27
  <%= pb_rails("timeline/item", props: {icon: "user", icon_color: "royal"}) do %>
28
28
  <%= pb_rails("body", props: {
29
29
  text: "Conversation started",
@@ -46,7 +46,7 @@
46
46
  <% end %>
47
47
 
48
48
  <%= pb_rails("flex/flex_item") do %>
49
- <%= pb_rails("timeline", props: {orientation: "vertical", gap: "md"}) do %>
49
+ <%= pb_rails("timeline", props: {orientation: "vertical", item_gap: "md"}) do %>
50
50
  <%= pb_rails("timeline/item", props: {icon: "user", icon_color: "royal"}) do %>
51
51
  <%= pb_rails("body", props: {
52
52
  text: "Conversation started",
@@ -69,7 +69,7 @@
69
69
  <% end %>
70
70
 
71
71
  <%= pb_rails("flex/flex_item") do %>
72
- <%= pb_rails("timeline", props: {orientation: "vertical", gap: "lg"}) do %>
72
+ <%= pb_rails("timeline", props: {orientation: "vertical", item_gap: "lg"}) do %>
73
73
  <%= pb_rails("timeline/item", props: {icon: "user", icon_color: "royal"}) do %>
74
74
  <%= pb_rails("body", props: {
75
75
  text: "Conversation started",
@@ -91,4 +91,3 @@
91
91
  <% end %>
92
92
  <% end %>
93
93
  <% end %>
94
-
@@ -10,7 +10,7 @@ const TimelineWithGap = (props) => (
10
10
  <Flex justify="evenly">
11
11
  <FlexItem>
12
12
  <Timeline
13
- gap="xs"
13
+ itemGap="xs"
14
14
  orientation="vertical"
15
15
  >
16
16
  <Timeline.Item
@@ -51,7 +51,7 @@ const TimelineWithGap = (props) => (
51
51
  </FlexItem>
52
52
  <FlexItem>
53
53
  <Timeline
54
- gap="sm"
54
+ itemGap="sm"
55
55
  orientation="vertical"
56
56
  >
57
57
  <Timeline.Item
@@ -92,7 +92,7 @@ const TimelineWithGap = (props) => (
92
92
  </FlexItem>
93
93
  <FlexItem>
94
94
  <Timeline
95
- gap="md"
95
+ itemGap="md"
96
96
  orientation="vertical"
97
97
  >
98
98
  <Timeline.Item
@@ -134,7 +134,7 @@ const TimelineWithGap = (props) => (
134
134
  </FlexItem>
135
135
  <FlexItem>
136
136
  <Timeline
137
- gap="lg"
137
+ itemGap="lg"
138
138
  orientation="vertical"
139
139
  >
140
140
  <Timeline.Item
@@ -1 +1 @@
1
- Use the optional `gap` prop to render the timeline kit with adjusted spacing between nodes. The `gap` prop will only work when utilized with a vertical timeline. Horizontal timelines will show no difference in spacing.
1
+ Use the optional `itemGap` prop to render the timeline kit with adjusted spacing between nodes. The `itemGap` prop will only work when utilized with a vertical timeline. Horizontal timelines will show no difference in spacing.
@@ -8,15 +8,15 @@ module Playbook
8
8
  default: "horizontal"
9
9
  prop :show_date, type: Playbook::Props::Boolean,
10
10
  default: false
11
- prop :gap, type: Playbook::Props::Enum,
12
- values: %w[xs sm md lg none],
13
- default: "none"
11
+ prop :item_gap, type: Playbook::Props::Enum,
12
+ values: %w[xs sm md lg none],
13
+ default: "none"
14
14
 
15
15
  def classname
16
16
  generate_classname("pb_timeline_kit",
17
17
  orientation,
18
18
  date_class,
19
- gap_class)
19
+ item_gap_class)
20
20
  end
21
21
 
22
22
  private
@@ -25,8 +25,8 @@ module Playbook
25
25
  show_date ? "with_date" : nil
26
26
  end
27
27
 
28
- def gap_class
29
- gap == "none" ? nil : "gap_#{gap}"
28
+ def item_gap_class
29
+ item_gap == "none" ? nil : "gap_#{item_gap}"
30
30
  end
31
31
  end
32
32
  end
@@ -0,0 +1,29 @@
1
+ @import "spacing";
2
+
3
+ .gap_none {
4
+ gap: 0;
5
+ }
6
+
7
+ .gap_xxs {
8
+ gap: $space_xxs;
9
+ }
10
+
11
+ .gap_xs {
12
+ gap: $space_xs;
13
+ }
14
+
15
+ .gap_sm {
16
+ gap: $space_sm;
17
+ }
18
+
19
+ .gap_md {
20
+ gap: $space_md;
21
+ }
22
+
23
+ .gap_lg {
24
+ gap: $space_lg;
25
+ }
26
+
27
+ .gap_xl {
28
+ gap: $space_xl;
29
+ }
@@ -27,6 +27,7 @@ export default [
27
27
  "overflow",
28
28
  "order",
29
29
  "numberSpacing",
30
+ "gap",
30
31
  "maxWidth",
31
32
  "minWidth",
32
33
  "width",