playbook_ui 16.1.0.pre.alpha.play264213817 → 16.1.0.pre.alpha.play273614090
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.
- checksums.yaml +4 -4
- data/app/pb_kits/playbook/pb_advanced_table/Components/RegularTableView.tsx +12 -2
- data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +33 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_background_custom.jsx +71 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_background_custom.md +4 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +1 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +2 -1
- data/app/pb_kits/playbook/pb_card/docs/_card_light.html.erb +3 -35
- data/app/pb_kits/playbook/pb_checkbox/_checkbox.scss +1 -1
- data/app/pb_kits/playbook/pb_checkbox/_checkbox.tsx +17 -0
- data/app/pb_kits/playbook/pb_checkbox/checkbox.html.erb +10 -1
- data/app/pb_kits/playbook/pb_checkbox/checkbox.rb +2 -0
- data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_required_indicator.html.erb +6 -0
- data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_required_indicator.jsx +17 -0
- data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_required_indicator.md +3 -0
- data/app/pb_kits/playbook/pb_checkbox/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_checkbox/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_date_picker/_date_picker.tsx +14 -5
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_default.md +1 -0
- data/app/pb_kits/playbook/pb_dialog/_dialog.scss +8 -6
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +6 -0
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +83 -13
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_blank_selection_rails.md +3 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_blank_selection_react.md +3 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.html.erb +52 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.jsx +72 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.md +5 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height.jsx +33 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height_rails.html.erb +20 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height_rails.md +8 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height_react.md +8 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_display_rails.html.erb +1 -1
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_label.html.erb +6 -3
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_label.jsx +1 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_label.md +3 -1
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.html.erb +9 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.jsx +33 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.md +3 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +6 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/index.js +4 -1
- data/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +12 -6
- data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +15 -0
- data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +94 -0
- data/app/pb_kits/playbook/pb_dropdown/dropdown_container.rb +5 -1
- data/app/pb_kits/playbook/pb_dropdown/dropdown_trigger.html.erb +15 -10
- data/app/pb_kits/playbook/pb_dropdown/dropdown_trigger.rb +4 -0
- data/app/pb_kits/playbook/pb_dropdown/index.js +191 -83
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownContainer.tsx +3 -0
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +18 -1
- data/app/pb_kits/playbook/pb_dropdown/utilities/clickOutsideHelper.tsx +6 -0
- data/app/pb_kits/playbook/pb_filter/Filter/SortMenu.tsx +1 -1
- data/app/pb_kits/playbook/pb_filter/docs/_filter_default.html.erb +2 -2
- data/app/pb_kits/playbook/pb_filter/docs/_filter_default.jsx +16 -9
- data/app/pb_kits/playbook/pb_filter/filter.rb +2 -2
- data/app/pb_kits/playbook/pb_form/docs/_form_with_required_indicator.html.erb +6 -2
- data/app/pb_kits/playbook/pb_form/pb_form_validation.js +9 -2
- data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.html.erb +5 -5
- data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.jsx +4 -4
- data/app/pb_kits/playbook/pb_form_pill/form_pill.rb +4 -0
- data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.scss +7 -0
- data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +638 -549
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.html.erb +3 -3
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.jsx +4 -7
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.md +3 -0
- data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.test.jsx +4 -4
- data/app/pb_kits/playbook/pb_passphrase/_passphrase.tsx +40 -7
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_meter_settings.jsx +1 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_required_indicator.html.erb +7 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_required_indicator.jsx +24 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_required_indicator.md +3 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_passphrase/passphrase.rb +2 -0
- data/app/pb_kits/playbook/pb_passphrase/passphrase.test.jsx +30 -1
- data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +3 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_required_indicator.html.erb +5 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_required_indicator.jsx +14 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_required_indicator.md +3 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.rb +3 -0
- data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.test.js +34 -3
- data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.tsx +51 -16
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_label.jsx +44 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_label.md +1 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_required_indicator.jsx +1 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_label.jsx +28 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_label.md +1 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_required_indicator.jsx +1 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/docs/index.js +2 -0
- data/app/pb_kits/playbook/pb_table/index.ts +29 -27
- data/app/pb_kits/playbook/pb_text_input/text_input.html.erb +10 -10
- data/app/pb_kits/playbook/pb_textarea/_textarea.tsx +10 -0
- data/app/pb_kits/playbook/pb_textarea/docs/_textarea_default.html.erb +3 -3
- data/app/pb_kits/playbook/pb_textarea/docs/_textarea_default.jsx +3 -0
- data/app/pb_kits/playbook/pb_textarea/docs/_textarea_default.md +1 -0
- data/app/pb_kits/playbook/pb_textarea/textarea.html.erb +25 -9
- data/app/pb_kits/playbook/pb_textarea/textarea.rb +7 -1
- data/app/pb_kits/playbook/pb_time_picker/_time_picker.tsx +97 -11
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_on_handler.jsx +5 -2
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_required_indicator.html.erb +6 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_required_indicator.jsx +16 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_required_indicator.md +3 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_time_picker/time_picker.rb +3 -0
- data/app/pb_kits/playbook/pb_time_picker/time_picker.test.jsx +47 -1
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.test.jsx +24 -1
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +412 -324
- data/app/pb_kits/playbook/pb_typeahead/components/Control.tsx +2 -0
- data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +4 -1
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_required_indicator.html.erb +16 -0
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_required_indicator.jsx +23 -0
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_required_indicator.md +3 -0
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_truncated_text.html.erb +1 -1
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_truncated_text.jsx +1 -1
- data/app/pb_kits/playbook/pb_typeahead/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_typeahead/docs/index.js +22 -21
- data/app/pb_kits/playbook/pb_typeahead/typeahead.html.erb +3 -2
- data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +7 -1
- data/dist/chunks/{_pb_line_graph-BgKF_zz1.js → _pb_line_graph-DuJNCf7N.js} +1 -1
- data/dist/chunks/_typeahead-BKSzddAX.js +1 -0
- data/dist/chunks/{globalProps-BhVYCqRf.js → globalProps-Bc-FVsRt.js} +1 -1
- data/dist/chunks/lib-BwX82vim.js +29 -0
- data/dist/chunks/vendor.js +3 -3
- data/dist/menu.yml +2 -2
- data/dist/playbook-rails-react-bindings.js +1 -1
- data/dist/playbook-rails.js +1 -1
- data/dist/playbook.css +1 -1
- data/lib/playbook/forms/builder/form_field_builder.rb +1 -1
- data/lib/playbook/forms/builder/phone_number_field.rb +9 -0
- data/lib/playbook/forms/builder/typeahead_field.rb +15 -1
- data/lib/playbook/forms/builder.rb +2 -2
- data/lib/playbook/truncate.rb +1 -1
- data/lib/playbook/version.rb +1 -1
- metadata +42 -6
- data/dist/chunks/_typeahead-B9a6ZsEP.js +0 -1
- data/dist/chunks/lib-DD34ZrWL.js +0 -29
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 33c67b8b945f889b6ccfea5e7b8279f6a865dcfc2463be92d4fc8bd6bbc9c83e
|
|
4
|
+
data.tar.gz: e265e657829ea242acf7c0196106f86db4dc37a3eba77ec23ae747d2908f08fa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 042aa299615c7b669b07bc8ba55d273c10074a9dc8e6d89f0ec02a5903a0e9908ff92abe1bfeec1a170e4138436a10e9c68ca7ee951e232c8c6d483ef35228e2
|
|
7
|
+
data.tar.gz: f6d08b67113ad1fd61d652ab425e529c6730768bb028d2314f3c4285e207b6303d10c8003c0113d0ffbeec560776d29aa06094f5753caed34dd23764fc3417d3
|
|
@@ -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
|
-
|
|
70
|
-
|
|
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
|
|
@@ -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("
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
@@ -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,
|
|
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,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
|
|
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
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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;
|
|
@@ -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
|
-
|
|
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
|
-
<
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
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.
|