playbook_ui 14.19.0 → 14.20.0.pre.alpha.PLAY2127dropdowncloseonselectionpropreact8006
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 +11 -1
- data/app/pb_kits/playbook/pb_advanced_table/Components/TableActionBar.tsx +175 -16
- data/app/pb_kits/playbook/pb_advanced_table/Components/TableHeaderCell.tsx +56 -25
- data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableState.ts +23 -13
- data/app/pb_kits/playbook/pb_advanced_table/Utilities/VisibilityTree.ts +47 -0
- data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +14 -10
- data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +7 -2
- data/app/pb_kits/playbook/pb_advanced_table/advanced_table.html.erb +16 -8
- data/app/pb_kits/playbook/pb_advanced_table/advanced_table.rb +9 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_beta.md +6 -2
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_visibility.jsx +57 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_visibility.md +4 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_visibility_custom.jsx +62 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_visibility_custom.md +1 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_visibility_multi.jsx +82 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_visibility_multi.md +1 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_visibility_with_state.jsx +66 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_visibility_with_state.md +3 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_default.md +1 -1
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows_actions_rails.html.erb +137 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows_actions_rails.md +3 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows_header_rails.html.erb +40 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_selectable_rows_header_rails.md +1 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +6 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +5 -1
- data/app/pb_kits/playbook/pb_advanced_table/index.js +155 -12
- data/app/pb_kits/playbook/pb_advanced_table/scss_partials/advanced_table_sticky_mixin.scss +1 -0
- data/app/pb_kits/playbook/pb_advanced_table/table_action_bar.html.erb +23 -0
- data/app/pb_kits/playbook/pb_advanced_table/table_action_bar.rb +19 -0
- data/app/pb_kits/playbook/pb_advanced_table/table_header.rb +4 -0
- data/app/pb_kits/playbook/pb_checkbox/checkbox.html.erb +4 -11
- data/app/pb_kits/playbook/pb_checkbox/checkbox.rb +10 -6
- data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_indeterminate.html.erb +2 -48
- data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_indeterminate_rails.md +1 -0
- data/app/pb_kits/playbook/pb_checkbox/index.js +56 -0
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +1 -1
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +86 -19
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_close_on_select.jsx +42 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_close_on_select.md +1 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_default_rails.html.erb +31 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_default_rails.md +5 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select.jsx +56 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select.md +3 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_display.jsx +58 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_display.md +3 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_display_rails.html.erb +20 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_display_rails.md +1 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_rails.html.erb +19 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_rails.md +3 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_with_autocomplete.html.erb +20 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_with_autocomplete.jsx +57 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_with_autocomplete.md +1 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_with_custom_options.html.erb +50 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_with_custom_options.jsx +105 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_with_default.html.erb +22 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_with_default.jsx +67 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_display.jsx +11 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_display.md +1 -1
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_display_rails.html.erb +33 -2
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_display_rails.md +3 -1
- data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +13 -1
- data/app/pb_kits/playbook/pb_dropdown/docs/index.js +6 -0
- data/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +3 -3
- data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +16 -2
- data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +108 -2
- data/app/pb_kits/playbook/pb_dropdown/dropdown_trigger.html.erb +34 -13
- data/app/pb_kits/playbook/pb_dropdown/dropdown_trigger.rb +3 -1
- data/app/pb_kits/playbook/pb_dropdown/hooks/useHandleOnKeydown.tsx +0 -6
- data/app/pb_kits/playbook/pb_dropdown/index.js +359 -29
- data/app/pb_kits/playbook/pb_dropdown/keyboard_accessibility.js +39 -12
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownContainer.tsx +2 -2
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownOption.tsx +26 -18
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +97 -20
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/MultiSelectTriggerDisplay.tsx +58 -0
- data/app/pb_kits/playbook/pb_file_upload/_file_upload.scss +13 -0
- data/app/pb_kits/playbook/pb_file_upload/_file_upload.tsx +11 -1
- data/app/pb_kits/playbook/pb_file_upload/docs/_file_upload_error.html.erb +1 -0
- data/app/pb_kits/playbook/pb_file_upload/docs/_file_upload_error.jsx +41 -0
- data/app/pb_kits/playbook/pb_file_upload/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_file_upload/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_file_upload/file_upload.html.erb +1 -0
- data/app/pb_kits/playbook/pb_file_upload/file_upload.rb +7 -1
- data/app/pb_kits/playbook/pb_file_upload/fileupload.test.js +18 -0
- data/app/pb_kits/playbook/pb_form/docs/_form_form_with.html.erb +1 -0
- data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +1 -0
- data/app/pb_kits/playbook/pb_form_group/_error_state_mixin.scss +2 -2
- data/app/pb_kits/playbook/pb_form_pill/_form_pill.scss +19 -12
- data/app/pb_kits/playbook/pb_home_address_street/_home_address_street.tsx +13 -7
- data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +2 -2
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_color.html.erb +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_color.jsx +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_default.html.erb +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_default.jsx +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled.html.erb +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled.jsx +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled_options.html.erb +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled_options.jsx +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled_options_default.html.erb +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled_options_default.jsx +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled_options_parent.html.erb +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled_options_parent.jsx +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled_options_parent_default.html.erb +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled_options_parent_default.jsx +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_error.html.erb +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_error.jsx +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.html.erb +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.jsx +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_react_hook.jsx +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_reset.html.erb +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_return_all_selected.html.erb +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_return_all_selected.jsx +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_selected_ids.html.erb +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_selected_ids.md +2 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_selected_ids_react.jsx +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_selected_ids_react.md +3 -1
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_single.html.erb +22 -22
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_single.jsx +22 -22
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_single_children_only.html.erb +22 -22
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_single_children_only.jsx +22 -22
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children.jsx +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children_with_radios.jsx +11 -11
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_form.html.erb +11 -11
- data/app/pb_kits/playbook/pb_person/_person.tsx +12 -2
- data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.scss +9 -9
- data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +4 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_exclude_countries.html.erb +4 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_exclude_countries.jsx +15 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_exclude_countries.md +1 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_only_countries.jsx +1 -1
- data/app/pb_kits/playbook/pb_phone_number_input/docs/example.yml +4 -3
- 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_section_separator/_section_separator.tsx +2 -2
- data/app/pb_kits/playbook/pb_select/docs/_select_custom_select_subheaders.html.erb +12 -0
- data/app/pb_kits/playbook/pb_select/docs/_select_custom_select_subheaders.jsx +31 -0
- data/app/pb_kits/playbook/pb_select/docs/_select_custom_select_subheaders.md +1 -0
- data/app/pb_kits/playbook/pb_select/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_select/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_table/docs/_table_with_selectable_rows.html.erb +3 -51
- data/app/pb_kits/playbook/pb_text_input/_text_input.scss +4 -2
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +73 -3
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_preserve_input.jsx +23 -0
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_preserve_input.md +1 -0
- data/app/pb_kits/playbook/pb_typeahead/docs/example.yml +1 -0
- data/app/pb_kits/playbook/pb_typeahead/docs/index.js +1 -0
- data/dist/chunks/_typeahead-CRW6dJbW.js +22 -0
- data/dist/chunks/_weekday_stacked-fRmhq4xQ.js +45 -0
- data/dist/chunks/lazysizes-B7xYodB-.js +1 -0
- data/dist/chunks/lib-D5R1BjUn.js +29 -0
- data/dist/chunks/{pb_form_validation-BioH7DWv.js → pb_form_validation-BZ2AVAi_.js} +1 -1
- data/dist/chunks/vendor.js +1 -1
- data/dist/menu.yml +1 -1
- data/dist/playbook-doc.js +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/kit_base.rb +3 -3
- data/lib/playbook/version.rb +2 -2
- metadata +54 -8
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_default.html.erb +0 -10
- data/dist/chunks/_typeahead-D62OcwsT.js +0 -22
- data/dist/chunks/_weekday_stacked-Ceh9N0ow.js +0 -45
- data/dist/chunks/lazysizes-DHz07jlL.js +0 -1
- data/dist/chunks/lib-CeKZrPmu.js +0 -29
@@ -10,6 +10,7 @@ import { globalProps } from "../../utilities/globalProps";
|
|
10
10
|
import { useHandleOnKeyDown } from "../hooks/useHandleOnKeydown";
|
11
11
|
|
12
12
|
import DropdownContext from "../context";
|
13
|
+
import MultiSelectTriggerDisplay from "./MultiSelectTriggerDisplay";
|
13
14
|
|
14
15
|
import Body from "../../pb_body/_body";
|
15
16
|
import Icon from "../../pb_icon/_icon";
|
@@ -43,14 +44,18 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
43
44
|
|
44
45
|
const {
|
45
46
|
autocomplete,
|
47
|
+
closeOnSelection,
|
46
48
|
filterItem,
|
49
|
+
handleBackspace,
|
47
50
|
handleChange,
|
48
51
|
handleWrapperClick,
|
49
52
|
inputRef,
|
50
53
|
inputWrapperRef,
|
51
54
|
isDropDownClosed,
|
52
55
|
isInputFocused,
|
56
|
+
multiSelect,
|
53
57
|
selected,
|
58
|
+
setIsDropDownClosed,
|
54
59
|
setIsInputFocused,
|
55
60
|
toggleDropdown,
|
56
61
|
} = useContext(DropdownContext);
|
@@ -69,11 +74,21 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
69
74
|
const triggerWrapperClasses = buildCss(
|
70
75
|
"dropdown_trigger_wrapper",
|
71
76
|
isInputFocused && "focus",
|
72
|
-
!autocomplete && "select_only"
|
77
|
+
!autocomplete && !multiSelect && "select_only"
|
73
78
|
);
|
74
79
|
|
80
|
+
const selectedArray = Array.isArray(selected)
|
81
|
+
? selected
|
82
|
+
: selected && Object.keys(selected).length
|
83
|
+
? [selected]
|
84
|
+
: [];
|
85
|
+
|
86
|
+
const joinedLabels = multiSelect
|
87
|
+
? ""
|
88
|
+
: selectedArray.map((option) => option.label).join(", ");
|
89
|
+
|
75
90
|
const customDisplayPlaceholder = selected?.label ? (
|
76
|
-
|
91
|
+
""
|
77
92
|
) : autocomplete ? (
|
78
93
|
""
|
79
94
|
) : placeholder ? (
|
@@ -82,19 +97,34 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
82
97
|
"Select..."
|
83
98
|
);
|
84
99
|
|
85
|
-
const defaultDisplayPlaceholder =
|
86
|
-
?
|
100
|
+
const defaultDisplayPlaceholder = joinedLabels
|
101
|
+
? joinedLabels
|
87
102
|
: autocomplete
|
88
103
|
? ""
|
89
104
|
: placeholder
|
90
105
|
? placeholder
|
91
106
|
: "Select...";
|
92
107
|
|
108
|
+
// Click handler that respects closeOnSelection
|
109
|
+
const handleInputClick = (e: React.MouseEvent) => {
|
110
|
+
e.stopPropagation(); // keep the wrapper's handler from firing
|
111
|
+
if (isDropDownClosed) {
|
112
|
+
// Always open if closed
|
113
|
+
setIsDropDownClosed(false);
|
114
|
+
} else if (!closeOnSelection) {
|
115
|
+
// Keep open if closeOnSelection is false
|
116
|
+
return;
|
117
|
+
} else {
|
118
|
+
// Default behavior - toggle
|
119
|
+
toggleDropdown();
|
120
|
+
}
|
121
|
+
};
|
122
|
+
|
93
123
|
return (
|
94
|
-
<div {...ariaProps}
|
95
|
-
{...dataProps}
|
124
|
+
<div {...ariaProps}
|
125
|
+
{...dataProps}
|
96
126
|
{...htmlProps}
|
97
|
-
className={classes}
|
127
|
+
className={classes}
|
98
128
|
id={id}
|
99
129
|
>
|
100
130
|
{
|
@@ -125,34 +155,65 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
125
155
|
paddingX="sm"
|
126
156
|
paddingY="xs"
|
127
157
|
>
|
128
|
-
<FlexItem>
|
129
|
-
<Flex align="center"
|
158
|
+
<FlexItem fixedSize={multiSelect ? "85%" : ""}>
|
159
|
+
<Flex align="center"
|
160
|
+
wrap
|
161
|
+
>
|
130
162
|
{customDisplay ? (
|
131
163
|
<Flex align="center">
|
132
164
|
{customDisplay}
|
133
|
-
<Body dark={dark}
|
134
|
-
paddingLeft={`${
|
165
|
+
<Body dark={dark}
|
166
|
+
paddingLeft={`${joinedLabels ? "xs" : "none"}`}
|
135
167
|
>
|
136
168
|
{customDisplayPlaceholder}
|
137
169
|
</Body>
|
138
170
|
</Flex>
|
139
171
|
) : (
|
140
|
-
|
141
|
-
|
142
|
-
|
172
|
+
multiSelect ? (
|
173
|
+
<>
|
174
|
+
<MultiSelectTriggerDisplay
|
175
|
+
autocomplete={autocomplete}
|
176
|
+
dark={dark}
|
177
|
+
placeholder={placeholder}
|
178
|
+
selected={selectedArray}
|
179
|
+
/>
|
180
|
+
{autocomplete && (
|
181
|
+
<input
|
182
|
+
className="dropdown_input"
|
183
|
+
onChange={handleChange}
|
184
|
+
onClick={handleInputClick}
|
185
|
+
onFocus={() => setIsInputFocused(true)}
|
186
|
+
onKeyDown={(e) => {
|
187
|
+
handleKeyDown(e);
|
188
|
+
e.stopPropagation(); //Fixes issue with keyboard accessibility
|
189
|
+
}}
|
190
|
+
placeholder={
|
191
|
+
joinedLabels
|
192
|
+
? ""
|
193
|
+
: placeholder
|
194
|
+
? placeholder
|
195
|
+
: "Select..."
|
196
|
+
}
|
197
|
+
ref={inputRef}
|
198
|
+
value={filterItem}
|
199
|
+
/>
|
200
|
+
)}
|
201
|
+
</>
|
202
|
+
) : (
|
203
|
+
<Body dark={dark}
|
204
|
+
text={defaultDisplayPlaceholder}
|
205
|
+
/>
|
206
|
+
)
|
143
207
|
)}
|
144
|
-
{autocomplete && (
|
208
|
+
{autocomplete && !multiSelect && (
|
145
209
|
<input
|
146
210
|
className="dropdown_input"
|
147
211
|
onChange={handleChange}
|
148
|
-
onClick={
|
149
|
-
e.stopPropagation();// keep the wrapper’s handler from firing
|
150
|
-
toggleDropdown();
|
151
|
-
}}
|
212
|
+
onClick={handleInputClick}
|
152
213
|
onFocus={() => setIsInputFocused(true)}
|
153
214
|
onKeyDown={handleKeyDown}
|
154
215
|
placeholder={
|
155
|
-
|
216
|
+
joinedLabels
|
156
217
|
? ""
|
157
218
|
: placeholder
|
158
219
|
? placeholder
|
@@ -164,7 +225,9 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
164
225
|
)}
|
165
226
|
</Flex>
|
166
227
|
</FlexItem>
|
228
|
+
<FlexItem>
|
167
229
|
<Body
|
230
|
+
alignItems="center"
|
168
231
|
dark={dark}
|
169
232
|
display="flex"
|
170
233
|
htmlOptions={{
|
@@ -172,6 +235,19 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
172
235
|
}}
|
173
236
|
key={`${isDropDownClosed ? "chevron-down" : "chevron-up"}`}
|
174
237
|
>
|
238
|
+
{
|
239
|
+
selectedArray.length > 0 && (
|
240
|
+
<div onClick={(e)=>{e.stopPropagation();handleBackspace()}}>
|
241
|
+
<Icon
|
242
|
+
cursor="pointer"
|
243
|
+
dark={dark}
|
244
|
+
icon="times"
|
245
|
+
paddingRight="xs"
|
246
|
+
size="sm"
|
247
|
+
/>
|
248
|
+
</div>
|
249
|
+
)
|
250
|
+
}
|
175
251
|
<Icon
|
176
252
|
cursor="pointer"
|
177
253
|
dark={dark}
|
@@ -179,6 +255,7 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
179
255
|
size="sm"
|
180
256
|
/>
|
181
257
|
</Body>
|
258
|
+
</FlexItem>
|
182
259
|
</Flex>
|
183
260
|
</>
|
184
261
|
)
|
@@ -0,0 +1,58 @@
|
|
1
|
+
import React, { useContext } from "react";
|
2
|
+
import FormPill from "../../pb_form_pill/_form_pill";
|
3
|
+
import Flex from "../../pb_flex/_flex";
|
4
|
+
import Body from "../../pb_body/_body";
|
5
|
+
import { GenericObject } from "../../types";
|
6
|
+
import DropdownContext
|
7
|
+
from "../context";
|
8
|
+
type MultiSelectTriggerDisplayProps = {
|
9
|
+
autocomplete?: boolean;
|
10
|
+
selected: GenericObject[];
|
11
|
+
placeholder?: string;
|
12
|
+
dark?: boolean;
|
13
|
+
};
|
14
|
+
|
15
|
+
const MultiSelectTriggerDisplay = ({
|
16
|
+
autocomplete,
|
17
|
+
selected,
|
18
|
+
placeholder,
|
19
|
+
dark = false,
|
20
|
+
}: MultiSelectTriggerDisplayProps) => {
|
21
|
+
|
22
|
+
const { setSelected, onSelect, formPillProps } = useContext(DropdownContext);
|
23
|
+
|
24
|
+
if (selected.length === 0) {
|
25
|
+
if (autocomplete) return null;
|
26
|
+
return (
|
27
|
+
<Body dark={dark}
|
28
|
+
text={placeholder ? placeholder : "Select..."}
|
29
|
+
/>
|
30
|
+
)
|
31
|
+
}
|
32
|
+
|
33
|
+
const handleRemoveIconClick = (option: GenericObject) => {
|
34
|
+
setSelected((prev: GenericObject[]) => {
|
35
|
+
const next = prev.filter((item) => item.label !== option.label);
|
36
|
+
onSelect && onSelect(next);
|
37
|
+
return next;
|
38
|
+
});
|
39
|
+
}
|
40
|
+
|
41
|
+
return (
|
42
|
+
<Flex wrap>
|
43
|
+
{selected.map((option, i) => (
|
44
|
+
<FormPill
|
45
|
+
dark={dark}
|
46
|
+
key={i}
|
47
|
+
marginRight="xs"
|
48
|
+
onClick={(e)=>{e.stopPropagation();handleRemoveIconClick(option)}}
|
49
|
+
tabIndex={0}
|
50
|
+
text={option.label}
|
51
|
+
{...formPillProps}
|
52
|
+
/>
|
53
|
+
))}
|
54
|
+
</Flex>
|
55
|
+
);
|
56
|
+
};
|
57
|
+
|
58
|
+
export default MultiSelectTriggerDisplay;
|
@@ -9,10 +9,23 @@
|
|
9
9
|
border: none;
|
10
10
|
width: 0;
|
11
11
|
}
|
12
|
+
&.error {
|
13
|
+
[class^='pb_card_kit'] {
|
14
|
+
border-color: $error;
|
15
|
+
}
|
16
|
+
[class^='pb_body_kit'][status="negative"] {
|
17
|
+
margin-top: $space_xs;
|
18
|
+
}
|
19
|
+
}
|
12
20
|
}
|
13
21
|
|
14
22
|
.dark [class*='pb_file_upload_kit'] {
|
15
23
|
[class*='pb_card_kit'] {
|
16
24
|
border: 1px $text_dk_lighter dashed;
|
17
25
|
}
|
26
|
+
&.error {
|
27
|
+
[class^='pb_card_kit'] {
|
28
|
+
border-color: $error_dark;
|
29
|
+
}
|
30
|
+
}
|
18
31
|
}
|
@@ -22,6 +22,7 @@ type FileUploadProps = {
|
|
22
22
|
maxSize?: number,
|
23
23
|
onFilesAccepted: Callback<File, File>,
|
24
24
|
onFilesRejected: (error: string, files: readonly FileRejection[]) => void,
|
25
|
+
error?: string,
|
25
26
|
}
|
26
27
|
|
27
28
|
const getFormattedFileSize = (fileSize: number): string => {
|
@@ -36,6 +37,7 @@ const FileUpload = (props: FileUploadProps): React.ReactElement => {
|
|
36
37
|
customMessage,
|
37
38
|
dark = false,
|
38
39
|
data = {},
|
40
|
+
error,
|
39
41
|
htmlOptions = {},
|
40
42
|
maxSize,
|
41
43
|
onFilesAccepted = noop,
|
@@ -100,7 +102,7 @@ const FileUpload = (props: FileUploadProps): React.ReactElement => {
|
|
100
102
|
|
101
103
|
return (
|
102
104
|
<div
|
103
|
-
className={classnames(buildCss('pb_file_upload_kit'), globalProps(props), className)}
|
105
|
+
className={classnames(buildCss('pb_file_upload_kit'), { 'error': error }, globalProps(props), className)}
|
104
106
|
{...dataProps}
|
105
107
|
{...htmlProps}
|
106
108
|
{...getRootProps()}
|
@@ -118,6 +120,14 @@ const FileUpload = (props: FileUploadProps): React.ReactElement => {
|
|
118
120
|
}
|
119
121
|
</Body>
|
120
122
|
</Card>
|
123
|
+
{error && (
|
124
|
+
<Body
|
125
|
+
dark={dark}
|
126
|
+
marginTop="xxs"
|
127
|
+
status="negative"
|
128
|
+
text={error}
|
129
|
+
/>
|
130
|
+
)}
|
121
131
|
</div>
|
122
132
|
)
|
123
133
|
}
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= pb_rails("file_upload", props: {id: "error", error: raw(pb_rails("icon", props: { icon: "warning" }) + " Please upload a valid file")}) %>
|
@@ -0,0 +1,41 @@
|
|
1
|
+
import React, { useState } from 'react'
|
2
|
+
import FileUpload from '../_file_upload'
|
3
|
+
import List from '../../pb_list/_list'
|
4
|
+
import ListItem from '../../pb_list/_list_item'
|
5
|
+
import Icon from '../../pb_icon/_icon'
|
6
|
+
|
7
|
+
const AcceptedFilesList = ({ files }) => (
|
8
|
+
<List>
|
9
|
+
{files.map((file) => (
|
10
|
+
<ListItem key={file.name}>{file.name}</ListItem>
|
11
|
+
))}
|
12
|
+
</List>
|
13
|
+
)
|
14
|
+
|
15
|
+
const FileUploadError = (props) => {
|
16
|
+
const [filesToUpload, setFilesToUpload] = useState([])
|
17
|
+
|
18
|
+
const handleOnFilesAccepted = (files) => {
|
19
|
+
setFilesToUpload([...filesToUpload, ...files])
|
20
|
+
}
|
21
|
+
|
22
|
+
const error = (<>
|
23
|
+
<Icon icon="warning" /> Please upload a valid file
|
24
|
+
</>)
|
25
|
+
|
26
|
+
return (
|
27
|
+
<div>
|
28
|
+
<AcceptedFilesList
|
29
|
+
files={filesToUpload}
|
30
|
+
{...props}
|
31
|
+
/>
|
32
|
+
<FileUpload
|
33
|
+
error={error}
|
34
|
+
onFilesAccepted={handleOnFilesAccepted}
|
35
|
+
{...props}
|
36
|
+
/>
|
37
|
+
</div>
|
38
|
+
)
|
39
|
+
}
|
40
|
+
|
41
|
+
export default FileUploadError
|
@@ -3,6 +3,7 @@ examples:
|
|
3
3
|
rails:
|
4
4
|
- file_upload_default: File Upload
|
5
5
|
- file_upload_custom: Custom
|
6
|
+
- file_upload_error: Error
|
6
7
|
|
7
8
|
react:
|
8
9
|
- file_upload_default: Default List of files to upload
|
@@ -10,3 +11,4 @@ examples:
|
|
10
11
|
- file_upload_custom_message: Add a custom message
|
11
12
|
- file_upload_custom_description: Add your one accepted files description
|
12
13
|
- file_upload_max_size: Set a file size limit
|
14
|
+
- file_upload_error: Error
|
@@ -3,3 +3,4 @@ export { default as FileUploadAccept } from './_file_upload_accept.jsx'
|
|
3
3
|
export { default as FileUploadCustomMessage } from './_file_upload_custom_message.jsx'
|
4
4
|
export { default as FileUploadCustomDescription } from './_file_upload_custom_description.jsx'
|
5
5
|
export { default as FileUploadMaxSize } from './_file_upload_max_size.jsx'
|
6
|
+
export { default as FileUploadError } from './_file_upload_error.jsx'
|
@@ -21,14 +21,20 @@ module Playbook
|
|
21
21
|
prop :input_options, type: Playbook::Props::HashProp,
|
22
22
|
default: {}
|
23
23
|
|
24
|
+
prop :error, type: Playbook::Props::String
|
25
|
+
|
24
26
|
def classname
|
25
27
|
file_upload_class = generate_classname("pb_file_upload_kit")
|
26
|
-
file_upload_class + full_width_class
|
28
|
+
file_upload_class + error_class + full_width_class
|
27
29
|
end
|
28
30
|
|
29
31
|
def full_width_class
|
30
32
|
full_width ? " full_width" : ""
|
31
33
|
end
|
34
|
+
|
35
|
+
def error_class
|
36
|
+
error.present? ? "_error" : ""
|
37
|
+
end
|
32
38
|
end
|
33
39
|
end
|
34
40
|
end
|
@@ -50,3 +50,21 @@ test('displays custom message', () => {
|
|
50
50
|
const kit = screen.getByTestId(testid)
|
51
51
|
expect(kit).toHaveTextContent('Hello world!')
|
52
52
|
})
|
53
|
+
|
54
|
+
test('handles error state with error prop', () => {
|
55
|
+
const errorText = "Test error message"
|
56
|
+
render(
|
57
|
+
<FileUpload
|
58
|
+
data={{ testid: testid }}
|
59
|
+
error={errorText}
|
60
|
+
/>
|
61
|
+
)
|
62
|
+
|
63
|
+
const kit = screen.getByTestId(testid)
|
64
|
+
|
65
|
+
expect(kit).toHaveClass('pb_file_upload_kit')
|
66
|
+
expect(kit).toHaveClass('error')
|
67
|
+
|
68
|
+
const errorElement = screen.getByText(errorText)
|
69
|
+
expect(errorElement).toBeInTheDocument()
|
70
|
+
})
|
@@ -99,6 +99,7 @@
|
|
99
99
|
<%= form.url_field :example_url_field, props: { label: true } %>
|
100
100
|
<%= form.text_area :example_text_area, props: { label: true } %>
|
101
101
|
<%= form.dropdown_field :example_dropdown, props: { label: true, options: example_dropdown_options } %>
|
102
|
+
<%= form.dropdown_field :example_dropdown_multi, props: { label: true, options: example_dropdown_options, multi_select: true } %>
|
102
103
|
<%= form.select :example_select, [ ["Yes", 1], ["No", 2] ], props: { label: true } %>
|
103
104
|
<%= form.collection_select :example_collection_select, example_collection, :value, :name, props: { label: true } %>
|
104
105
|
<%= form.check_box :example_checkbox,
|
@@ -98,6 +98,7 @@
|
|
98
98
|
<%= form.url_field :example_url_field_validation, props: { label: true, required: true } %>
|
99
99
|
<%= form.text_area :example_text_area_validation, props: { label: true, required: true } %>
|
100
100
|
<%= form.dropdown_field :example_dropdown_validation, props: { label: true, options: example_dropdown_options, required: true } %>
|
101
|
+
<%= form.dropdown_field :example_dropdown_validation_multi, props: { label: true, options: example_dropdown_options, multi_select: true, required: true } %>
|
101
102
|
<%= form.select :example_select_validation, [ ["Yes", 1], ["No", 2] ], props: { label: true, blank_selection: "Select One...", required: true, validation_message: "Please, select an option." } %>
|
102
103
|
<%= form.collection_select :example_collection_select_validation, example_collection, :value, :name, props: { label: true, blank_selection: "Select One...", required: true } %>
|
103
104
|
<%= form.check_box :example_checkbox, props: { text: "Example Checkbox", label: true, required: true } %>
|
@@ -23,7 +23,7 @@
|
|
23
23
|
@mixin error-state-right-side-select-kit {
|
24
24
|
&:has(.pb_text_input_kit:not(.error)):has(.pb_text_input_kit_label):has(.pb_select_kit_wrapper.error),
|
25
25
|
&:has(.pb_text_input_kit.error):has(.pb_text_input_kit_label):has(.pb_select_kit_wrapper) {
|
26
|
-
&:not(:has(.pb_phone_number_input)) {
|
26
|
+
&:not(:has(.pb_phone_number_input)):not(:has(.passphrase-text-input)) {
|
27
27
|
align-items: flex-start;
|
28
28
|
|
29
29
|
.pb_select_kit_wrapper {
|
@@ -49,7 +49,7 @@
|
|
49
49
|
|
50
50
|
@mixin error-state-left-side-select-kit {
|
51
51
|
&:has(.pb_select_kit_label):has(.pb_select_kit_wrapper):has(.pb_text_input_kit.error) {
|
52
|
-
&:not(:has(.pb_phone_number_input)) {
|
52
|
+
&:not(:has(.pb_phone_number_input)):not(:has(.passphrase-text-input)) {
|
53
53
|
align-items: flex-start;
|
54
54
|
|
55
55
|
.pb_text_input_kit.error {
|
@@ -20,9 +20,12 @@ $form_pill_colors: map-merge($status_color_text, map-merge($data_colors, $produc
|
|
20
20
|
margin-bottom: 2px;
|
21
21
|
margin-top: 2px;
|
22
22
|
cursor: pointer;
|
23
|
-
.pb_form_pill_text, .
|
23
|
+
.pb_form_pill_text, .pb_form_pill_tag {
|
24
24
|
font-size: $font_small !important;
|
25
25
|
}
|
26
|
+
.pb_form_pill_close {
|
27
|
+
font-size: 17px;
|
28
|
+
}
|
26
29
|
|
27
30
|
&[class*=wrapped] {
|
28
31
|
height: max-content;
|
@@ -96,7 +99,9 @@ $form_pill_colors: map-merge($status_color_text, map-merge($data_colors, $produc
|
|
96
99
|
display: flex;
|
97
100
|
align-items: center;
|
98
101
|
height: 17px;
|
99
|
-
|
102
|
+
width: 17px;
|
103
|
+
justify-content: center;
|
104
|
+
border-radius: 50%;
|
100
105
|
cursor: pointer;
|
101
106
|
@if ($color_name == "neutral") {
|
102
107
|
color: $text_lt_default;
|
@@ -146,8 +151,7 @@ $form_pill_colors: map-merge($status_color_text, map-merge($data_colors, $produc
|
|
146
151
|
outline-offset: -1px;
|
147
152
|
}
|
148
153
|
.pb_form_pill_icon {
|
149
|
-
height:
|
150
|
-
width: 12px !important;
|
154
|
+
height: 0.875em;
|
151
155
|
padding-right: $space_xs;
|
152
156
|
+ .pb_form_pill_text, + .pb_form_pill_tag,
|
153
157
|
+ .pb_tooltip_kit .pb_form_pill_text, + .pb_tooltip_kit .pb_form_pill_tag,
|
@@ -158,7 +162,7 @@ $form_pill_colors: map-merge($status_color_text, map-merge($data_colors, $produc
|
|
158
162
|
&.small {
|
159
163
|
height: 17px;
|
160
164
|
padding: 0 $space-xs;
|
161
|
-
.pb_form_pill_text, .
|
165
|
+
.pb_form_pill_text, .pb_form_pill_tag {
|
162
166
|
font-size: $font_smallest !important;
|
163
167
|
}
|
164
168
|
.pb_form_pill_text, .pb_form_pill_tag {
|
@@ -166,17 +170,20 @@ $form_pill_colors: map-merge($status_color_text, map-merge($data_colors, $produc
|
|
166
170
|
padding: 0 $space_xxs;
|
167
171
|
}
|
168
172
|
.pb_form_pill_close {
|
169
|
-
height:
|
170
|
-
|
173
|
+
height: 14px;
|
174
|
+
width: 14px;
|
175
|
+
font-size: 15px;
|
176
|
+
border-radius: 50%;
|
171
177
|
}
|
172
178
|
[class^=pb_avatar_kit] .avatar_wrapper {
|
173
|
-
flex-basis:
|
174
|
-
height:
|
175
|
-
margin-top:
|
176
|
-
width:
|
177
|
-
&::before { line-height:
|
179
|
+
flex-basis: 14px;
|
180
|
+
height: 14px;
|
181
|
+
margin-top: 3px;
|
182
|
+
width: 14px;
|
183
|
+
&::before { line-height: 15px; }
|
178
184
|
}
|
179
185
|
.pb_form_pill_icon {
|
186
|
+
height: 0.75em;
|
180
187
|
padding-right: $space_xxs;
|
181
188
|
+ .pb_form_pill_text, + .pb_form_pill_tag,
|
182
189
|
+ .pb_tooltip_kit .pb_form_pill_text, + .pb_tooltip_kit .pb_form_pill_tag,
|
@@ -81,6 +81,11 @@ const HomeAddressStreet = (props: HomeAddressStreetProps): React.ReactElement =>
|
|
81
81
|
|
82
82
|
const formatStreetAdr = (address: string): string => preserveCase ? address : titleize(address)
|
83
83
|
|
84
|
+
const uppercaseState = state?.toUpperCase() ?? ''
|
85
|
+
|
86
|
+
const fields = [address, addressCont, city, homeId, homeUrl, houseStyle, state, territory, zipcode]
|
87
|
+
const hasAllEmptyProps = fields.every(field => field === undefined || field === null || field === '')
|
88
|
+
|
84
89
|
return (
|
85
90
|
<div
|
86
91
|
className={classes(className, dark)}
|
@@ -88,7 +93,8 @@ const HomeAddressStreet = (props: HomeAddressStreetProps): React.ReactElement =>
|
|
88
93
|
{...dataProps}
|
89
94
|
{...htmlProps}
|
90
95
|
>
|
91
|
-
{
|
96
|
+
{hasAllEmptyProps && '—'}
|
97
|
+
{emphasis == 'street' && !hasAllEmptyProps &&
|
92
98
|
<div>
|
93
99
|
<Title
|
94
100
|
className="pb_home_address_street_address"
|
@@ -105,11 +111,11 @@ const HomeAddressStreet = (props: HomeAddressStreetProps): React.ReactElement =>
|
|
105
111
|
{titleize(addressCont)}
|
106
112
|
</Title>
|
107
113
|
<Body color="light">
|
108
|
-
{`${titleize(city)}, ${
|
114
|
+
{`${city ? `${titleize(city)}, ` : ''}${uppercaseState}${zipcode ? ` ${zipcode}` : ''}`}
|
109
115
|
</Body>
|
110
116
|
</div>
|
111
117
|
}
|
112
|
-
{emphasis == 'city' &&
|
118
|
+
{emphasis == 'city' && !hasAllEmptyProps &&
|
113
119
|
<div>
|
114
120
|
<Body color="light">
|
115
121
|
{joinPresent([formatStreetAdr(address), houseStyle], ' · ')}
|
@@ -122,18 +128,18 @@ const HomeAddressStreet = (props: HomeAddressStreetProps): React.ReactElement =>
|
|
122
128
|
size={4}
|
123
129
|
tag="span"
|
124
130
|
>
|
125
|
-
{`${titleize(city)}, ${
|
131
|
+
{`${city ? `${titleize(city)}, ` : ''}${uppercaseState}`}
|
126
132
|
</Title>
|
127
133
|
<Body
|
128
134
|
color="light"
|
129
135
|
tag="span"
|
130
136
|
>
|
131
|
-
{` ${zipcode}`}
|
137
|
+
{` ${zipcode ?? ''}`}
|
132
138
|
</Body>
|
133
139
|
</div>
|
134
140
|
</div>
|
135
141
|
}
|
136
|
-
{emphasis == 'none' &&
|
142
|
+
{emphasis == 'none' && !hasAllEmptyProps &&
|
137
143
|
<div>
|
138
144
|
<Body dark={dark}>
|
139
145
|
{joinPresent([formatStreetAdr(address), houseStyle], ' · ')}
|
@@ -144,7 +150,7 @@ const HomeAddressStreet = (props: HomeAddressStreetProps): React.ReactElement =>
|
|
144
150
|
color="light"
|
145
151
|
dark={dark}
|
146
152
|
>
|
147
|
-
{`${titleize(city)}, ${
|
153
|
+
{`${city ? `${titleize(city)}, ` : ''}${uppercaseState}${zipcode ? ` ${zipcode}` : ''}`}
|
148
154
|
</Body>
|
149
155
|
</div>
|
150
156
|
</div>
|
@@ -195,8 +195,8 @@ const MultiLevelSelect = forwardRef<HTMLInputElement, MultiLevelSelectProps>((pr
|
|
195
195
|
if (!selectedItem.length) {
|
196
196
|
setSingleSelectedItem({ id: [], value: "", item: [] });
|
197
197
|
} else {
|
198
|
-
const { id,
|
199
|
-
setSingleSelectedItem({ id: [id], value, item: selectedItem });
|
198
|
+
const { id, label } = selectedItem[0];
|
199
|
+
setSingleSelectedItem({ id: [id], value: label, item: selectedItem });
|
200
200
|
}
|
201
201
|
}
|
202
202
|
}
|