playbook_ui 16.1.0.pre.alpha.play264213818 → 16.1.0.pre.alpha.play274314102

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 (159) 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_checkbox/_checkbox.scss +1 -1
  10. data/app/pb_kits/playbook/pb_checkbox/_checkbox.tsx +17 -0
  11. data/app/pb_kits/playbook/pb_checkbox/checkbox.html.erb +10 -1
  12. data/app/pb_kits/playbook/pb_checkbox/checkbox.rb +2 -0
  13. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_required_indicator.html.erb +6 -0
  14. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_required_indicator.jsx +17 -0
  15. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_required_indicator.md +3 -0
  16. data/app/pb_kits/playbook/pb_checkbox/docs/example.yml +2 -0
  17. data/app/pb_kits/playbook/pb_checkbox/docs/index.js +1 -0
  18. data/app/pb_kits/playbook/pb_date_picker/_date_picker.tsx +14 -5
  19. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_default.md +1 -0
  20. data/app/pb_kits/playbook/pb_dialog/_dialog.scss +8 -6
  21. data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +6 -0
  22. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +83 -13
  23. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_blank_selection_rails.md +3 -0
  24. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_blank_selection_react.md +3 -0
  25. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.html.erb +52 -0
  26. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.jsx +72 -0
  27. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.md +5 -0
  28. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height.jsx +33 -0
  29. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height_rails.html.erb +20 -0
  30. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height_rails.md +8 -0
  31. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height_react.md +8 -0
  32. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_label.html.erb +6 -3
  33. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_label.jsx +1 -0
  34. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_label.md +3 -1
  35. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.html.erb +9 -0
  36. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.jsx +33 -0
  37. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.md +3 -0
  38. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +6 -0
  39. data/app/pb_kits/playbook/pb_dropdown/docs/index.js +4 -1
  40. data/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +11 -5
  41. data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +15 -0
  42. data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +94 -0
  43. data/app/pb_kits/playbook/pb_dropdown/dropdown_container.rb +5 -1
  44. data/app/pb_kits/playbook/pb_dropdown/dropdown_trigger.html.erb +7 -2
  45. data/app/pb_kits/playbook/pb_dropdown/dropdown_trigger.rb +4 -0
  46. data/app/pb_kits/playbook/pb_dropdown/index.js +184 -77
  47. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownContainer.tsx +3 -0
  48. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +18 -1
  49. data/app/pb_kits/playbook/pb_dropdown/utilities/clickOutsideHelper.tsx +6 -0
  50. data/app/pb_kits/playbook/pb_filter/Filter/SortMenu.tsx +1 -1
  51. data/app/pb_kits/playbook/pb_filter/docs/_filter_default.html.erb +2 -2
  52. data/app/pb_kits/playbook/pb_filter/docs/_filter_default.jsx +16 -9
  53. data/app/pb_kits/playbook/pb_filter/filter.rb +2 -2
  54. data/app/pb_kits/playbook/pb_form/docs/_form_with_required_indicator.html.erb +6 -2
  55. data/app/pb_kits/playbook/pb_form/pb_form_validation.js +9 -2
  56. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.html.erb +5 -5
  57. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.jsx +4 -4
  58. data/app/pb_kits/playbook/pb_form_pill/form_pill.rb +4 -0
  59. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.scss +7 -0
  60. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +638 -549
  61. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.html.erb +3 -3
  62. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.jsx +4 -7
  63. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.md +3 -0
  64. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.test.jsx +4 -4
  65. data/app/pb_kits/playbook/pb_passphrase/_passphrase.tsx +40 -7
  66. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_meter_settings.jsx +1 -0
  67. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_required_indicator.html.erb +7 -0
  68. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_required_indicator.jsx +24 -0
  69. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_required_indicator.md +3 -0
  70. data/app/pb_kits/playbook/pb_passphrase/docs/example.yml +2 -0
  71. data/app/pb_kits/playbook/pb_passphrase/docs/index.js +1 -0
  72. data/app/pb_kits/playbook/pb_passphrase/passphrase.rb +2 -0
  73. data/app/pb_kits/playbook/pb_passphrase/passphrase.test.jsx +30 -1
  74. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +3 -0
  75. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_required_indicator.html.erb +5 -0
  76. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_required_indicator.jsx +14 -0
  77. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_required_indicator.md +3 -0
  78. data/app/pb_kits/playbook/pb_phone_number_input/docs/example.yml +2 -0
  79. data/app/pb_kits/playbook/pb_phone_number_input/docs/index.js +1 -0
  80. data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.rb +3 -0
  81. data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.test.js +34 -3
  82. data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.tsx +71 -34
  83. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_label.jsx +44 -0
  84. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_label.md +1 -0
  85. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_required_indicator.jsx +1 -0
  86. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_attributes.jsx +4 -0
  87. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_default.jsx +4 -0
  88. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_focus.jsx +5 -0
  89. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_inline.jsx +4 -0
  90. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_label.jsx +33 -0
  91. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_label.md +1 -0
  92. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_preview.jsx +4 -0
  93. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_required_indicator.jsx +5 -0
  94. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_simple.jsx +4 -0
  95. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_sticky.jsx +4 -0
  96. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_templates.jsx +4 -0
  97. data/app/pb_kits/playbook/pb_rich_text_editor/docs/example.yml +23 -20
  98. data/app/pb_kits/playbook/pb_rich_text_editor/docs/index.js +2 -1
  99. data/app/pb_kits/playbook/pb_table/index.ts +29 -27
  100. data/app/pb_kits/playbook/pb_text_input/text_input.html.erb +10 -10
  101. data/app/pb_kits/playbook/pb_textarea/_textarea.tsx +10 -0
  102. data/app/pb_kits/playbook/pb_textarea/docs/_textarea_default.html.erb +3 -3
  103. data/app/pb_kits/playbook/pb_textarea/docs/_textarea_default.jsx +3 -0
  104. data/app/pb_kits/playbook/pb_textarea/docs/_textarea_default.md +1 -0
  105. data/app/pb_kits/playbook/pb_textarea/textarea.html.erb +25 -9
  106. data/app/pb_kits/playbook/pb_textarea/textarea.rb +7 -1
  107. data/app/pb_kits/playbook/pb_time_picker/_time_picker.tsx +97 -11
  108. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_on_handler.jsx +5 -2
  109. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_required_indicator.html.erb +6 -0
  110. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_required_indicator.jsx +16 -0
  111. data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_required_indicator.md +3 -0
  112. data/app/pb_kits/playbook/pb_time_picker/docs/example.yml +2 -0
  113. data/app/pb_kits/playbook/pb_time_picker/docs/index.js +1 -0
  114. data/app/pb_kits/playbook/pb_time_picker/time_picker.rb +3 -0
  115. data/app/pb_kits/playbook/pb_time_picker/time_picker.test.jsx +47 -1
  116. data/app/pb_kits/playbook/pb_typeahead/_typeahead.test.jsx +24 -1
  117. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +412 -324
  118. data/app/pb_kits/playbook/pb_typeahead/components/Control.tsx +2 -0
  119. data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +4 -1
  120. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_required_indicator.html.erb +16 -0
  121. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_required_indicator.jsx +23 -0
  122. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_required_indicator.md +3 -0
  123. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_truncated_text.html.erb +1 -1
  124. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_truncated_text.jsx +1 -1
  125. data/app/pb_kits/playbook/pb_typeahead/docs/example.yml +2 -0
  126. data/app/pb_kits/playbook/pb_typeahead/docs/index.js +22 -21
  127. data/app/pb_kits/playbook/pb_typeahead/typeahead.html.erb +3 -2
  128. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +7 -1
  129. data/dist/chunks/{_pb_line_graph-BgKF_zz1.js → _pb_line_graph-DuJNCf7N.js} +1 -1
  130. data/dist/chunks/_typeahead-Cx2lp7TD.js +1 -0
  131. data/dist/chunks/{globalProps-BhVYCqRf.js → globalProps-Bc-FVsRt.js} +1 -1
  132. data/dist/chunks/lib-BwX82vim.js +29 -0
  133. data/dist/chunks/vendor.js +3 -3
  134. data/dist/menu.yml +2 -2
  135. data/dist/playbook-rails-react-bindings.js +1 -1
  136. data/dist/playbook-rails.js +1 -1
  137. data/dist/playbook.css +1 -1
  138. data/lib/playbook/forms/builder/form_field_builder.rb +1 -1
  139. data/lib/playbook/forms/builder/phone_number_field.rb +9 -0
  140. data/lib/playbook/forms/builder/typeahead_field.rb +15 -1
  141. data/lib/playbook/forms/builder.rb +2 -2
  142. data/lib/playbook/truncate.rb +1 -1
  143. data/lib/playbook/version.rb +1 -1
  144. metadata +42 -19
  145. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_attributes.html.erb +0 -5
  146. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_default.html.erb +0 -1
  147. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_focus.html.erb +0 -3
  148. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_inline.html.erb +0 -6
  149. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_preview.html.erb +0 -35
  150. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_required_indicator.html.erb +0 -10
  151. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_simple.html.erb +0 -1
  152. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_sticky.html.erb +0 -1
  153. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_templates.html.erb +0 -115
  154. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_toolbar_bottom.html.erb +0 -4
  155. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_toolbar_bottom.jsx +0 -14
  156. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.html.erb +0 -5
  157. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.rb +0 -63
  158. data/dist/chunks/_typeahead-B9a6ZsEP.js +0 -1
  159. data/dist/chunks/lib-DD34ZrWL.js +0 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5780d8a980b27fef50be2bd9f824684732373c72b707d5af5f8071c627b878a
4
- data.tar.gz: b304d2a4a3bd2a77b73ba7e95b70f5aff9bd6b1f4b78ed99a494712666ce6a1a
3
+ metadata.gz: 55c0e17fe1c9ebc070555123e048fad21174f1103ff7c924a70ef6fcbaf10f67
4
+ data.tar.gz: 7e49dfd207ef248a867883cbd4d2dc3c39f93dbb872578493a6732d2906097bf
5
5
  SHA512:
6
- metadata.gz: c8133ed39e3328aa2e76508e31c51e7ff0c81103c99f8c2324367333e2168ea08610f59fdff5d1bef97d3372637ee29150ee3d6541d83c3cea7417414bed5481
7
- data.tar.gz: f5488d8e0b060a56590d3c390d832a1760d9bd7e1b97a02afc0958c9252ae67f9667f2856d2ff70d3cdfc8fe71658aa2135bccef62f154e68a1db7f2a20fa807
6
+ metadata.gz: df84055b7fa9e7ba93d1e94fe8c4944033897e8841c05ef21c52191a91083856c072cb1d67a53c184d6cb39e28276eb862eb37818d12b200936b4e6e252d7c2e
7
+ data.tar.gz: 3ad38213a67a4a3c9440db8558c75728d4a6df072070420d1ef93a14efd5f13416addd58c165e5dc92e300ac1c3613870f0812f5bd6896fd10e8323d4eaed27d
@@ -66,8 +66,18 @@ const TableCellRenderer = ({
66
66
  // Find the "owning" colDefinition by accessor. Needed for multi column logic
67
67
  const colDef = findColumnDefByAccessor(columnDefinitions ?? [], column.id)
68
68
  const cellAlignment = colDef?.columnStyling?.cellAlignment ?? "right"
69
- const cellFontColor = colDef?.columnStyling?.fontColor
70
- const cellBackgroundColor = colDef?.columnStyling?.cellBackgroundColor
69
+
70
+ // Support function-based styling for conditional rendering
71
+ const cellFontColorValue = colDef?.columnStyling?.fontColor
72
+ const cellFontColor = typeof cellFontColorValue === 'function'
73
+ ? cellFontColorValue(row)
74
+ : cellFontColorValue
75
+
76
+ const cellBackgroundColorValue = colDef?.columnStyling?.cellBackgroundColor
77
+ const cellBackgroundColor = typeof cellBackgroundColorValue === 'function'
78
+ ? cellBackgroundColorValue(row)
79
+ : cellBackgroundColorValue
80
+
71
81
  const paddingValue = colDef?.columnStyling?.cellPadding ?? customRowStyle?.cellPadding
72
82
  const paddingClass = paddingValue ? `p_${paddingValue}` : undefined
73
83
 
@@ -920,6 +920,39 @@ test("columnStyling.backgroundColor works as excpected", () => {
920
920
  expect(firstEnrollmentCell).toHaveStyle({ backgroundColor: colors.error_subtle });
921
921
  });
922
922
 
923
+ test("columnStyling.cellBackgroundColor works as expected with function", () => {
924
+ const styledColumnDefs = [
925
+ {
926
+ accessor: "year",
927
+ label: "Year",
928
+ cellAccessors: ["quarter", "month", "day"],
929
+ },
930
+ {
931
+ accessor: "newEnrollments",
932
+ label: "New Enrollments",
933
+ columnStyling:{
934
+ cellBackgroundColor: (row) => row.original.newEnrollments > 15 ? colors.success_subtle : colors.error_subtle,
935
+ fontColor: (row) => row.original.newEnrollments > 15 ? colors.success : colors.error,
936
+ },
937
+ },
938
+ {
939
+ accessor: "scheduledMeetings",
940
+ label: "Scheduled Meetings",
941
+ },
942
+ ];
943
+
944
+ render(
945
+ <AdvancedTable
946
+ columnDefinitions={styledColumnDefs}
947
+ data={{ testid: testId }}
948
+ tableData={MOCK_DATA}
949
+ />
950
+ );
951
+
952
+ const firstEnrollmentCell = screen.getAllByText("20")[0].closest("td");
953
+ expect(firstEnrollmentCell).toHaveStyle({ backgroundColor: colors.success_subtle, color: colors.success });
954
+ });
955
+
923
956
  test("columnStyling.headerBackgroundColor works as excpected", () => {
924
957
  const styledColumnDefs = [
925
958
  {
@@ -0,0 +1,71 @@
1
+ import React from "react"
2
+ import AdvancedTable from '../_advanced_table'
3
+ import colors from '../../tokens/exports/_colors.module.scss'
4
+ import MOCK_DATA from "./advanced_table_mock_data.json"
5
+ import Flex from '../../pb_flex/_flex'
6
+ import Title from '../../pb_title/_title'
7
+ import Body from '../../pb_body/_body'
8
+
9
+
10
+ const AdvancedTableColumnStylingBackgroundCustom = (props) => {
11
+ const columnDefinitions = [
12
+ {
13
+ accessor: "year",
14
+ label: "Year",
15
+ cellAccessors: ["quarter", "month", "day"],
16
+ customRenderer: (row, value) => (
17
+ <Flex flexDirection="column">
18
+ <Title size={4}
19
+ text={value}
20
+ />
21
+ <Body text="lorem ipsum" />
22
+ <Body text="lorem ipsum" />
23
+ </Flex>
24
+ ),
25
+ },
26
+ {
27
+ accessor: "newEnrollments",
28
+ label: "New Enrollments",
29
+ columnStyling:{
30
+ cellBackgroundColor: (row) => row.original.newEnrollments > 15 ? colors.success_subtle : colors.error_subtle,
31
+ fontColor: (row) => row.original.newEnrollments > 15 ? colors.success : colors.error,
32
+ },
33
+ },
34
+ {
35
+ accessor: "scheduledMeetings",
36
+ label: "Scheduled Meetings",
37
+ columnStyling:{
38
+ cellBackgroundColor: (row) => row.original.scheduledMeetings >= 15 ? colors.info_subtle : colors.warning_subtle
39
+ },
40
+ },
41
+ {
42
+ accessor: "attendanceRate",
43
+ label: "Attendance Rate",
44
+ columnStyling:{cellBackgroundColor: colors.info, headerBackgroundColor: colors.info, fontColor: colors.white, headerFontColor: colors.white},
45
+ },
46
+ {
47
+ accessor: "completedClasses",
48
+ label: "Completed Classes",
49
+ },
50
+ {
51
+ accessor: "classCompletionRate",
52
+ label: "Class Completion Rate",
53
+ },
54
+ {
55
+ accessor: "graduatedStudents",
56
+ label: "Graduated Students",
57
+ },
58
+ ]
59
+
60
+ return (
61
+ <div>
62
+ <AdvancedTable
63
+ columnDefinitions={columnDefinitions}
64
+ tableData={MOCK_DATA}
65
+ {...props}
66
+ />
67
+ </div>
68
+ )
69
+ }
70
+
71
+ export default AdvancedTableColumnStylingBackgroundCustom
@@ -0,0 +1,4 @@
1
+ `cellBackgroundColor` and `fontColor` can also accept functions for conditional styling based on row data. The function receives the row object and returns a color value.
2
+
3
+ See the code snippet below for more details.
4
+
@@ -78,6 +78,7 @@ examples:
78
78
  - advanced_table_column_styling: Column Styling
79
79
  - advanced_table_column_styling_column_headers: Column Styling with Multiple Headers
80
80
  - advanced_table_column_styling_background: Column Styling Background Color
81
+ - advanced_table_column_styling_background_custom: Column Styling Background Color (Custom)
81
82
  - advanced_table_column_styling_background_multi: Column Styling Background Color with Multiple Headers
82
83
  - advanced_table_padding_control: Padding Control using Column Styling
83
84
  - advanced_table_column_border_color: Column Group Border Color
@@ -47,4 +47,5 @@ export { default as AdvancedTableSortPerColumnForMultiColumn } from './_advanced
47
47
  export { default as AdvancedTablePaddingControl } from './_advanced_table_padding_control.jsx'
48
48
  export { default as AdvancedTablePaddingControlPerRow } from './_advanced_table_padding_control_per_row.jsx'
49
49
  export { default as AdvancedTableColumnStylingBackground } from './_advanced_table_column_styling_background.jsx'
50
- export { default as AdvancedTableColumnStylingBackgroundMulti } from './_advanced_table_column_styling_background_multi.jsx'
50
+ export { default as AdvancedTableColumnStylingBackgroundMulti } from './_advanced_table_column_styling_background_multi.jsx'
51
+ export { default as AdvancedTableColumnStylingBackgroundCustom } from './_advanced_table_column_styling_background_custom.jsx'
@@ -1,36 +1,4 @@
1
- <%= pb_rails("flex", props: {
2
- gap: "md",
3
- orientation: "column"
4
- }) do %>
5
-
6
- <%= pb_rails("caption", props: { text: "flex direction responsive: row on md + xl, column as default" }) %>
7
- <%= pb_rails("card", props: {
8
- flex_direction: { default: "column", md: "row", xl:"row" }
9
- }) do %>
10
- <%= pb_rails("body", props: { text: "Item 1", color: "default" }) %>
11
- <%= pb_rails("body", props: { text: "Item 2", color: "default" }) %>
12
- <%= pb_rails("body", props: { text: "Item 3", color: "default" }) %>
13
- <% end %>
14
-
15
- <%= pb_rails("caption", props: { text: "align items responsive: start on default, center on md, end on lg" }) %>
16
- <%= pb_rails("card", props: {
17
- display: "flex",
18
- flex_direction: "row",
19
- align_items: { default: "start", md: "center", lg: "end" },
20
- }) do %>
21
- <%= pb_rails("card", props: {height:"xs" }) do %> Card 1 <% end %>
22
- <%= pb_rails("card", props: { height:"sm" }) do %> Card 2 <% end %>
23
- <%= pb_rails("card", props: { height:"xs" }) do %> Card 3 <% end %>
24
- <% end %>
25
-
26
- <%= pb_rails("caption", props: { text: "Text Align responsive: left on default, center on md, right on lg" }) %>
27
- <%= pb_rails("card", props: {
28
- text_align: { default: "left", md: "center", lg: "right" },
29
- width: "100%"
30
- }) do %>
31
- <%= pb_rails("body", props: { }) do %> text 1 <% end %>
32
- <%= pb_rails("body", props: { }) do %> text 2 <% end %>
33
- <%= pb_rails("body", props: { }) do %> text 3 <% end %>
34
- <% end %>
1
+ <%= pb_rails("card", props: {
2
+ }) do %>
3
+ Card content
35
4
  <% end %>
36
-
@@ -168,4 +168,4 @@ $transition: $transition_cubic;
168
168
  border-color: $error;
169
169
  }
170
170
  }
171
- }
171
+ }
@@ -4,6 +4,8 @@ import Icon from '../pb_icon/_icon'
4
4
  import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../utilities/props'
5
5
  import classnames from 'classnames'
6
6
  import { globalProps, GlobalProps } from '../utilities/globalProps'
7
+ import colors from '../tokens/exports/_colors.module.scss'
8
+ import spacing from '../tokens/exports/_spacing.module.scss'
7
9
 
8
10
  type CheckboxProps = {
9
11
  aria?: {[key: string]: string},
@@ -19,6 +21,7 @@ type CheckboxProps = {
19
21
  indeterminate?: boolean,
20
22
  name?: string,
21
23
  onChange?: (event: React.FormEvent<HTMLInputElement>) => void,
24
+ requiredIndicator?: boolean,
22
25
  tabIndex?: number,
23
26
  text?: string,
24
27
  value?: string,
@@ -39,6 +42,7 @@ const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>((props, ref) => {
39
42
  indeterminate = false,
40
43
  name = '',
41
44
  onChange = () => { void 0 },
45
+ requiredIndicator = false,
42
46
  tabIndex,
43
47
  text = '',
44
48
  value = '',
@@ -124,7 +128,20 @@ const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>((props, ref) => {
124
128
  variant={null}
125
129
  >
126
130
  {text}
131
+ {requiredIndicator && (
132
+ <span
133
+ aria-hidden="true"
134
+ className="pb_required_indicator"
135
+ style={{
136
+ color: colors.error,
137
+ marginLeft: spacing.space_xs,
138
+ }}
139
+ >
140
+ {'*'}
141
+ </span>
142
+ )}
127
143
  </Body>
144
+
128
145
  </label>
129
146
  )
130
147
  })
@@ -5,6 +5,15 @@
5
5
  <%= pb_rails("icon", props: { icon: "minus", classname: "indeterminate_icon hidden", fixed_width: true}) %>
6
6
  </span>
7
7
  <span class="pb_checkbox_label">
8
- <%= pb_rails("body", props: { status: object.checkbox_label_status, text: object.text, dark: object.dark, margin_right: object.form_spacing ? "xs" : "" }) %>
8
+ <%= pb_rails("body", props: { status: object.checkbox_label_status, dark: object.dark, margin_right: object.form_spacing ? "xs" : "" }) do %>
9
+ <%= object.text%>
10
+ <% if object.required_indicator %>
11
+ <span
12
+ class="pb_checkbox_required_indicator"
13
+ aria-hidden="true"
14
+ style="color: #DA0014;"
15
+ >*</span>
16
+ <% end %>
17
+ <% end %>
9
18
  </span>
10
19
  <% end %>
@@ -23,6 +23,8 @@ module Playbook
23
23
  prop :hidden_input, type: Playbook::Props::Boolean,
24
24
  default: false
25
25
  prop :hidden_value
26
+ prop :required_indicator, type: Playbook::Props::Boolean,
27
+ default: false
26
28
 
27
29
  def classname
28
30
  generate_classname("pb_checkbox_kit", checked_class) + error_class
@@ -0,0 +1,6 @@
1
+ <%= pb_rails("checkbox" , props: {
2
+ required_indicator: true,
3
+ text: "Checkbox Label",
4
+ value: "checkbox-value",
5
+ name: "checkbox-name"
6
+ }) %>
@@ -0,0 +1,17 @@
1
+ import React from 'react'
2
+ import Checkbox from '../_checkbox'
3
+
4
+ const CheckboxRequiredIndicator = () => {
5
+ return (
6
+ <div>
7
+ <Checkbox
8
+ name="checkbox-name"
9
+ requiredIndicator
10
+ text="Checkbox label"
11
+ value="check-box value"
12
+ />
13
+ </div>
14
+ )
15
+ }
16
+
17
+ export default CheckboxRequiredIndicator
@@ -0,0 +1,3 @@
1
+ The `requiredIndicator`/`required_indicator` prop displays a red asterisk (*) next to the label, visually indicating that the field is required. This is purely visual and does not enforce validation.
2
+
3
+ You can use `requiredIndicator`/`required_indicator` with any validation approach: HTML5 validation via the `required` prop, client-side validation, or backend validation. For this reason, it works independently and doesn't need to be paired with the `required` prop.
@@ -8,6 +8,7 @@ examples:
8
8
  - checkbox_indeterminate: Indeterminate Checkbox
9
9
  - checkbox_disabled: Disabled Checkbox
10
10
  - checkbox_form: Form and Hidden Input
11
+ # - checkbox_required_indicator: Required Indicator
11
12
 
12
13
  react:
13
14
  - checkbox_default: Default
@@ -17,6 +18,7 @@ examples:
17
18
  - checkbox_error: Default w/ Error
18
19
  - checkbox_indeterminate: Indeterminate Checkbox
19
20
  - checkbox_disabled: Disabled Checkbox
21
+ # - checkbox_required_indicator: Required Indicator
20
22
 
21
23
  swift:
22
24
  - checkbox_default_swift: Default
@@ -5,3 +5,4 @@ export { default as CheckboxError } from './_checkbox_error.jsx'
5
5
  export { default as CheckboxChecked } from './_checkbox_checked.jsx'
6
6
  export { default as CheckboxIndeterminate } from './_checkbox_indeterminate.jsx'
7
7
  export { default as CheckboxDisabled } from './_checkbox_disabled.jsx'
8
+ export { default as CheckboxRequiredIndicator } from './_checkbox_required_indicator.jsx'
@@ -40,7 +40,7 @@ type DatePickerProps = {
40
40
  maxDate: string,
41
41
  minDate: string,
42
42
  name: string,
43
- pickerId?: string,
43
+ pickerId: string,
44
44
  placeholder?: string,
45
45
  positionElement?: HTMLElement | null,
46
46
  scrollContainer?: string,
@@ -196,6 +196,8 @@ const DatePicker = (props: DatePickerProps): React.ReactElement => {
196
196
 
197
197
  const angleDown = getAllIcons()["angleDown"].icon as unknown as { [key: string]: SVGElement }
198
198
 
199
+ const errorId = error ? `${pickerId}-error` : undefined
200
+
199
201
  return (
200
202
  <div
201
203
  {...ariaProps}
@@ -211,14 +213,18 @@ const DatePicker = (props: DatePickerProps): React.ReactElement => {
211
213
  >
212
214
 
213
215
  {!hideLabel && (
214
- <Caption
215
- className="pb_date_picker_kit_label"
216
- text={label}
217
- />
216
+ <label htmlFor={pickerId}>
217
+ <Caption
218
+ className="pb_date_picker_kit_label"
219
+ text={label}
220
+ />
221
+ </label>
218
222
  )}
219
223
  <>
220
224
  <div className="date_picker_input_wrapper">
221
225
  <input
226
+ aria-describedby={errorId}
227
+ aria-invalid={!!error}
222
228
  autoComplete="off"
223
229
  className="date_picker_input"
224
230
  disabled={disableInput}
@@ -232,6 +238,9 @@ const DatePicker = (props: DatePickerProps): React.ReactElement => {
232
238
 
233
239
  {error &&
234
240
  <Body
241
+ aria={{ atomic: "true", live: "polite" }}
242
+ htmlOptions={{ role: "alert" }}
243
+ id={errorId}
235
244
  status="negative"
236
245
  text={error}
237
246
  variant={null}
@@ -0,0 +1 @@
1
+ `pickerId`/`picker_id` is a **required prop** to instantiate the Date Picker. The presence of `pickerId`/`picker_id` in your Date Picker also associates the label with the input, providing the ability to focus the Date Picker by clicking the label.
@@ -135,6 +135,8 @@
135
135
  position: sticky;
136
136
  top: 0;
137
137
  background-color: $white;
138
+ border-top-left-radius: $border_radius_md;
139
+ border-top-right-radius: $border_radius_md;
138
140
  z-index: $z_8;
139
141
  }
140
142
 
@@ -256,7 +258,7 @@
256
258
  }
257
259
  &.full_height_left {
258
260
  justify-content: flex-start;
259
-
261
+
260
262
  .pb_dialog {
261
263
  border-radius: 0;
262
264
  height: 100%;
@@ -302,7 +304,7 @@
302
304
 
303
305
  &.full_height_center {
304
306
  justify-content: center;
305
-
307
+
306
308
  .pb_dialog {
307
309
  border-radius: 0;
308
310
  height: 100%;
@@ -346,7 +348,7 @@
346
348
 
347
349
  &.full_height_right {
348
350
  justify-content: flex-end;
349
-
351
+
350
352
  .pb_dialog {
351
353
  border-radius: 0;
352
354
  height: 100%;
@@ -417,7 +419,7 @@
417
419
  margin: unset !important;
418
420
  margin-right: auto !important;
419
421
  }
420
-
422
+
421
423
  .pb_dialog {
422
424
  border-radius: 0;
423
425
  height: 100% !important;
@@ -463,7 +465,7 @@
463
465
 
464
466
  &.full_height_center {
465
467
  justify-content: center;
466
-
468
+
467
469
  .pb_dialog {
468
470
  border-radius: 0;
469
471
  height: 100% !important;
@@ -510,7 +512,7 @@
510
512
  margin: unset !important;
511
513
  margin-left: auto !important;
512
514
  }
513
-
515
+
514
516
  .pb_dialog {
515
517
  border-radius: 0;
516
518
  height: 100% !important;
@@ -76,6 +76,12 @@
76
76
  z-index: $z_1;
77
77
  width: 100%;
78
78
 
79
+ &.constrain_height {
80
+ overflow-y: auto;
81
+ overflow-x: hidden;
82
+ max-height: 18em;
83
+ }
84
+
79
85
  .pb_dropdown_option,
80
86
  .pb_dropdown_option_list,
81
87
  .pb_dropdown_option_selected,
@@ -36,6 +36,8 @@ type DropdownProps = {
36
36
  blankSelection?: string;
37
37
  children?: React.ReactChild[] | React.ReactChild | React.ReactElement[];
38
38
  className?: string;
39
+ clearable?: boolean;
40
+ constrainHeight?: boolean;
39
41
  customQuickPickDates?: CustomQuickPickDates;
40
42
  formPillProps?: GenericObject;
41
43
  dark?: boolean;
@@ -49,6 +51,7 @@ type DropdownProps = {
49
51
  multiSelect?: boolean;
50
52
  onSelect?: (arg: GenericObject) => null;
51
53
  options?: GenericObject;
54
+ placeholder?: string;
52
55
  separators?: boolean;
53
56
  variant?: "default" | "subtle" | "quickpick";
54
57
  rangeEndsToday?: boolean;
@@ -74,6 +77,8 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
74
77
  blankSelection = '',
75
78
  children,
76
79
  className,
80
+ clearable = true,
81
+ constrainHeight = false,
77
82
  customQuickPickDates,
78
83
  dark = false,
79
84
  data = {},
@@ -87,6 +92,7 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
87
92
  formPillProps,
88
93
  onSelect,
89
94
  options,
95
+ placeholder,
90
96
  rangeEndsToday = false,
91
97
  controlsStartId,
92
98
  controlsEndId,
@@ -113,6 +119,16 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
113
119
 
114
120
  const [isDropDownClosed, setIsDropDownClosed, toggleDropdown] = useDropdown(isClosed);
115
121
 
122
+ // Use a suffix for the trigger ID to avoid conflict with the outer div's id
123
+ const sanitizeForId = (str: string) =>
124
+ str.toLowerCase().replace(/\s+/g, "_").replace(/[^a-z0-9_]/g, "");
125
+ const selectId = id
126
+ ? `${id}_trigger`
127
+ : label
128
+ ? sanitizeForId(label)
129
+ : undefined;
130
+ const errorId = error ? `${selectId}-error` : undefined;
131
+
116
132
  const [filterItem, setFilterItem] = useState("");
117
133
  const initialSelected = useMemo(() => {
118
134
  // Handle quickpick variant with string defaultValue (e.g., "This Month")
@@ -145,9 +161,19 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
145
161
 
146
162
  const dropdownRef = useRef(null);
147
163
  const inputRef = useRef<HTMLInputElement>(null);
148
- const inputWrapperRef = useRef(null);
164
+ const inputWrapperRef = useRef<HTMLDivElement | null>(null);
149
165
  const dropdownContainerRef = useRef(null);
150
166
 
167
+ const handleLabelClick = (e: React.MouseEvent) => {
168
+ e.stopPropagation();
169
+ if (selectId) {
170
+ const trigger = document.getElementById(selectId);
171
+ if (trigger) trigger.focus();
172
+ }
173
+ setIsInputFocused(true);
174
+ toggleDropdown();
175
+ };
176
+
151
177
  const selectedArray = Array.isArray(selected)
152
178
  ? selected
153
179
  : selected && Object.keys(selected).length
@@ -211,6 +237,34 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
211
237
  }
212
238
  }, [isDropDownClosed]);
213
239
 
240
+ // Auto-position dropdown above/below based on available space
241
+ useEffect(() => {
242
+ if (!isDropDownClosed && dropdownContainerRef.current) {
243
+ const container = dropdownContainerRef.current;
244
+ const wrapper = container.closest('.dropdown_wrapper') as HTMLElement;
245
+ if (!wrapper) return;
246
+
247
+ const wrapperRect = wrapper.getBoundingClientRect();
248
+ const h = container.getBoundingClientRect().height || container.scrollHeight;
249
+ const spaceBelow = window.innerHeight - wrapperRect.bottom;
250
+ const spaceAbove = wrapperRect.top;
251
+
252
+ // If not enough space below but enough space above, position above
253
+ if (spaceBelow < h + 10 && spaceAbove >= h + 10) {
254
+ container.style.top = "auto";
255
+ container.style.bottom = "calc(100% + 5px)";
256
+ container.style.marginTop = "0";
257
+ container.style.marginBottom = "0";
258
+ } else {
259
+ // Default: position below
260
+ container.style.top = "";
261
+ container.style.bottom = "";
262
+ container.style.marginTop = "";
263
+ container.style.marginBottom = "";
264
+ }
265
+ }
266
+ }, [isDropDownClosed, dropdownContainerRef]);
267
+
214
268
 
215
269
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
216
270
  setFilterItem(e.target.value);
@@ -375,10 +429,14 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
375
429
  value={{
376
430
  activeStyle,
377
431
  autocomplete,
432
+ clearable,
378
433
  dropdownContainerRef,
379
- filteredOptions,
434
+ error,
435
+ errorId,
380
436
  filterItem,
437
+ filteredOptions,
381
438
  focusedOptionIndex,
439
+ label,
382
440
  formPillProps,
383
441
  handleBackspace,
384
442
  handleChange,
@@ -388,6 +446,7 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
388
446
  inputWrapperRef,
389
447
  isDropDownClosed,
390
448
  isInputFocused,
449
+ selectId,
391
450
  multiSelect,
392
451
  onSelect,
393
452
  optionsWithBlankSelection,
@@ -399,13 +458,20 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
399
458
  toggleDropdown
400
459
  }}
401
460
  >
402
- {label &&
403
- <Caption
404
- dark={dark}
405
- marginBottom="xs"
406
- text={label}
407
- />
408
- }
461
+ {label && (
462
+ <label
463
+ data-dropdown="pb-dropdown-label"
464
+ htmlFor={selectId}
465
+ onClick={handleLabelClick}
466
+ >
467
+ <Caption
468
+ className="pb_dropdown_kit_label"
469
+ dark={dark}
470
+ marginBottom="xs"
471
+ text={label}
472
+ />
473
+ </label>
474
+ )}
409
475
  <div className={`dropdown_wrapper ${error ? 'error' : ''}`}
410
476
  onBlur={() => {
411
477
  // Debounce to delay the execution to prevent jumpiness in Focus state
@@ -426,8 +492,8 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
426
492
  </>
427
493
  ) : (
428
494
  <>
429
- <DropdownTrigger />
430
- <DropdownContainer>
495
+ <DropdownTrigger placeholder={placeholder} />
496
+ <DropdownContainer constrainHeight={constrainHeight}>
431
497
  {optionsWithBlankSelection &&
432
498
  optionsWithBlankSelection?.map((option: GenericObject) => (
433
499
  <DropdownOption key={option.id}
@@ -438,12 +504,16 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
438
504
  </>
439
505
  )}
440
506
 
441
- {error &&
507
+ {error && (
442
508
  <Body
509
+ aria={{ atomic: "true", live: "polite" }}
510
+ dark={dark}
511
+ htmlOptions={{ role: "alert" }}
512
+ id={errorId}
443
513
  status="negative"
444
514
  text={error}
445
515
  />
446
- }
516
+ )}
447
517
  </div>
448
518
  </DropdownContext.Provider>
449
519
  </div>
@@ -0,0 +1,3 @@
1
+ The `blank_selection` prop adds a blank option at the top of the dropdown options list. This allows users to explicitly clear their selection by choosing the blank option.
2
+
3
+ The blank selection option appears as the first item in the dropdown and has an empty value (`id: ""`, `value: ""`). When selected, it effectively clears the dropdown selection.