playbook_ui 16.1.0.pre.alpha.play264213818 → 16.1.0.pre.alpha.play277814027

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 (92) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/Components/RegularTableView.tsx +12 -2
  3. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +33 -0
  4. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_background_custom.jsx +71 -0
  5. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_background_custom.md +4 -0
  6. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +1 -0
  7. data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +2 -1
  8. data/app/pb_kits/playbook/pb_card/docs/_card_light.html.erb +3 -35
  9. data/app/pb_kits/playbook/pb_date_picker/_date_picker.tsx +14 -5
  10. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_default.md +1 -0
  11. data/app/pb_kits/playbook/pb_dialog/_dialog.scss +8 -6
  12. data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +6 -0
  13. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +37 -2
  14. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_blank_selection_rails.md +3 -0
  15. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_blank_selection_react.md +3 -0
  16. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.html.erb +52 -0
  17. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.jsx +72 -0
  18. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.md +5 -0
  19. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height.jsx +33 -0
  20. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height_rails.html.erb +20 -0
  21. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height_rails.md +8 -0
  22. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height_react.md +8 -0
  23. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.html.erb +9 -0
  24. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.jsx +33 -0
  25. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.md +3 -0
  26. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +6 -0
  27. data/app/pb_kits/playbook/pb_dropdown/docs/index.js +4 -1
  28. data/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +2 -2
  29. data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +6 -0
  30. data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +94 -0
  31. data/app/pb_kits/playbook/pb_dropdown/dropdown_container.rb +5 -1
  32. data/app/pb_kits/playbook/pb_dropdown/index.js +59 -4
  33. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownContainer.tsx +3 -0
  34. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +2 -1
  35. data/app/pb_kits/playbook/pb_filter/Filter/SortMenu.tsx +1 -1
  36. data/app/pb_kits/playbook/pb_filter/docs/_filter_default.html.erb +2 -2
  37. data/app/pb_kits/playbook/pb_filter/docs/_filter_default.jsx +16 -9
  38. data/app/pb_kits/playbook/pb_filter/filter.rb +2 -2
  39. data/app/pb_kits/playbook/pb_form/docs/_form_with_required_indicator.html.erb +2 -0
  40. data/app/pb_kits/playbook/pb_form/pb_form_validation.js +9 -2
  41. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.html.erb +5 -5
  42. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.jsx +4 -4
  43. data/app/pb_kits/playbook/pb_form_pill/form_pill.rb +4 -0
  44. data/app/pb_kits/playbook/pb_passphrase/_passphrase.tsx +20 -5
  45. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_meter_settings.jsx +1 -0
  46. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_required_indicator.html.erb +7 -0
  47. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_required_indicator.jsx +24 -0
  48. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_required_indicator.md +3 -0
  49. data/app/pb_kits/playbook/pb_passphrase/docs/example.yml +2 -0
  50. data/app/pb_kits/playbook/pb_passphrase/docs/index.js +1 -0
  51. data/app/pb_kits/playbook/pb_passphrase/passphrase.rb +2 -0
  52. data/app/pb_kits/playbook/pb_passphrase/passphrase.test.jsx +30 -1
  53. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +3 -0
  54. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_required_indicator.html.erb +5 -0
  55. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_required_indicator.jsx +14 -0
  56. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_required_indicator.md +3 -0
  57. data/app/pb_kits/playbook/pb_phone_number_input/docs/example.yml +2 -0
  58. data/app/pb_kits/playbook/pb_phone_number_input/docs/index.js +1 -0
  59. data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.rb +3 -0
  60. data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.test.js +34 -3
  61. data/app/pb_kits/playbook/pb_textarea/_textarea.tsx +10 -0
  62. data/app/pb_kits/playbook/pb_textarea/docs/_textarea_default.html.erb +3 -3
  63. data/app/pb_kits/playbook/pb_textarea/docs/_textarea_default.jsx +3 -0
  64. data/app/pb_kits/playbook/pb_textarea/docs/_textarea_default.md +1 -0
  65. data/app/pb_kits/playbook/pb_textarea/textarea.html.erb +25 -9
  66. data/app/pb_kits/playbook/pb_textarea/textarea.rb +7 -1
  67. data/app/pb_kits/playbook/pb_time_picker/_time_picker.tsx +97 -11
  68. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_on_handler.jsx +5 -2
  69. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_required_indicator.html.erb +6 -0
  70. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_required_indicator.jsx +16 -0
  71. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_required_indicator.md +3 -0
  72. data/app/pb_kits/playbook/pb_time_picker/docs/example.yml +2 -0
  73. data/app/pb_kits/playbook/pb_time_picker/docs/index.js +1 -0
  74. data/app/pb_kits/playbook/pb_time_picker/time_picker.rb +3 -0
  75. data/app/pb_kits/playbook/pb_time_picker/time_picker.test.jsx +47 -1
  76. data/app/pb_kits/playbook/pb_typeahead/_typeahead.test.jsx +24 -1
  77. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +2 -1
  78. data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +4 -1
  79. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_truncated_text.html.erb +1 -1
  80. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_truncated_text.jsx +1 -1
  81. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +4 -0
  82. data/dist/chunks/_typeahead-CWA5wlah.js +1 -0
  83. data/dist/chunks/vendor.js +3 -3
  84. data/dist/menu.yml +2 -2
  85. data/dist/playbook-rails-react-bindings.js +1 -1
  86. data/dist/playbook-rails.js +1 -1
  87. data/dist/playbook.css +1 -1
  88. data/lib/playbook/forms/builder/phone_number_field.rb +9 -0
  89. data/lib/playbook/truncate.rb +1 -1
  90. data/lib/playbook/version.rb +1 -1
  91. metadata +28 -3
  92. data/dist/chunks/_typeahead-B9a6ZsEP.js +0 -1
@@ -0,0 +1,33 @@
1
+ import React from 'react'
2
+ import Dropdown from '../../pb_dropdown/_dropdown'
3
+
4
+ const DropdownWithPlaceholder = (props) => {
5
+
6
+ const options = [
7
+ {
8
+ label: "United States",
9
+ value: "unitedStates",
10
+ id: "us"
11
+ },
12
+ {
13
+ label: "Canada",
14
+ value: "canada",
15
+ id: "ca"
16
+ },
17
+ {
18
+ label: "Pakistan",
19
+ value: "pakistan",
20
+ id: "pk"
21
+ }
22
+ ];
23
+
24
+ return (
25
+ <Dropdown
26
+ options={options}
27
+ placeholder="Choose a country"
28
+ {...props}
29
+ />
30
+ )
31
+ }
32
+
33
+ export default DropdownWithPlaceholder
@@ -0,0 +1,3 @@
1
+ The `placeholder` prop allows you to customize the placeholder text that appears when no option is selected in the dropdown.
2
+
3
+ The placeholder prop works with all dropdown variants (`default`, `subtle`, and `quickpick`). When no option is selected, the placeholder text is displayed. When an option is selected, the placeholder is replaced by the selected option's label. The default placeholder text is "Select..." if no placeholder is provided.
@@ -21,7 +21,10 @@ examples:
21
21
  - dropdown_default_value: Default Value
22
22
  - dropdown_multi_select_with_default: Multi Select Default Value
23
23
  - dropdown_blank_selection: Blank Selection
24
+ - dropdown_with_placeholder: Placeholder
24
25
  - dropdown_separators_hidden: Separators Hidden
26
+ - dropdown_with_clearable: Clearable
27
+ - dropdown_with_constrain_height_rails: Constrain Height
25
28
  - dropdown_quickpick_rails: Quick Pick Variant
26
29
  - dropdown_quickpick_range_end_rails: Quick Pick Variant (Range Ends Today)
27
30
  - dropdown_quickpick_default_dates: Quick Pick Variant (Default Dates)
@@ -52,7 +55,10 @@ examples:
52
55
  - dropdown_default_value: Default Value
53
56
  - dropdown_multi_select_with_default: Multi Select Default Value
54
57
  - dropdown_blank_selection: Blank Selection
58
+ - dropdown_with_placeholder: Placeholder
55
59
  - dropdown_clear_selection: Clear Selection
60
+ - dropdown_with_clearable: Clearable
61
+ - dropdown_with_constrain_height: Constrain Height
56
62
  - dropdown_separators_hidden: Separators Hidden
57
63
  - dropdown_with_external_control: useDropdown Hook
58
64
  - dropdown_quickpick: Quick Pick Variant
@@ -11,6 +11,7 @@ export { default as DropdownSubcomponentStructure } from './_dropdown_subcompone
11
11
  export { default as DropdownError } from './_dropdown_error.jsx'
12
12
  export { default as DropdownDefaultValue } from './_dropdown_default_value.jsx'
13
13
  export { default as DropdownBlankSelection } from './_dropdown_blank_selection.jsx'
14
+ export { default as DropdownWithPlaceholder } from './_dropdown_with_placeholder.jsx'
14
15
  export { default as DropdownClearSelection } from './_dropdown_clear_selection.jsx'
15
16
  export { default as DropdownSubtleVariant } from './_dropdown_subtle_variant.jsx'
16
17
  export { default as DropdownSeparatorsHidden } from './_dropdown_separators_hidden.jsx'
@@ -27,4 +28,6 @@ export { default as DropdownQuickpick } from './_dropdown_quickpick.jsx'
27
28
  export { default as DropdownQuickpickRangeEnd } from './_dropdown_quickpick_range_end.jsx'
28
29
  export { default as DropdownQuickpickDefaultDates } from './_dropdown_quickpick_default_dates.jsx'
29
30
  export { default as DropdownQuickpickWithDatePickers } from './_dropdown_quickpick_with_date_pickers.jsx'
30
- export { default as DropdownQuickpickCustom } from './_dropdown_quickpick_custom.jsx'
31
+ export { default as DropdownQuickpickCustom } from './_dropdown_quickpick_custom.jsx'
32
+ export { default as DropdownWithClearable } from './_dropdown_with_clearable.jsx'
33
+ export { default as DropdownWithConstrainHeight } from './_dropdown_with_constrain_height.jsx'
@@ -18,8 +18,8 @@
18
18
  <%= content.presence %>
19
19
  <%= pb_rails("body", props: { status: "negative", text: object.error }) %>
20
20
  <% else %>
21
- <%= pb_rails("dropdown/dropdown_trigger", props:{ autocomplete: object.autocomplete, multi_select:object.multi_select }) %>
22
- <%= pb_rails("dropdown/dropdown_container", props: { searchbar: object.searchbar }) do %>
21
+ <%= pb_rails("dropdown/dropdown_trigger", props:{ autocomplete: object.autocomplete, multi_select:object.multi_select, placeholder: object.placeholder }) %>
22
+ <%= pb_rails("dropdown/dropdown_container", props: { searchbar: object.searchbar, constrain_height: object.constrain_height }) do %>
23
23
  <% if options_with_blank.present? %>
24
24
  <% options_with_blank.each do |option| %>
25
25
  <%= pb_rails("dropdown/dropdown_option", props: {option: option}) %>
@@ -36,6 +36,8 @@ module Playbook
36
36
  default: ""
37
37
  prop :controls_start_id, type: Playbook::Props::String,
38
38
  default: ""
39
+ prop :clearable, type: Playbook::Props::Boolean,
40
+ default: true
39
41
  prop :start_date_id, type: Playbook::Props::String,
40
42
  default: "start_date_id"
41
43
  prop :start_date_name, type: Playbook::Props::String,
@@ -44,12 +46,16 @@ module Playbook
44
46
  default: "end_date_id"
45
47
  prop :end_date_name, type: Playbook::Props::String,
46
48
  default: "end_date_name"
49
+ prop :placeholder, type: Playbook::Props::String
50
+ prop :constrain_height, type: Playbook::Props::Boolean,
51
+ default: false
47
52
 
48
53
  def data
49
54
  Hash(prop(:data)).merge(
50
55
  pb_dropdown: true,
51
56
  pb_dropdown_multi_select: multi_select,
52
57
  pb_dropdown_variant: variant,
58
+ pb_dropdown_clearable: clearable,
53
59
  form_pill_props: form_pill_props.to_json,
54
60
  start_date_id: variant == "quickpick" ? start_date_id : nil,
55
61
  end_date_id: variant == "quickpick" ? end_date_id : nil,
@@ -122,6 +122,80 @@ test('generated placeholder prop', () => {
122
122
 
123
123
  })
124
124
 
125
+ test('placeholder prop passed directly to Dropdown', () => {
126
+ render(
127
+ <Dropdown
128
+ data={{ testid: testId }}
129
+ options={options}
130
+ placeholder="Choose a country"
131
+ />
132
+ )
133
+
134
+ const kit = screen.getByTestId(testId)
135
+ const trigger = kit.querySelector('.pb_dropdown_trigger')
136
+ expect(trigger).toHaveTextContent('Choose a country')
137
+ })
138
+
139
+ test('placeholder works with default variant', () => {
140
+ render(
141
+ <Dropdown
142
+ data={{ testid: testId }}
143
+ options={options}
144
+ placeholder="Select an option"
145
+ variant="default"
146
+ />
147
+ )
148
+
149
+ const kit = screen.getByTestId(testId)
150
+ const trigger = kit.querySelector('.pb_dropdown_trigger')
151
+ expect(trigger).toHaveTextContent('Select an option')
152
+ })
153
+
154
+ test('placeholder works with subtle variant', () => {
155
+ render(
156
+ <Dropdown
157
+ data={{ testid: testId }}
158
+ options={options}
159
+ placeholder="Pick an option"
160
+ separators={false}
161
+ variant="subtle"
162
+ />
163
+ )
164
+
165
+ const kit = screen.getByTestId(testId)
166
+ expect(kit).toHaveClass('pb_dropdown_subtle_separators_hidden')
167
+ const trigger = kit.querySelector('.pb_dropdown_trigger')
168
+ expect(trigger).toHaveTextContent('Pick an option')
169
+ })
170
+
171
+ test('placeholder works with quickpick variant', () => {
172
+ render(
173
+ <Dropdown
174
+ data={{ testid: testId }}
175
+ placeholder="Select a date range"
176
+ variant="quickpick"
177
+ />
178
+ )
179
+
180
+ const kit = screen.getByTestId(testId)
181
+ expect(kit).toHaveClass('pb_dropdown_quickpick')
182
+ const trigger = kit.querySelector('.pb_dropdown_trigger')
183
+ expect(trigger).toHaveTextContent('Select a date range')
184
+ })
185
+
186
+ test('placeholder shows default "Select..." when not provided', () => {
187
+ render(
188
+ <Dropdown
189
+ data={{ testid: testId }}
190
+ options={options}
191
+ />
192
+ )
193
+
194
+ const kit = screen.getByTestId(testId)
195
+ const trigger = kit.querySelector('.pb_dropdown_trigger')
196
+ expect(trigger).toHaveTextContent('Select...')
197
+ })
198
+
125
199
  test('generated label prop', () => {
126
200
  render (
127
201
  <Dropdown
@@ -466,7 +540,27 @@ test("quickpick clears selection when clicking X icon", () => {
466
540
  expect(trigger).toHaveTextContent("Select...")
467
541
  })
468
542
 
543
+ test("quickpick hides clear icon when clearable is false", () => {
544
+ render(
545
+ <Dropdown
546
+ clearable={false}
547
+ data={{ testid: testId }}
548
+ defaultValue="This Week"
549
+ variant="quickpick"
550
+ />
551
+ )
552
+
553
+ const kit = screen.getByTestId(testId)
554
+ const trigger = kit.querySelector('.pb_dropdown_trigger')
555
+
556
+ expect(trigger).toHaveTextContent("This Week")
557
+
558
+ const clearIcon = kit.querySelector('[aria-label="times icon"]')
559
+ expect(clearIcon).not.toBeInTheDocument()
560
+ })
561
+
469
562
  test("quickpick returns date array values when option selected", () => {
563
+ // eslint-disable-next-line react/no-multi-comp
470
564
  const TestComponent = () => {
471
565
  const [selected, setSelected] = useState(null)
472
566
  return (
@@ -5,9 +5,13 @@ module Playbook
5
5
  class DropdownContainer < Playbook::KitBase
6
6
  prop :searchbar, type: Playbook::Props::Boolean,
7
7
  default: false
8
+ prop :constrain_height, type: Playbook::Props::Boolean,
9
+ default: false
8
10
 
9
11
  def classname
10
- generate_classname("pb_dropdown_container", "close", separator: " ")
12
+ classes = %w[pb_dropdown_container close]
13
+ classes << "constrain_height" if constrain_height
14
+ generate_classname(*classes, separator: " ")
11
15
  end
12
16
 
13
17
  def data
@@ -48,6 +48,7 @@ export default class PbDropdown extends PbEnhancedElement {
48
48
  this.updatePills();
49
49
 
50
50
  this.clearBtn = this.element.querySelector(CLEAR_ICON_SELECTOR);
51
+ this.isClearable = this.element.dataset.pbDropdownClearable !== "false";
51
52
  if (this.clearBtn) {
52
53
  this.clearBtn.style.display = "none";
53
54
  this.clearBtn.addEventListener("click", (e) => {
@@ -60,6 +61,10 @@ export default class PbDropdown extends PbEnhancedElement {
60
61
 
61
62
  updateClearButton() {
62
63
  if (!this.clearBtn) return;
64
+ if (!this.isClearable) {
65
+ this.clearBtn.style.display = "none";
66
+ return;
67
+ }
63
68
  const hasSelection = this.isMultiSelect
64
69
  ? this.selectedOptions.size > 0
65
70
  : Boolean(this.element.querySelector(DROPDOWN_INPUT).value);
@@ -109,15 +114,51 @@ export default class PbDropdown extends PbEnhancedElement {
109
114
  adjustDropdownHeight() {
110
115
  if (this.target.classList.contains("open")) {
111
116
  const el = this.target;
117
+ const shouldConstrain = el.classList.contains("constrain_height");
112
118
  el.style.height = "auto";
113
119
  requestAnimationFrame(() => {
114
- const newHeight = el.scrollHeight + "px";
115
- el.offsetHeight; // force reflow
116
- el.style.height = newHeight;
120
+ if (shouldConstrain) {
121
+ // Calculate 18em in pixels (matches SCSS max-height: 18em)
122
+ const fontSize = parseFloat(getComputedStyle(el).fontSize) || 16;
123
+ const maxHeight = fontSize * 18;
124
+ const scrollHeight = el.scrollHeight;
125
+ const newHeight = Math.min(scrollHeight, maxHeight);
126
+ el.offsetHeight; // force reflow
127
+ el.style.height = newHeight + "px";
128
+ } else {
129
+ el.offsetHeight; // force reflow
130
+ el.style.height = el.scrollHeight + "px";
131
+ }
117
132
  });
118
133
  }
119
134
  }
120
135
 
136
+ adjustDropdownPosition(container) {
137
+ if (!container) return;
138
+
139
+ const wrapper = this.element.querySelector(".dropdown_wrapper");
140
+ if (!wrapper) return;
141
+
142
+ const wrapperRect = wrapper.getBoundingClientRect();
143
+ const h = container.getBoundingClientRect().height || container.scrollHeight;
144
+ const spaceBelow = window.innerHeight - wrapperRect.bottom;
145
+ const spaceAbove = wrapperRect.top;
146
+
147
+ // If not enough space below but enough space above, position above
148
+ if (spaceBelow < h + 10 && spaceAbove >= h + 10) {
149
+ container.style.top = "auto";
150
+ container.style.bottom = "calc(100% + 5px)";
151
+ container.style.marginTop = "0";
152
+ container.style.marginBottom = "0";
153
+ } else {
154
+ // Default: position below
155
+ container.style.top = "";
156
+ container.style.bottom = "";
157
+ container.style.marginTop = "";
158
+ container.style.marginBottom = "";
159
+ }
160
+ }
161
+
121
162
  handleSearch(term = "") {
122
163
  const lcTerm = term.toLowerCase();
123
164
  let hasMatch = false
@@ -365,7 +406,21 @@ export default class PbDropdown extends PbEnhancedElement {
365
406
  showElement(elem) {
366
407
  elem.classList.remove("close");
367
408
  elem.classList.add("open");
368
- elem.style.height = elem.scrollHeight + "px";
409
+
410
+ const shouldConstrain = elem.classList.contains("constrain_height");
411
+ if (shouldConstrain) {
412
+ // Calculate height respecting max-height constraint (18em)
413
+ const fontSize = parseFloat(getComputedStyle(elem).fontSize) || 16;
414
+ const maxHeight = fontSize * 18; // matches SCSS max-height: 18em
415
+ const scrollHeight = elem.scrollHeight;
416
+ const height = Math.min(scrollHeight, maxHeight);
417
+ elem.style.height = height + "px";
418
+ } else {
419
+ elem.style.height = elem.scrollHeight + "px";
420
+ }
421
+
422
+ // Auto-position dropdown above if not enough space below
423
+ this.adjustDropdownPosition(elem);
369
424
  }
370
425
 
371
426
  hideElement(elem) {
@@ -19,6 +19,7 @@ type DropdownContainerProps = {
19
19
  aria?: { [key: string]: string };
20
20
  children?: React.ReactChild[] | React.ReactChild;
21
21
  className?: string;
22
+ constrainHeight?: boolean;
22
23
  dark?: boolean;
23
24
  data?: { [key: string]: string };
24
25
  htmlOptions?: {[key: string]: string | number | boolean | (() => void)},
@@ -31,6 +32,7 @@ const DropdownContainer = (props: DropdownContainerProps) => {
31
32
  aria = {},
32
33
  children,
33
34
  className,
35
+ constrainHeight = false,
34
36
  dark = false,
35
37
  data = {},
36
38
  htmlOptions = {},
@@ -54,6 +56,7 @@ const DropdownContainer = (props: DropdownContainerProps) => {
54
56
  const classes = classnames(
55
57
  buildCss("pb_dropdown_container"),
56
58
  `${isDropDownClosed ? "close" : "open"}`,
59
+ constrainHeight && "constrain_height",
57
60
  globalProps(props),
58
61
  className
59
62
  );
@@ -44,6 +44,7 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
44
44
 
45
45
  const {
46
46
  autocomplete,
47
+ clearable,
47
48
  filterItem,
48
49
  handleBackspace,
49
50
  handleChange,
@@ -225,7 +226,7 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
225
226
  key={`${isDropDownClosed ? "chevron-down" : "chevron-up"}`}
226
227
  >
227
228
  {
228
- selectedArray.length > 0 && (
229
+ clearable !== false && selectedArray.length > 0 && (
229
230
  <div onClick={(e)=>{e.stopPropagation();handleBackspace()}}>
230
231
  <Icon
231
232
  cursor="pointer"
@@ -21,7 +21,7 @@ const nextValue = (value: SortValue[], name: string): SortValue => {
21
21
  }
22
22
 
23
23
  const directionIcon = (dir: Direction) => (
24
- dir == 'asc' ? 'sort-amount-up' : 'sort-amount-down'
24
+ dir == 'asc' ? 'arrow-up-short-wide' : 'arrow-down-wide-short'
25
25
  )
26
26
 
27
27
  const renderOptions = (options: SortOptions, value: SortValue[], handleChange: (arg0: SortValue) => void) => (
@@ -1,7 +1,7 @@
1
1
  <%=
2
2
  pb_rails("filter", props: {
3
3
  min_width: "360px",
4
- id: "1",
4
+ id: "filter-demo-1",
5
5
  margin_bottom: "xl",
6
6
  filters: [
7
7
  { name: "name", value: "John Wick" },
@@ -44,7 +44,7 @@
44
44
  <%=
45
45
  pb_rails("filter", props: {
46
46
  min_width: "360px",
47
- id: "def2",
47
+ id: "filter-demo-2",
48
48
  sort_menu: [
49
49
  { item: "Popularity", link: "?q[sorts]=managers_popularity+asc", active: true, direction: "desc" },
50
50
  { item: "Mananger's Title", link: "?q[sorts]=managers_title+asc", active: false },
@@ -1,4 +1,4 @@
1
- import React from 'react'
1
+ import React, { useState } from 'react'
2
2
 
3
3
  import Button from '../../pb_button/_button'
4
4
  import Filter from '../../pb_filter/_filter'
@@ -6,11 +6,18 @@ import Flex from '../../pb_flex/_flex'
6
6
  import Select from '../../pb_select/_select'
7
7
  import TextInput from '../../pb_text_input/_text_input'
8
8
 
9
- const SortingChangeCallback = (sortOptions) => {
10
- alert(JSON.stringify(sortOptions[0]))
11
- }
12
-
13
9
  const FilterDefault = (props) => {
10
+ const [sortValue, setSortValue] = useState([{ name: 'popularity', dir: 'desc' }])
11
+ const [sortValue2, setSortValue2] = useState([{ name: 'popularity', dir: 'desc' }])
12
+
13
+ const handleSortChange = (sortOptions) => {
14
+ setSortValue(sortOptions)
15
+ alert(JSON.stringify(sortOptions[0]))
16
+ }
17
+
18
+ const handleSortChange2 = (sortOptions) => {
19
+ setSortValue2(sortOptions)
20
+ }
14
21
  const options = [
15
22
  { value: 'USA' },
16
23
  { value: 'Canada' },
@@ -29,7 +36,7 @@ const FilterDefault = (props) => {
29
36
  }}
30
37
  marginBottom="xl"
31
38
  minWidth="375px"
32
- onSortChange={SortingChangeCallback}
39
+ onSortChange={handleSortChange}
33
40
  results={1}
34
41
  sortOptions={{
35
42
  popularity: 'Popularity',
@@ -38,7 +45,7 @@ const FilterDefault = (props) => {
38
45
  // eslint-disable-next-line
39
46
  manager_name: 'Manager\'s Name',
40
47
  }}
41
- sortValue={[{ name: 'popularity', dir: 'desc' }]}
48
+ sortValue={sortValue}
42
49
  {...props}
43
50
  >
44
51
  {({ closePopover }) => (
@@ -82,7 +89,7 @@ const FilterDefault = (props) => {
82
89
  <Filter
83
90
  double
84
91
  minWidth="375px"
85
- onSortChange={SortingChangeCallback}
92
+ onSortChange={handleSortChange2}
86
93
  results={0}
87
94
  sortOptions={{
88
95
  popularity: 'Popularity',
@@ -91,7 +98,7 @@ const FilterDefault = (props) => {
91
98
  // eslint-disable-next-line
92
99
  manager_name: 'Manager\'s Name',
93
100
  }}
94
- sortValue={[{ name: 'popularity', dir: 'desc' }]}
101
+ sortValue={sortValue2}
95
102
  {...props}
96
103
  >
97
104
  {({ closePopover }) => (
@@ -44,9 +44,9 @@ module Playbook
44
44
  def sort_icon(direction)
45
45
  case direction
46
46
  when "asc"
47
- "sort-amount-up"
47
+ "arrow-up-short-wide"
48
48
  when "desc"
49
- "sort-amount-down"
49
+ "arrow-down-wide-short"
50
50
  else
51
51
  ""
52
52
  end
@@ -8,6 +8,8 @@
8
8
  <%= form.search_field :example_search_field, props: { label: true, required: true, required_indicator: true } %>
9
9
  <%= form.password_field :example_password_field, props: { label: true, required: true, required_indicator: true } %>
10
10
  <%= form.url_field :example_url_field, props: { label: true, required: true, required_indicator: true } %>
11
+ <%= form.phone_number_field :example_phone_number_field, props: { label: true, required: true, required_indicator: true } %>
12
+ <%= form.time_picker :example_time_picker_required_indicator, props: { label: true, required: true, required_indicator: true } %>
11
13
 
12
14
  <%= form.actions do |action| %>
13
15
  <%= action.submit %>
@@ -26,6 +26,10 @@ class PbFormValidation extends PbEnhancedElement {
26
26
  const isPhoneNumberInput = field.closest('.pb_phone_number_input')
27
27
  if (isPhoneNumberInput) return
28
28
 
29
+ // Skip TimePicker inputs - they handle their own validation
30
+ const isTimePickerInput = field.closest('.pb_time_picker')
31
+ if (isTimePickerInput) return
32
+
29
33
  FIELD_EVENTS.forEach((e) => {
30
34
  field.addEventListener(e, debounce((event) => {
31
35
  this.validateFormField(event)
@@ -67,13 +71,16 @@ class PbFormValidation extends PbEnhancedElement {
67
71
 
68
72
  // Check if this is a phone number input
69
73
  const isPhoneNumberInput = kitElement.classList.contains('pb_phone_number_input')
74
+
75
+ // Check if this is a TimePicker input
76
+ const isTimePickerInput = kitElement.classList.contains('pb_time_picker')
70
77
 
71
78
  // ensure clean error message state
72
79
  this.clearError(target)
73
80
  kitElement.classList.add('error')
74
81
 
75
- // Only add error message if it's NOT a phone number input
76
- if (!isPhoneNumberInput) {
82
+ // Only add error message if it's NOT a phone number input or TimePicker input
83
+ if (!isPhoneNumberInput && !isTimePickerInput) {
77
84
  // set the error message element
78
85
  const errorMessageContainer = this.errorMessageContainer
79
86
 
@@ -15,7 +15,7 @@
15
15
  options: names,
16
16
  label: "Truncation Within Typeahead",
17
17
  pills: true,
18
- truncate: 1,
18
+ truncate: "1",
19
19
  }) %>
20
20
 
21
21
  <%= pb_rails("caption", props: { text: "Form Pill Truncation" }) %>
@@ -24,19 +24,19 @@
24
24
  name: "Princess Amelia Mignonette Grimaldi Thermopolis Renaldo",
25
25
  avatar_url: "https://randomuser.me/api/portraits/women/44.jpg",
26
26
  tabindex: 0,
27
- truncate: 1,
27
+ truncate: "1",
28
28
  id: "truncation-1"
29
29
  }) %>
30
30
  <%= pb_rails("form_pill", props: {
31
31
  icon: "badge-check",
32
32
  text: "icon and a very long tag to show truncation",
33
33
  tabindex: 0,
34
- truncate: 1,
34
+ truncate: "1",
35
35
  id: "truncation-2"
36
36
  }) %>
37
37
  <%= pb_rails("form_pill", props: {
38
38
  text: "form pill long tag no tooltip show truncation",
39
39
  tabindex: 0,
40
- truncate: 1,
40
+ truncate: "1",
41
41
  }) %>
42
- <% end %>
42
+ <% end %>
@@ -21,7 +21,7 @@ const FormPillTruncatedText = (props) => {
21
21
  isMulti
22
22
  label="Truncation Within Typeahead"
23
23
  options={names}
24
- truncate={1}
24
+ truncate={"1"}
25
25
  {...props}
26
26
  />
27
27
  <Caption text="Form Pill Truncation"/>
@@ -31,20 +31,20 @@ const FormPillTruncatedText = (props) => {
31
31
  name="Princess Amelia Mignonette Grimaldi Thermopolis Renaldo"
32
32
  onClick={() => alert('Click!')}
33
33
  tabIndex={0}
34
- truncate={1}
34
+ truncate={"1"}
35
35
  />
36
36
  <FormPill
37
37
  icon="badge-check"
38
38
  onClick={() => {alert('Click!')}}
39
39
  tabIndex={0}
40
40
  text="icon and a very long tag to show truncation"
41
- truncate={1}
41
+ truncate={"1"}
42
42
  />
43
43
  <FormPill
44
44
  onClick={() => {alert('Click!')}}
45
45
  tabIndex={0}
46
46
  text="form pill with a very long tag to show truncation"
47
- truncate={1}
47
+ truncate={"1"}
48
48
  />
49
49
  </Card>
50
50
  </>
@@ -49,6 +49,10 @@ module Playbook
49
49
  def wrapped_class
50
50
  wrapped ? "wrapped" : nil
51
51
  end
52
+
53
+ def truncate_props
54
+ nil
55
+ end
52
56
  end
53
57
  end
54
58
  end
@@ -7,6 +7,7 @@ import { globalProps } from "../utilities/globalProps"
7
7
  import Body from '../pb_body/_body'
8
8
  import Caption from '../pb_caption/_caption'
9
9
  import CircleIconButton from '../pb_circle_icon_button/_circle_icon_button'
10
+ import colors from '../tokens/exports/_colors.module.scss'
10
11
  import Flex from '../pb_flex/_flex'
11
12
  import Icon from '../pb_icon/_icon'
12
13
  import PbReactPopover from '../pb_popover/_popover'
@@ -25,6 +26,7 @@ type PassphraseProps = {
25
26
  inputProps?: GenericObject,
26
27
  label?: string,
27
28
  onChange: (inputValue: string) => void,
29
+ requiredIndicator?: boolean,
28
30
  showTipsBelow?: "always" | "xs" | "sm" | "md" | "lg" | "xl",
29
31
  tips?: Array<string>,
30
32
  uncontrolled?: boolean,
@@ -43,6 +45,7 @@ const Passphrase = (props: PassphraseProps): React.ReactElement => {
43
45
  inputProps = {},
44
46
  label = confirmation ? "Confirm Passphrase" : "Passphrase",
45
47
  onChange = () => undefined,
48
+ requiredIndicator = false,
46
49
  showTipsBelow = "always",
47
50
  tips = [],
48
51
  uncontrolled = false,
@@ -99,6 +102,7 @@ const Passphrase = (props: PassphraseProps): React.ReactElement => {
99
102
 
100
103
  const shieldIcon = getAllIcons()["shieldCheck"]
101
104
  const eyeIcon = getAllIcons()["eye"]
105
+ const hasLabel = label && label !== ""
102
106
 
103
107
  return (
104
108
  <div
@@ -109,11 +113,22 @@ const Passphrase = (props: PassphraseProps): React.ReactElement => {
109
113
  id={id}
110
114
  >
111
115
  <label>
112
- <Flex align="baseline">
113
- <Caption
114
- className="passphrase-label"
115
- text={label}
116
- />
116
+ <Flex
117
+ align="baseline"
118
+ {...(hasLabel ? { marginBottom: "xs" } : {})}
119
+ >
120
+ {hasLabel && (requiredIndicator ? (
121
+ <Caption
122
+ className="passphrase-label"
123
+ >
124
+ {label} <span style={{ color: `${colors.error}` }}>*</span>
125
+ </Caption>
126
+ ) : (
127
+ <Caption
128
+ className="passphrase-label"
129
+ text={label}
130
+ />
131
+ ))}
117
132
  {tips.length > 0 && !confirmation &&
118
133
  <PbReactPopover
119
134
  className="passphrase-tips"