playbook_ui 15.2.0.pre.alpha.toastfixes11417 → 15.2.0.pre.rc.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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableState.ts +0 -7
  3. data/app/pb_kits/playbook/pb_button/_button.scss +0 -6
  4. data/app/pb_kits/playbook/pb_button/docs/_button_loading.html.erb +3 -7
  5. data/app/pb_kits/playbook/pb_button/docs/_button_loading.jsx +0 -29
  6. data/app/pb_kits/playbook/pb_date_picker/_date_picker.scss +0 -4
  7. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_stacked_alert.html.erb +16 -16
  8. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_stacked_alert.jsx +1 -2
  9. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_status.html.erb +31 -31
  10. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_status.jsx +3 -4
  11. data/app/pb_kits/playbook/pb_draggable/_draggable.scss +0 -8
  12. data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +29 -105
  13. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/_fixed_confirmation_toast.scss +1 -0
  14. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/_fixed_confirmation_toast.tsx +3 -5
  15. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/_description.md +0 -2
  16. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/fixed_confirmation_toast.rb +1 -4
  17. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/fixed_confirmation_toast.test.js +0 -10
  18. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +1 -12
  19. data/app/pb_kits/playbook/pb_icon_circle/_icon_circle.tsx +2 -2
  20. data/app/pb_kits/playbook/pb_icon_stat_value/_icon_stat_value.scss +21 -15
  21. data/app/pb_kits/playbook/pb_icon_stat_value/_icon_stat_value.tsx +5 -6
  22. data/app/pb_kits/playbook/pb_icon_stat_value/icon_stat_value.html.erb +0 -2
  23. data/app/pb_kits/playbook/pb_icon_stat_value/icon_stat_value.rb +3 -11
  24. data/app/pb_kits/playbook/pb_icon_stat_value/icon_stat_value.test.js +8 -9
  25. data/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.scss +9 -36
  26. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +22 -105
  27. data/app/pb_kits/playbook/pb_text_input/_text_input.scss +0 -3
  28. data/app/pb_kits/playbook/pb_text_input/_text_input.tsx +6 -14
  29. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_default.html.erb +4 -8
  30. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_default.jsx +0 -5
  31. data/app/pb_kits/playbook/pb_text_input/text_input.html.erb +1 -3
  32. data/app/pb_kits/playbook/pb_text_input/text_input.rb +0 -6
  33. data/app/pb_kits/playbook/pb_timeline/_timeline.scss +233 -250
  34. data/app/pb_kits/playbook/pb_timeline/_timeline.tsx +1 -1
  35. data/app/pb_kits/playbook/pb_timeline/timeline.test.js +2 -2
  36. data/app/pb_kits/playbook/pb_typeahead/_typeahead.scss +10 -10
  37. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +1 -39
  38. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +0 -2
  39. data/app/pb_kits/playbook/tokens/_positioning.scss +0 -1
  40. data/app/pb_kits/playbook/utilities/_hover.scss +2 -2
  41. data/app/pb_kits/playbook/utilities/_positioning.scss +1 -6
  42. data/app/pb_kits/playbook/utilities/globalProps.ts +3 -5
  43. data/dist/chunks/{_line_graph-BmLd7KGj.js → _line_graph-C9stNsP3.js} +1 -1
  44. data/dist/chunks/_typeahead-D3MtsWXG.js +6 -0
  45. data/dist/chunks/{_weekday_stacked-5TWRvQol.js → _weekday_stacked-Bvc7R5vH.js} +3 -3
  46. data/dist/chunks/vendor.js +1 -1
  47. data/dist/playbook-doc.js +2 -2
  48. data/dist/playbook-rails-react-bindings.js +1 -1
  49. data/dist/playbook-rails.js +1 -1
  50. data/dist/playbook.css +1 -1
  51. data/lib/playbook/hover.rb +1 -5
  52. data/lib/playbook/pb_forms_helper.rb +6 -7
  53. data/lib/playbook/version.rb +2 -2
  54. data/lib/playbook/z_index.rb +1 -1
  55. metadata +5 -6
  56. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_default.md +0 -1
  57. data/dist/chunks/_typeahead-BhGFKFka.js +0 -6
@@ -60,10 +60,7 @@ module Playbook
60
60
  end
61
61
 
62
62
  def classname
63
- default_z_index = z_index.present? ? "" : " z_index_max"
64
- # IMPORTANT: the AutoClose class must be the last class in the string for JS to read it correctly
65
- # Changing the order will break the auto_close functionality
66
- generate_classname("pb_fixed_confirmation_toast_kit", status, multi_line_class) + close_class + position_class + icon_class + default_z_index + auto_close_class
63
+ generate_classname("pb_fixed_confirmation_toast_kit", status, multi_line_class) + close_class + position_class + auto_close_class + icon_class
67
64
  end
68
65
  end
69
66
  end
@@ -75,14 +75,4 @@ test('renders position when provided', () => {
75
75
  />
76
76
  );
77
77
  expect(container.querySelector('.positioned_toast')).toBeInTheDocument();
78
- });
79
-
80
- test('renders max zIndex by default', () => {
81
- const { container } = render(<FixedConfirmationToast />);
82
- expect(container.querySelector('.z_index_max')).toBeInTheDocument();
83
- });
84
-
85
- test('applies custom zIndex when provided', () => {
86
- const { container } = render(<FixedConfirmationToast zIndex={10} />);
87
- expect(container.querySelector('.z_index_10')).toBeInTheDocument();
88
78
  });
@@ -21,15 +21,6 @@
21
21
  ]
22
22
  %>
23
23
 
24
- <%
25
- example_typeahead_options = [
26
- { label: 'Orange', value: '#FFA500' },
27
- { label: 'Red', value: '#FF0000' },
28
- { label: 'Green', value: '#00FF00' },
29
- { label: 'Blue', value: '#0000FF' },
30
- ]
31
- %>
32
-
33
24
  <% treeData = [{
34
25
  label: "Power Home Remodeling",
35
26
  value: "Power Home Remodeling",
@@ -98,10 +89,8 @@
98
89
 
99
90
  <%= pb_form_with(scope: :example, method: :get, url: "", validate: true) do |form| %>
100
91
  <%= form.typeahead :example_typeahead_validation, props: { data: { typeahead_example2: true, user: {} }, label: true, placeholder: "Search for a user", required: true, validation: { message: "Please select a user." } } %>
101
- <%= form.typeahead :example_typeahead_validation_react, props: { options: example_typeahead_options, pills: true, label: "Example Typeahead (React Rendered)", placeholder: "Search for a user", required: true, validation: { message: "Please select a color." } } %>
102
- <%= form.typeahead :example_typeahead_validation_react_2, props: { options: example_typeahead_options, pills: true, label: "Example Typeahead 2 (React Rendered)", placeholder: "Search for a user", required: true } %>
103
92
  <%= form.text_field :example_text_field_validation, props: { label: true, required: true } %>
104
- <%= form.phone_number_field :example_phone_number_field_validation, props: { label: "Example phone field", hidden_inputs: true, required: true } %>
93
+ <%= form.phone_number_field :example_phone_number_field_validation, props: { label: "Example phone field", hidden_inputs: true } %>
105
94
  <%= form.email_field :example_email_field_validation, props: { label: true, required: true } %>
106
95
  <%= form.number_field :example_number_field_validation, props: { label: true, required: true } %>
107
96
  <%= form.search_field :example_project_number_validation, props: { label: true, required: true, validation: { pattern: "[0-9]{2}-[0-9]{5}", message: "Please enter a valid project number (example: 33-12345)." } } %>
@@ -3,7 +3,7 @@ import React from 'react'
3
3
  import classnames from 'classnames'
4
4
 
5
5
  import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../utilities/props'
6
- import { globalProps, GlobalProps } from '../utilities/globalProps'
6
+ import { globalProps } from '../utilities/globalProps'
7
7
 
8
8
  import Icon from '../pb_icon/_icon'
9
9
 
@@ -26,7 +26,7 @@ type IconCircleProps = {
26
26
  | "orange"
27
27
  | "green"
28
28
  | "lighter",
29
- } & GlobalProps
29
+ }
30
30
 
31
31
  const IconCircle = (props: IconCircleProps) => {
32
32
  const {
@@ -3,45 +3,51 @@
3
3
  @import "../tokens/spacing";
4
4
  @import "../pb_icon_circle/icon_circle";
5
5
 
6
- .pb_icon_stat_value_kit_horizontal,
7
- .pb_icon_stat_value_kit_vertical
8
- {
6
+ [class^=pb_icon_stat_value_kit]{
9
7
  display: flex;
10
8
  align-items: baseline;
11
9
 
12
- &.pb_icon_stat_value_kit_vertical {
10
+ &[class*=_vertical] {
13
11
  flex-direction: column;
14
12
 
15
- &.text_align_center {
13
+ &[class*=_center] {
16
14
  align-items: center;
17
15
 
18
- .pb_title_kit,
19
- .pb_body_kit,
20
- .pb_caption_kit_md {
16
+ [class^=pb_title],
17
+ [class^=pb_body],
18
+ [class^=pb_caption] {
21
19
  text-align: center;
22
20
  }
23
21
  }
24
22
 
25
- &.text_align_right {
23
+ &[class*=_right] {
26
24
  align-items: flex-end;
27
25
 
28
- .pb_title_kit,
29
- .pb_body_kit,
30
- .pb_caption_kit_md {
26
+ [class^=pb_title],
27
+ [class^=pb_body],
28
+ [class^=pb_caption] {
31
29
  text-align: right;
32
30
  }
33
31
  }
32
+
33
+ [class^=pb_icon_circle] {
34
+ margin-bottom: $space-xs;
35
+ }
34
36
  }
35
37
 
36
- &.pb_icon_stat_value_kit_horizontal {
38
+ &[class*=_horizontal] {
37
39
  align-items: center;
38
40
 
39
- &.text_align_center {
41
+ &[class*=_center] {
40
42
  justify-content: center;
41
43
  }
42
44
 
43
- &.text_align_right {
45
+ &[class*=_right] {
44
46
  justify-content: flex-end;
45
47
  }
48
+
49
+ [class^=pb_icon_circle] {
50
+ margin-right: $space-sm;
51
+ }
46
52
  }
47
53
  }
@@ -2,7 +2,7 @@ import React from 'react'
2
2
  import classnames from 'classnames'
3
3
 
4
4
  import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../utilities/props'
5
- import { globalProps, GlobalProps } from '../utilities/globalProps'
5
+ import { globalProps } from '../utilities/globalProps'
6
6
 
7
7
  import Body from '../pb_body/_body'
8
8
  import Caption from '../pb_caption/_caption'
@@ -33,7 +33,8 @@ type IconStatValueProps = {
33
33
  | "yellow"
34
34
  | "orange"
35
35
  | "green"
36
- } & GlobalProps
36
+ | "lighter",
37
+ }
37
38
 
38
39
  const IconStatValue = (props: IconStatValueProps): React.ReactElement => {
39
40
  const {
@@ -49,13 +50,13 @@ const IconStatValue = (props: IconStatValueProps): React.ReactElement => {
49
50
  text = '',
50
51
  unit = '',
51
52
  value = 0,
52
- variant = 'default',
53
+ variant = 'lighter',
53
54
  } = props
54
55
  const ariaProps = buildAriaProps(aria)
55
56
  const dataProps = buildDataProps(data)
56
57
  const htmlProps = buildHtmlProps(htmlOptions)
57
58
  const classes = classnames(
58
- buildCss('pb_icon_stat_value_kit', orientation), globalProps(props),
59
+ buildCss('pb_icon_stat_value_kit', orientation, size, variant), globalProps(props),
59
60
  className
60
61
  )
61
62
  const titleSize = function(size: "sm" | "md" | "lg") {
@@ -100,8 +101,6 @@ const IconStatValue = (props: IconStatValueProps): React.ReactElement => {
100
101
  <IconCircle
101
102
  dark={dark}
102
103
  icon={icon}
103
- marginBottom={orientation == 'vertical' ? 'xs' : undefined}
104
- marginRight={orientation == 'horizontal' ? 'sm' : undefined}
105
104
  size={size}
106
105
  variant={variant}
107
106
  />
@@ -3,8 +3,6 @@
3
3
  <%= pb_rails("icon_circle", props: {
4
4
  dark: object.dark,
5
5
  icon: object.icon,
6
- margin_right: object.icon_margin_right,
7
- margin_bottom: object.icon_margin_bottom,
8
6
  size: object.size,
9
7
  variant: object.variant }) %>
10
8
 
@@ -9,8 +9,8 @@ module Playbook
9
9
  values: %w[sm md lg],
10
10
  default: "sm"
11
11
  prop :variant, type: Playbook::Props::Enum,
12
- values: %w[default royal blue purple teal red yellow green orange],
13
- default: "default"
12
+ values: %w[default royal blue purple teal red yellow green orange lighter],
13
+ default: "lighter"
14
14
 
15
15
  prop :orientation, type: Playbook::Props::Enum,
16
16
  values: %w[vertical horizontal],
@@ -25,7 +25,7 @@ module Playbook
25
25
  prop :value
26
26
 
27
27
  def classname
28
- generate_classname("pb_icon_stat_value_kit", orientation)
28
+ generate_classname("pb_icon_stat_value_kit", orientation, size, variant)
29
29
  end
30
30
 
31
31
  def value_string
@@ -41,14 +41,6 @@ module Playbook
41
41
  3
42
42
  end
43
43
  end
44
-
45
- def icon_margin_right
46
- orientation === "horizontal" && "sm"
47
- end
48
-
49
- def icon_margin_bottom
50
- orientation === "vertical" && "xs"
51
- end
52
44
  end
53
45
  end
54
46
  end
@@ -18,7 +18,7 @@ describe("IconStatValue Kit", () => {
18
18
  )
19
19
 
20
20
  const kit = screen.getByTestId(testId)
21
- expect(kit).toHaveClass("pb_icon_stat_value_kit_horizontal")
21
+ expect(kit).toHaveClass("pb_icon_stat_value_kit_horizontal_sm_lighter")
22
22
  })
23
23
 
24
24
  test("renders icon", () => {
@@ -99,10 +99,9 @@ describe("IconStatValue Kit", () => {
99
99
  value={64.18}
100
100
  />
101
101
  )
102
- const size = sizeProp === "sm" ? "3" : sizeProp === "md" ? "2" : "1"
102
+
103
103
  const kit = screen.getByTestId(testId)
104
- const title = kit.querySelector(".pb_title_kit")
105
- expect(title).toHaveClass(`pb_title_${size}`)
104
+ expect(kit).toHaveClass(`pb_icon_stat_value_kit_horizontal_${sizeProp}_lighter`)
106
105
 
107
106
  cleanup()
108
107
  })
@@ -116,7 +115,8 @@ describe("IconStatValue Kit", () => {
116
115
  "teal",
117
116
  "red",
118
117
  "yellow",
119
- "green"].forEach(
118
+ "green",
119
+ "lighter"].forEach(
120
120
  (colorProp) => {
121
121
  render(
122
122
  <IconStatValue
@@ -128,10 +128,9 @@ describe("IconStatValue Kit", () => {
128
128
  variant={colorProp}
129
129
  />
130
130
  )
131
-
131
+
132
132
  const kit = screen.getByTestId(testId)
133
- const iconCircle = kit.querySelector(`.pb_icon_circle_kit_size_sm_${colorProp}`)
134
- expect(iconCircle).toBeInTheDocument()
133
+ expect(kit).toHaveClass(`pb_icon_stat_value_kit_horizontal_sm_${colorProp}`)
135
134
 
136
135
  cleanup()
137
136
  })
@@ -150,7 +149,7 @@ describe("IconStatValue Kit", () => {
150
149
  )
151
150
 
152
151
  const kit = screen.getByTestId(testId)
153
- expect(kit).toHaveClass("pb_icon_stat_value_kit_vertical")
152
+ expect(kit).toHaveClass("pb_icon_stat_value_kit_vertical_sm_lighter")
154
153
  })
155
154
 
156
155
  })
@@ -89,18 +89,7 @@ $positions: (
89
89
  }
90
90
  }
91
91
 
92
- .pb_multiple_users_stacked_kit,
93
- .pb_multiple_users_stacked_kit_single,
94
- .pb_multiple_users_stacked_kit_bubble,
95
- .pb_multiple_users_stacked_kit_single_bubble,
96
- .pb_multiple_users_stacked_kit_bubble_size_sm,
97
- .pb_multiple_users_stacked_kit_bubble_size_md,
98
- .pb_multiple_users_stacked_kit_bubble_size_lg,
99
- .pb_multiple_users_stacked_kit_bubble_size_xl,
100
- .pb_multiple_users_stacked_kit_single_bubble_size_sm,
101
- .pb_multiple_users_stacked_kit_single_bubble_size_md,
102
- .pb_multiple_users_stacked_kit_single_bubble_size_lg,
103
- .pb_multiple_users_stacked_kit_single_bubble_size_xl {
92
+ [class^=pb_multiple_users_stacked_kit] {
104
93
  $container_size: map-get($avatar-sizes, "xs");
105
94
  $bubble_container_size: map-get($avatar-sizes, "sm");
106
95
  $overlap: -15px;
@@ -114,8 +103,7 @@ $positions: (
114
103
  position: relative;
115
104
  flex-shrink: 0;
116
105
  flex-grow: 0;
117
- .pb_avatar_kit_size_xs.pb_multiple_users_stacked_item,
118
- .pb_avatar_kit_size_md.pb_multiple_users_stacked_item {
106
+ [class^=pb_avatar_kit].pb_multiple_users_stacked_item {
119
107
  @include avatar-size($stacked_size);
120
108
  &.dark {
121
109
  .avatar_wrapper {
@@ -129,17 +117,10 @@ $positions: (
129
117
  }
130
118
  }
131
119
  }
132
- &.pb_multiple_users_stacked_kit_single .pb_multiple_users_stacked_item,
133
- &.pb_multiple_users_stacked_kit_single_bubble .pb_multiple_users_stacked_item,
134
- &.pb_multiple_users_stacked_kit_single_bubble_size_sm .pb_multiple_users_stacked_item,
135
- &.pb_multiple_users_stacked_kit_single_bubble_size_md .pb_multiple_users_stacked_item,
136
- &.pb_multiple_users_stacked_kit_single_bubble_size_lg .pb_multiple_users_stacked_item,
137
- &.pb_multiple_users_stacked_kit_single_bubble_size_xl .pb_multiple_users_stacked_item {
120
+ &[class*=_single] .pb_multiple_users_stacked_item {
138
121
  @include avatar-size(28px);
139
122
  }
140
- .pb_avatar_kit_size_xs.second_item,
141
- .pb_avatar_kit_size_md.second_item,
142
- .pb_badge_kit_primary_rounded.second_item {
123
+ [class^=pb_avatar_kit].second_item, [class^=pb_badge_kit].second_item {
143
124
  @include position((bottom: 0, right: 0));
144
125
  z-index: 2;
145
126
  background: tint($primary, 90%);
@@ -162,8 +143,7 @@ $positions: (
162
143
 
163
144
  // Iterate over each size to adjust the bubble container only when class contains "_bubble_"
164
145
  @each $size_name, $size_value in $avatar-sizes {
165
- &.pb_multiple_users_stacked_kit_bubble_size_#{$size_name},
166
- &.pb_multiple_users_stacked_kit_single_bubble_size_#{$size_name} {
146
+ &[class*=_bubble_][class*=_size_#{$size_name}] {
167
147
  // Set bubble container size based on the class
168
148
  $bubble_container_size: $size_value;
169
149
  $container_size: $size_value;
@@ -181,8 +161,7 @@ $positions: (
181
161
  background-color: $card_dark;
182
162
  }
183
163
 
184
- .pb_avatar_kit_size_xs.pb_multiple_users_stacked_item,
185
- .pb_avatar_kit_size_md.pb_multiple_users_stacked_item {
164
+ [class^=pb_avatar_kit].pb_multiple_users_stacked_item {
186
165
  @include avatar-size($bubble_container_size * 0.45); // Adjust the size of stacked avatars
187
166
 
188
167
  &.dark {
@@ -196,8 +175,7 @@ $positions: (
196
175
  }
197
176
  }
198
177
 
199
- .pb_avatar_kit_size_xs,
200
- .pb_avatar_kit_size_md {
178
+ [class^=pb_avatar_kit] {
201
179
  // First Item
202
180
  &.first_item {
203
181
  @include position(map-get(map-get($positions, 'first-item-double'), $size_name));
@@ -257,13 +235,8 @@ $positions: (
257
235
  }
258
236
  }
259
237
 
260
- &.pb_multiple_users_stacked_kit_single_bubble,
261
- &.pb_multiple_users_stacked_kit_single_bubble_size_sm,
262
- &.pb_multiple_users_stacked_kit_single_bubble_size_md,
263
- &.pb_multiple_users_stacked_kit_single_bubble_size_lg,
264
- &.pb_multiple_users_stacked_kit_single_bubble_size_xl {
265
- .pb_avatar_kit_size_xs.first_item,
266
- .pb_avatar_kit_size_md.first_item {
238
+ &[class*=_single_bubble] {
239
+ [class^=pb_avatar_kit].first_item {
267
240
  @include position((top: 0, left: 0));
268
241
  @include avatar-size($bubble_container_size);
269
242
  }
@@ -111,7 +111,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
111
111
  const inputRef = useRef<HTMLInputElement | null>(null)
112
112
  const itiRef = useRef<any>(null);
113
113
  const [inputValue, setInputValue] = useState(value)
114
- const [error, setError] = useState(props.error || "")
114
+ const [error, setError] = useState(props.error)
115
115
  const [dropDownIsOpen, setDropDownIsOpen] = useState(false)
116
116
  const [selectedData, setSelectedData] = useState()
117
117
  const [hasTyped, setHasTyped] = useState(false)
@@ -124,6 +124,24 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
124
124
  }
125
125
  }, [error, onValidate])
126
126
 
127
+ /*
128
+ useImperativeHandle exposes the kit's input element to a parent component via a ref.
129
+ See the Playbook docs for use cases.
130
+ Read: https://react.dev/reference/react/useImperativeHandle
131
+ */
132
+ useImperativeHandle(ref, () => {
133
+ return {
134
+ clearField() {
135
+ setInputValue("")
136
+ setError("")
137
+ setHasTyped(false)
138
+ },
139
+ inputNode() {
140
+ return inputRef.current
141
+ }
142
+ }
143
+ })
144
+
127
145
  const unformatNumber = (formattedNumber: any) => {
128
146
  return formattedNumber.replace(/\D/g, "")
129
147
  }
@@ -146,13 +164,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
146
164
 
147
165
  const validateTooShortNumber = (itiInit: any) => {
148
166
  if (!itiInit) return
149
-
150
- // If field is empty, don't show "too short" error
151
- if (!inputValue || inputValue.trim() === '') {
152
- setError('')
153
- return false
154
- }
155
-
156
167
  if (itiInit.getValidationError() === ValidationError.TooShort) {
157
168
  return showFormattedError('too short')
158
169
  } else {
@@ -172,7 +183,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
172
183
  }
173
184
 
174
185
  const validateUnhandledError = (itiInit: any) => {
175
- if (!itiInit) return
186
+ if (!required || !itiInit) return
176
187
  if (itiInit.getValidationError() === ValidationError.SomethingWentWrong) {
177
188
  if (inputValue.length === 1) {
178
189
  return showFormattedError('too short')
@@ -195,27 +206,14 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
195
206
 
196
207
  const validateRepeatCountryCode = (itiInit: any) => {
197
208
  if (!itiInit) return
198
- const countryDialCode = itiRef.current.getSelectedCountryData().dialCode;
209
+ const countryDialCode = itiInit.getSelectedCountryData().dialCode;
199
210
  if (unformatNumber(inputValue).startsWith(countryDialCode)) {
200
211
  return showFormattedError('repeat country code')
201
212
  }
202
213
  }
203
214
 
204
- const validateRequiredField = () => {
205
- if (!inputValue || inputValue.trim() === '') {
206
- setError('Missing phone number')
207
- return true
208
- }
209
- return false
210
- }
211
215
 
212
216
  const validateErrors = () => {
213
- // If field is empty, show error message
214
- if (!inputValue || inputValue.trim() === '') {
215
- if (validateRequiredField()) return
216
- return
217
- }
218
-
219
217
  if (!hasTyped && !error) return
220
218
 
221
219
  if (itiRef.current) isValid(itiRef.current.isValidNumber())
@@ -227,87 +225,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
227
225
  if (validateRepeatCountryCode(itiRef.current)) return
228
226
  }
229
227
 
230
- /*
231
- useImperativeHandle exposes the kit's input element to a parent component via a ref.
232
- See the Playbook docs for use cases.
233
- Read: https://react.dev/reference/react/useImperativeHandle
234
- */
235
- useImperativeHandle(ref, () => {
236
- return {
237
- clearField() {
238
- setInputValue("")
239
- setError("")
240
- setHasTyped(false)
241
- },
242
- inputNode() {
243
- return inputRef.current
244
- },
245
- // Expose validation method for React Hook Form
246
- validate() {
247
- // Run validation and return error message or true
248
- const isEmpty = !inputValue || inputValue.trim() === ''
249
-
250
- if (isEmpty) {
251
- // Show missing phone number error
252
- const errorMessage = 'Missing phone number'
253
- setError(errorMessage)
254
- setHasTyped(true)
255
- // Only return error for React Hook Form if field is required
256
- return required ? errorMessage : true
257
- }
258
-
259
- if (!itiRef.current) {
260
- return true
261
- }
262
-
263
- // Check for repeat country code first
264
- const countryDialCode = itiRef.current.getSelectedCountryData().dialCode;
265
- if (unformatNumber(inputValue).startsWith(countryDialCode)) {
266
- const countryName = itiRef.current.getSelectedCountryData().name
267
- const errorMessage = `Invalid ${countryName} phone number (repeat country code)`
268
- setError(errorMessage)
269
- setHasTyped(true)
270
- return errorMessage
271
- }
272
-
273
- // Check if it only contains valid characters
274
- if (!containOnlyNumbers(inputValue)) {
275
- const countryName = itiRef.current.getSelectedCountryData().name
276
- const errorMessage = `Invalid ${countryName} phone number (enter numbers only)`
277
- setError(errorMessage)
278
- setHasTyped(true)
279
- return errorMessage
280
- }
281
-
282
- // Check if valid number
283
- if (!itiRef.current.isValidNumber()) {
284
- const countryName = itiRef.current.getSelectedCountryData().name
285
- const validationError = itiRef.current.getValidationError()
286
- let errorMessage = ''
287
-
288
- if (validationError === ValidationError.TooShort) {
289
- errorMessage = `Invalid ${countryName} phone number (too short)`
290
- } else if (validationError === ValidationError.TooLong) {
291
- errorMessage = `Invalid ${countryName} phone number (too long)`
292
- } else if (validationError === ValidationError.MissingAreaCode) {
293
- errorMessage = `Invalid ${countryName} phone number (missing area code)`
294
- } else {
295
- errorMessage = `Invalid ${countryName} phone number`
296
- }
297
-
298
- setError(errorMessage)
299
- setHasTyped(true)
300
-
301
- return errorMessage
302
- }
303
-
304
- // Clear error if valid
305
- setError('')
306
- return true
307
- }
308
- }
309
- })
310
-
311
228
  const getCurrentSelectedData = (itiInit: any, inputValue: string) => {
312
229
  return { ...itiInit.getSelectedCountryData(), number: inputValue }
313
230
  }
@@ -383,7 +300,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
383
300
  dark,
384
301
  "data-phone-number": JSON.stringify(selectedData),
385
302
  disabled,
386
- error: hasTyped ? error : props.error,
303
+ error,
387
304
  type: 'tel',
388
305
  id,
389
306
  label,
@@ -3,9 +3,6 @@
3
3
  @import "../tokens/colors";
4
4
 
5
5
  .pb_text_input_kit {
6
- label {
7
- display: block !important;
8
- }
9
6
  .pb_text_input_kit_label {
10
7
  margin-bottom: $space_xs;
11
8
  display: block;
@@ -140,14 +140,10 @@ const TextInput = (props: TextInputProps, ref: React.LegacyRef<HTMLInputElement>
140
140
  formattedValue = value
141
141
  }
142
142
 
143
- const errorId = error ? `${id}-error` : undefined
144
-
145
143
  const textInput = (
146
144
  childInput ? React.cloneElement(children, { className: "text_input" }) :
147
145
  (<input
148
146
  {...domSafeProps(props)}
149
- aria-describedby={errorId}
150
- aria-invalid={!!error}
151
147
  autoComplete={typeof autoComplete === "string" ? autoComplete : ( autoComplete ? undefined : "off" )}
152
148
  className="text_input"
153
149
  disabled={disabled}
@@ -206,20 +202,16 @@ const TextInput = (props: TextInputProps, ref: React.LegacyRef<HTMLInputElement>
206
202
  {...htmlProps}
207
203
  className={css}
208
204
  >
209
- {label && (
210
- <label htmlFor={id}>
211
- <Caption className="pb_text_input_kit_label"
212
- text={label}
213
- />
214
- </label>
215
- )}
205
+ {label &&
206
+ <Caption
207
+ className="pb_text_input_kit_label"
208
+ text={label}
209
+ />
210
+ }
216
211
  <div className={`${addOnCss} text_input_wrapper`}>
217
212
  {render}
218
213
 
219
214
  {error && <Body
220
- aria={{ atomic: "true", live: "polite" }}
221
- htmlOptions={{ role: "alert" }}
222
- id={errorId}
223
215
  status="negative"
224
216
  text={error}
225
217
  variant={null}
@@ -9,27 +9,23 @@
9
9
 
10
10
  <%= pb_rails("text_input", props: {
11
11
  label: "Last Name",
12
- placeholder: "Enter last name",
13
- id: "last-name"
12
+ placeholder: "Enter last name"
14
13
  }) %>
15
14
 
16
15
  <%= pb_rails("text_input", props: {
17
16
  label: "Phone Number",
18
17
  type: "phone",
19
- placeholder: "Enter phone number",
20
- id: "phone"
18
+ placeholder: "Enter phone number"
21
19
  }) %>
22
20
 
23
21
  <%= pb_rails("text_input", props: {
24
22
  label: "Email Address",
25
23
  type: "email",
26
- placeholder: "Enter email address",
27
- id: "email"
24
+ placeholder: "Enter email address"
28
25
  }) %>
29
26
 
30
27
  <%= pb_rails("text_input", props: {
31
28
  label: "Zip Code",
32
29
  type: "number",
33
- placeholder: "Enter zip code",
34
- id: "zip"
30
+ placeholder: "Enter zip code"
35
31
  }) %>