playbook_ui 13.25.0 β†’ 13.26.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (193) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/index.js +1 -0
  3. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.html.erb +10 -14
  4. data/app/pb_kits/playbook/pb_advanced_table/table_body.html.erb +5 -9
  5. data/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb +2 -6
  6. data/app/pb_kits/playbook/pb_advanced_table/table_row.html.erb +2 -6
  7. data/app/pb_kits/playbook/pb_avatar_action_button/avatar_action_button.html.erb +1 -6
  8. data/app/pb_kits/playbook/pb_background/background.html.erb +2 -11
  9. data/app/pb_kits/playbook/pb_badge/badge.html.erb +1 -6
  10. data/app/pb_kits/playbook/pb_bar_graph/_bar_graph.tsx +1 -1
  11. data/app/pb_kits/playbook/pb_body/_body.tsx +1 -1
  12. data/app/pb_kits/playbook/pb_body/body.html.erb +1 -6
  13. data/app/pb_kits/playbook/pb_bread_crumbs/bread_crumb_item.html.erb +1 -6
  14. data/app/pb_kits/playbook/pb_bread_crumbs/bread_crumbs.html.erb +2 -7
  15. data/app/pb_kits/playbook/pb_button/_button.scss +1 -1
  16. data/app/pb_kits/playbook/pb_button/button.html.erb +2 -3
  17. data/app/pb_kits/playbook/pb_button_toolbar/button_toolbar.html.erb +2 -7
  18. data/app/pb_kits/playbook/pb_caption/caption.html.erb +1 -6
  19. data/app/pb_kits/playbook/pb_card/card.html.erb +1 -7
  20. data/app/pb_kits/playbook/pb_card/card_body.html.erb +1 -6
  21. data/app/pb_kits/playbook/pb_card/card_header.html.erb +1 -6
  22. data/app/pb_kits/playbook/pb_checkbox/_checkbox.scss +49 -0
  23. data/app/pb_kits/playbook/pb_checkbox/_checkbox.tsx +3 -0
  24. data/app/pb_kits/playbook/pb_checkbox/checkbox.html.erb +1 -6
  25. data/app/pb_kits/playbook/pb_checkbox/checkbox.rb +2 -1
  26. data/app/pb_kits/playbook/pb_checkbox/checkbox.test.js +14 -0
  27. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_disabled.html.erb +23 -0
  28. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_disabled.jsx +29 -0
  29. data/app/pb_kits/playbook/pb_checkbox/docs/example.yml +2 -0
  30. data/app/pb_kits/playbook/pb_checkbox/docs/index.js +1 -0
  31. data/app/pb_kits/playbook/pb_circle_icon_button/circle_icon_button.html.erb +1 -6
  32. data/app/pb_kits/playbook/pb_collapsible/collapsible.html.erb +1 -6
  33. data/app/pb_kits/playbook/pb_collapsible/collapsible_content.html.erb +1 -6
  34. data/app/pb_kits/playbook/pb_collapsible/collapsible_main.html.erb +1 -7
  35. data/app/pb_kits/playbook/pb_contact/contact.html.erb +1 -6
  36. data/app/pb_kits/playbook/pb_currency/currency.html.erb +1 -6
  37. data/app/pb_kits/playbook/pb_currency/docs/_currency_alignment_swift.md +43 -0
  38. data/app/pb_kits/playbook/pb_currency/docs/_currency_props_swift.md +12 -0
  39. data/app/pb_kits/playbook/pb_currency/docs/_currency_size_swift.md +31 -0
  40. data/app/pb_kits/playbook/pb_currency/docs/example.yml +5 -0
  41. data/app/pb_kits/playbook/pb_dashboard_value/dashboard_value.html.erb +1 -6
  42. data/app/pb_kits/playbook/pb_date/date.html.erb +1 -6
  43. data/app/pb_kits/playbook/pb_date_picker/date_picker.html.erb +2 -6
  44. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_on_change.md +3 -1
  45. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_on_close.md +3 -1
  46. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_range_limit.md +1 -1
  47. data/app/pb_kits/playbook/pb_date_range_inline/date_range_inline.html.erb +1 -5
  48. data/app/pb_kits/playbook/pb_date_range_stacked/date_range_stacked.html.erb +1 -5
  49. data/app/pb_kits/playbook/pb_date_range_stacked/docs/_date_range_stacked_default_swift.md +14 -0
  50. data/app/pb_kits/playbook/pb_date_range_stacked/docs/_date_range_stacked_props_swift.md +9 -0
  51. data/app/pb_kits/playbook/pb_date_range_stacked/docs/example.yml +4 -0
  52. data/app/pb_kits/playbook/pb_date_stacked/date_stacked.html.erb +1 -5
  53. data/app/pb_kits/playbook/pb_date_time/date_time.html.erb +1 -6
  54. data/app/pb_kits/playbook/pb_date_time_stacked/date_time_stacked.html.erb +1 -7
  55. data/app/pb_kits/playbook/pb_date_year_stacked/date_year_stacked.html.erb +1 -5
  56. data/app/pb_kits/playbook/pb_detail/detail.html.erb +1 -6
  57. data/app/pb_kits/playbook/pb_dialog/_dialog.scss +4 -2
  58. data/app/pb_kits/playbook/pb_dialog/dialog.html.erb +1 -6
  59. data/app/pb_kits/playbook/pb_dialog/dialog_body.html.erb +2 -7
  60. data/app/pb_kits/playbook/pb_dialog/dialog_footer.html.erb +1 -5
  61. data/app/pb_kits/playbook/pb_dialog/dialog_header.html.erb +2 -6
  62. data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +100 -34
  63. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +93 -26
  64. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_default.jsx +4 -22
  65. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_default.md +1 -0
  66. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_subcomponent_structure.jsx +42 -0
  67. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_subcomponent_structure.md +7 -0
  68. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_autocomplete.jsx +84 -0
  69. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_autocomplete.md +1 -0
  70. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_autocomplete_and_custom_display.jsx +101 -0
  71. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_autocomplete_and_custom_display.md +1 -0
  72. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_display.jsx +5 -3
  73. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_display.md +5 -0
  74. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_options.jsx +6 -9
  75. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_options.md +1 -0
  76. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_padding.jsx +48 -0
  77. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_padding.md +1 -0
  78. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_trigger.jsx +5 -5
  79. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_trigger.md +1 -0
  80. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_external_control.jsx +59 -0
  81. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_hook.jsx +72 -0
  82. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_label.jsx +39 -0
  83. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_label.md +1 -0
  84. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +7 -0
  85. data/app/pb_kits/playbook/pb_dropdown/docs/index.js +7 -0
  86. data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +200 -10
  87. data/app/pb_kits/playbook/pb_dropdown/hooks/useDropdown.tsx +2 -2
  88. data/app/pb_kits/playbook/pb_dropdown/hooks/useHandleOnKeydown.tsx +14 -9
  89. data/app/pb_kits/playbook/pb_dropdown/scss_partials/_dropdown_animation.scss +18 -0
  90. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownContainer.tsx +22 -8
  91. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownOption.tsx +56 -29
  92. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +130 -68
  93. data/app/pb_kits/playbook/pb_dropdown/utilities/clickOutsideHelper.tsx +41 -0
  94. data/app/pb_kits/playbook/pb_dropdown/utilities/index.ts +2 -0
  95. data/app/pb_kits/playbook/pb_dropdown/utilities/subComponentHelper.tsx +9 -7
  96. data/app/pb_kits/playbook/pb_file_upload/file_upload.html.erb +1 -6
  97. data/app/pb_kits/playbook/pb_filter/filter.html.erb +1 -5
  98. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/fixed_confirmation_toast.html.erb +1 -6
  99. data/app/pb_kits/playbook/pb_flex/flex.html.erb +1 -5
  100. data/app/pb_kits/playbook/pb_flex/flex_item.html.erb +2 -6
  101. data/app/pb_kits/playbook/pb_form_group/form_group.html.erb +1 -6
  102. data/app/pb_kits/playbook/pb_form_pill/form_pill.html.erb +1 -1
  103. data/app/pb_kits/playbook/pb_hashtag/hashtag.html.erb +1 -6
  104. data/app/pb_kits/playbook/pb_highlight/highlight.html.erb +1 -5
  105. data/app/pb_kits/playbook/pb_home_address_street/home_address_street.html.erb +1 -5
  106. data/app/pb_kits/playbook/pb_icon_circle/icon_circle.html.erb +2 -7
  107. data/app/pb_kits/playbook/pb_icon_stat_value/icon_stat_value.html.erb +1 -6
  108. data/app/pb_kits/playbook/pb_icon_value/icon_value.html.erb +1 -6
  109. data/app/pb_kits/playbook/pb_label_pill/label_pill.html.erb +1 -6
  110. data/app/pb_kits/playbook/pb_label_value/label_value.html.erb +1 -6
  111. data/app/pb_kits/playbook/pb_layout/body.html.erb +1 -5
  112. data/app/pb_kits/playbook/pb_layout/footer.html.erb +1 -5
  113. data/app/pb_kits/playbook/pb_layout/header.html.erb +1 -5
  114. data/app/pb_kits/playbook/pb_layout/item.html.erb +1 -5
  115. data/app/pb_kits/playbook/pb_layout/layout.html.erb +1 -5
  116. data/app/pb_kits/playbook/pb_layout/sidebar.html.erb +1 -5
  117. data/app/pb_kits/playbook/pb_list/item.html.erb +2 -8
  118. data/app/pb_kits/playbook/pb_list/list.html.erb +2 -8
  119. data/app/pb_kits/playbook/pb_loading_inline/loading_inline.html.erb +1 -6
  120. data/app/pb_kits/playbook/pb_message/message.html.erb +1 -6
  121. data/app/pb_kits/playbook/pb_message/message_mention.html.erb +1 -6
  122. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.html.erb +1 -6
  123. data/app/pb_kits/playbook/pb_multiple_users/multiple_users.html.erb +1 -6
  124. data/app/pb_kits/playbook/pb_multiple_users_stacked/multiple_users_stacked.html.erb +1 -6
  125. data/app/pb_kits/playbook/pb_nav/item.html.erb +3 -14
  126. data/app/pb_kits/playbook/pb_nav/nav.html.erb +1 -6
  127. data/app/pb_kits/playbook/pb_online_status/online_status.html.erb +2 -6
  128. data/app/pb_kits/playbook/pb_pagination/pagination.html.erb +1 -6
  129. data/app/pb_kits/playbook/pb_passphrase/passphrase.html.erb +1 -1
  130. data/app/pb_kits/playbook/pb_person/person.html.erb +7 -12
  131. data/app/pb_kits/playbook/pb_person_contact/person_contact.html.erb +1 -6
  132. data/app/pb_kits/playbook/pb_pill/pill.html.erb +1 -6
  133. data/app/pb_kits/playbook/pb_popover/popover.html.erb +1 -6
  134. data/app/pb_kits/playbook/pb_progress_pills/progress_pills.html.erb +2 -6
  135. data/app/pb_kits/playbook/pb_progress_simple/docs/_progress_simple_flex.html.erb +3 -0
  136. data/app/pb_kits/playbook/pb_progress_simple/docs/_progress_simple_flex.jsx +16 -0
  137. data/app/pb_kits/playbook/pb_progress_simple/docs/_progress_simple_flex.md +1 -0
  138. data/app/pb_kits/playbook/pb_progress_simple/docs/example.yml +2 -0
  139. data/app/pb_kits/playbook/pb_progress_simple/docs/index.js +1 -0
  140. data/app/pb_kits/playbook/pb_progress_simple/progress_simple.html.erb +3 -6
  141. data/app/pb_kits/playbook/pb_progress_simple/progress_simple.rb +1 -1
  142. data/app/pb_kits/playbook/pb_progress_step/progress_step.html.erb +1 -5
  143. data/app/pb_kits/playbook/pb_progress_step/progress_step_item.html.erb +1 -5
  144. data/app/pb_kits/playbook/pb_radio/_radio.scss +35 -0
  145. data/app/pb_kits/playbook/pb_radio/_radio.tsx +3 -0
  146. data/app/pb_kits/playbook/pb_radio/docs/_radio_alignment.jsx +4 -1
  147. data/app/pb_kits/playbook/pb_radio/docs/_radio_default.jsx +4 -1
  148. data/app/pb_kits/playbook/pb_radio/docs/_radio_disabled.html.erb +26 -0
  149. data/app/pb_kits/playbook/pb_radio/docs/_radio_disabled.jsx +31 -0
  150. data/app/pb_kits/playbook/pb_radio/docs/_radio_error.jsx +2 -1
  151. data/app/pb_kits/playbook/pb_radio/docs/example.yml +2 -0
  152. data/app/pb_kits/playbook/pb_radio/docs/index.js +1 -0
  153. data/app/pb_kits/playbook/pb_radio/radio.html.erb +2 -8
  154. data/app/pb_kits/playbook/pb_radio/radio.rb +5 -0
  155. data/app/pb_kits/playbook/pb_radio/radio.test.js +17 -0
  156. data/app/pb_kits/playbook/pb_section_separator/_section_separator.scss +6 -2
  157. data/app/pb_kits/playbook/pb_section_separator/_section_separator_mixin.scss +11 -1
  158. data/app/pb_kits/playbook/pb_section_separator/section_separator.html.erb +1 -6
  159. data/app/pb_kits/playbook/pb_select/select.html.erb +1 -5
  160. data/app/pb_kits/playbook/pb_selectable_card/selectable_card.html.erb +1 -5
  161. data/app/pb_kits/playbook/pb_selectable_card_icon/selectable_card_icon.html.erb +1 -4
  162. data/app/pb_kits/playbook/pb_selectable_icon/selectable_icon.html.erb +1 -5
  163. data/app/pb_kits/playbook/pb_selectable_list/selectable_list.html.erb +1 -6
  164. data/app/pb_kits/playbook/pb_selectable_list/selectable_list_item.html.erb +1 -6
  165. data/app/pb_kits/playbook/pb_source/source.html.erb +1 -5
  166. data/app/pb_kits/playbook/pb_star_rating/star_rating.html.erb +1 -5
  167. data/app/pb_kits/playbook/pb_stat_change/stat_change.html.erb +1 -5
  168. data/app/pb_kits/playbook/pb_stat_value/stat_value.html.erb +1 -5
  169. data/app/pb_kits/playbook/pb_table/table.html.erb +2 -12
  170. data/app/pb_kits/playbook/pb_table/table_body.html.erb +6 -16
  171. data/app/pb_kits/playbook/pb_table/table_cell.html.erb +6 -16
  172. data/app/pb_kits/playbook/pb_table/table_head.html.erb +6 -16
  173. data/app/pb_kits/playbook/pb_table/table_header.html.erb +4 -13
  174. data/app/pb_kits/playbook/pb_table/table_row.html.erb +6 -16
  175. data/app/pb_kits/playbook/pb_textarea/textarea.html.erb +1 -5
  176. data/app/pb_kits/playbook/pb_time/time.html.erb +1 -5
  177. data/app/pb_kits/playbook/pb_time_range_inline/time_range_inline.html.erb +1 -5
  178. data/app/pb_kits/playbook/pb_time_stacked/time_stacked.html.erb +1 -5
  179. data/app/pb_kits/playbook/pb_timeline/item.html.erb +3 -7
  180. data/app/pb_kits/playbook/pb_timeline/timeline.html.erb +1 -5
  181. data/app/pb_kits/playbook/pb_timestamp/timestamp.html.erb +1 -6
  182. data/app/pb_kits/playbook/pb_title/title.html.erb +1 -6
  183. data/app/pb_kits/playbook/pb_title_count/title_count.html.erb +1 -6
  184. data/app/pb_kits/playbook/pb_title_detail/title_detail.html.erb +1 -5
  185. data/app/pb_kits/playbook/pb_toggle/toggle.html.erb +1 -6
  186. data/app/pb_kits/playbook/pb_tooltip/tooltip.html.erb +1 -5
  187. data/app/pb_kits/playbook/pb_user/user.html.erb +1 -6
  188. data/app/pb_kits/playbook/pb_user_badge/user_badge.html.erb +1 -6
  189. data/app/pb_kits/playbook/pb_weekday_stacked/weekday_stacked.html.erb +1 -6
  190. data/dist/playbook-rails.js +4 -4
  191. data/lib/playbook/kit_base.rb +1 -1
  192. data/lib/playbook/version.rb +2 -2
  193. metadata +33 -2
@@ -1,31 +1,38 @@
1
- import React, { useState, useRef, useEffect, ReactElement } from "react";
1
+ import React, { useState, useRef, useEffect } from "react";
2
2
  import classnames from "classnames";
3
- import { buildAriaProps, buildCss, buildDataProps } from "../utilities/props";
3
+ import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from "../utilities/props";
4
4
  import { globalProps } from "../utilities/globalProps";
5
+ import { GenericObject } from "../types";
5
6
 
6
7
  import Body from "../pb_body/_body";
8
+ import Caption from "../pb_caption/_caption";
7
9
 
8
10
  import DropdownContainer from "./subcomponents/DropdownContainer";
11
+ import DropdownContext from "./context";
9
12
  import DropdownOption from "./subcomponents/DropdownOption";
10
13
  import DropdownTrigger from "./subcomponents/DropdownTrigger";
11
- import DropdownContext from "./context";
12
14
  import useDropdown from "./hooks/useDropdown";
13
15
 
14
16
  import {
15
17
  separateChildComponents,
16
18
  prepareSubcomponents,
17
- } from "./utilities/subComponentHelper";
18
- import { GenericObject } from "../types";
19
+ handleClickOutside,
20
+ } from "./utilities";
19
21
 
20
22
  type DropdownProps = {
21
23
  aria?: { [key: string]: string };
22
24
  autocomplete?: boolean;
25
+ children?: React.ReactChild[] | React.ReactChild | React.ReactElement[];
23
26
  className?: string;
27
+ dark?: boolean;
24
28
  data?: { [key: string]: string };
29
+ htmlOptions?: {[key: string]: string | number | boolean | (() => void)},
25
30
  id?: string;
26
- children?: React.ReactChild[] | React.ReactChild | ReactElement[];
27
- options: GenericObject;
31
+ isClosed?: boolean;
32
+ label?: string;
28
33
  onSelect?: (arg: GenericObject) => null;
34
+ options: GenericObject;
35
+ triggerRef?: any;
29
36
  };
30
37
 
31
38
  const Dropdown = (props: DropdownProps) => {
@@ -34,49 +41,66 @@ const Dropdown = (props: DropdownProps) => {
34
41
  autocomplete = false,
35
42
  children,
36
43
  className,
44
+ dark = false,
37
45
  data = {},
46
+ htmlOptions = {},
38
47
  id,
39
- options,
48
+ isClosed = true,
49
+ label,
40
50
  onSelect,
51
+ options,
52
+ triggerRef
41
53
  } = props;
42
54
 
43
55
  const ariaProps = buildAriaProps(aria);
44
56
  const dataProps = buildDataProps(data);
57
+ const htmlProps = buildHtmlProps(htmlOptions);
45
58
  const classes = classnames(
46
59
  buildCss("pb_dropdown"),
47
60
  globalProps(props),
48
61
  className
49
62
  );
50
63
 
51
- const [isDropDownClosed, setIsDropDownClosed, toggleDropdown] = useDropdown();
64
+ const [isDropDownClosed, setIsDropDownClosed, toggleDropdown] = useDropdown(isClosed);
52
65
 
53
66
  const [filterItem, setFilterItem] = useState("");
54
- const [selected, setSelected] = useState({});
67
+ const [selected, setSelected] = useState<GenericObject>({});
55
68
  const [isInputFocused, setIsInputFocused] = useState(false);
56
69
  const [hasTriggerSubcomponent, setHasTriggerSubcomponent] = useState(true);
57
70
  const [hasContainerSubcomponent, setHasContainerSubcomponent] =
58
71
  useState(true);
59
-
60
72
  //state for keyboard events
61
73
  const [focusedOptionIndex, setFocusedOptionIndex] = useState(-1);
62
74
 
63
75
  const dropdownRef = useRef(null);
64
76
  const inputRef = useRef(null);
77
+ const inputWrapperRef = useRef(null);
78
+ const dropdownContainerRef = useRef(null);
65
79
 
66
80
  const { trigger, container, otherChildren } =
67
81
  separateChildComponents(children);
68
82
 
69
- // useEffect to handle clicks outside the dropdown
70
83
  useEffect(() => {
71
- const handleClickOutside = (e: MouseEvent) => {
72
- if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
73
- setIsDropDownClosed(true);
74
- setIsInputFocused(false);
84
+ // Set the parent element of the trigger to relative to allow for absolute positioning of the dropdown
85
+ //Only needed for when useDropdown hook used with external trigger
86
+ if (triggerRef?.current) {
87
+ const parentElement = triggerRef.current.parentNode;
88
+ if (parentElement) {
89
+ parentElement.style.position = 'relative';
75
90
  }
76
- };
77
- window.addEventListener("click", handleClickOutside);
91
+ }
92
+ // Handle clicks outside the dropdown
93
+ const handleClick = handleClickOutside({
94
+ inputWrapperRef,
95
+ dropdownContainerRef,
96
+ setIsDropDownClosed,
97
+ setFocusedOptionIndex,
98
+ setIsInputFocused,
99
+ });
100
+
101
+ window.addEventListener("click", handleClick);
78
102
  return () => {
79
- window.removeEventListener("click", handleClickOutside);
103
+ window.removeEventListener("click", handleClick);
80
104
  };
81
105
  }, []);
82
106
 
@@ -85,6 +109,30 @@ const Dropdown = (props: DropdownProps) => {
85
109
  setHasContainerSubcomponent(!!container);
86
110
  }, []);
87
111
 
112
+ // dropdown to toggle with external control
113
+ useEffect(()=> {
114
+ setIsDropDownClosed(isClosed)
115
+ },[isClosed])
116
+
117
+ const filteredOptions = options?.filter((option: GenericObject) =>
118
+ option.label.toLowerCase().includes(filterItem.toLowerCase())
119
+ );
120
+
121
+ // For keyboard accessibility: Set focus within dropdown to selected item if it exists
122
+ useEffect(() => {
123
+ if (!isDropDownClosed) {
124
+ let newIndex = 0;
125
+ if (selected && selected?.label) {
126
+ const selectedIndex = filteredOptions.findIndex((option: GenericObject) => option.label === selected.label);
127
+ if (selectedIndex >= 0) {
128
+ newIndex = selectedIndex;
129
+ }
130
+ }
131
+ setFocusedOptionIndex(newIndex);
132
+ }
133
+ }, [isDropDownClosed]);
134
+
135
+
88
136
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
89
137
  setFilterItem(e.target.value);
90
138
  setIsDropDownClosed(false);
@@ -94,7 +142,7 @@ const Dropdown = (props: DropdownProps) => {
94
142
  setSelected(selectedItem);
95
143
  setFilterItem("");
96
144
  setIsDropDownClosed(true);
97
- onSelect(selectedItem);
145
+ onSelect && onSelect(selectedItem);
98
146
  };
99
147
 
100
148
  const handleWrapperClick = () => {
@@ -104,14 +152,10 @@ const Dropdown = (props: DropdownProps) => {
104
152
 
105
153
  const handleBackspace = () => {
106
154
  setSelected({});
107
- onSelect(null);
155
+ onSelect && onSelect(null);
108
156
  setFocusedOptionIndex(-1);
109
157
  };
110
158
 
111
- const filteredOptions = options?.filter((option: GenericObject) =>
112
- option.label.toLowerCase().includes(filterItem.toLowerCase())
113
- );
114
-
115
159
  const componentsToRender = prepareSubcomponents({
116
160
  children,
117
161
  hasTriggerSubcomponent,
@@ -119,17 +163,22 @@ const Dropdown = (props: DropdownProps) => {
119
163
  trigger,
120
164
  container,
121
165
  otherChildren,
166
+ dark
122
167
  });
123
168
 
169
+
124
170
  return (
125
171
  <div {...ariaProps}
126
172
  {...dataProps}
173
+ {...htmlProps}
127
174
  className={classes}
128
175
  id={id}
176
+ style={triggerRef ? { position: "absolute"} : { position: "relative"}}
129
177
  >
130
178
  <DropdownContext.Provider
131
179
  value={{
132
180
  autocomplete,
181
+ dropdownContainerRef,
133
182
  filteredOptions,
134
183
  filterItem,
135
184
  focusedOptionIndex,
@@ -138,6 +187,7 @@ const Dropdown = (props: DropdownProps) => {
138
187
  handleOptionClick,
139
188
  handleWrapperClick,
140
189
  inputRef,
190
+ inputWrapperRef,
141
191
  isDropDownClosed,
142
192
  isInputFocused,
143
193
  options,
@@ -147,9 +197,26 @@ const Dropdown = (props: DropdownProps) => {
147
197
  setIsInputFocused,
148
198
  setSelected,
149
199
  toggleDropdown,
200
+ triggerRef
150
201
  }}
151
202
  >
203
+ {label &&
204
+ <Caption
205
+ dark={dark}
206
+ marginBottom="xs"
207
+ text={label}
208
+ />
209
+ }
152
210
  <div className="dropdown_wrapper"
211
+ onBlur={() => {
212
+ // Debounce to delay the execution to prevent jumpiness in Focus state
213
+ setTimeout(() => {
214
+ if (!dropdownRef.current.contains(document.activeElement)) {
215
+ setIsInputFocused(false);
216
+ }
217
+ }, 0);
218
+ }}
219
+ onFocus={() => setIsInputFocused(true)}
153
220
  ref={dropdownRef}
154
221
  >
155
222
  {children ? (
@@ -176,7 +243,7 @@ const Dropdown = (props: DropdownProps) => {
176
243
  </div>
177
244
  </DropdownContext.Provider>
178
245
  </div>
179
- );
246
+ )
180
247
  };
181
248
 
182
249
  Dropdown.Option = DropdownOption;
@@ -1,31 +1,20 @@
1
- import React, { useState } from 'react'
1
+ import React from 'react'
2
2
  import { Dropdown } from '../../'
3
3
 
4
4
  const DropdownDefault = (props) => {
5
- // eslint-disable-next-line no-unused-vars
6
- const [selectedOption, setSelectedOption] = useState();
7
5
 
8
6
  const options = [
9
7
  {
10
8
  label: "United States",
11
9
  value: "United States",
12
- areaCode: "+1",
13
- icon: "πŸ‡ΊπŸ‡Έ",
14
- id: "United-states"
15
10
  },
16
11
  {
17
- label: "Ukraine",
18
- value: "Ukraine",
19
- areaCode: "+380",
20
- icon: "πŸ‡ΊπŸ‡¦",
21
- id: "ukraine"
12
+ label: "Canada",
13
+ value: "Canada",
22
14
  },
23
15
  {
24
16
  label: "Pakistan",
25
17
  value: "Pakistan",
26
- areaCode: "+92",
27
- icon: "πŸ‡΅πŸ‡°",
28
- id: "pakistan"
29
18
  }
30
19
  ];
31
20
 
@@ -33,16 +22,9 @@ const [selectedOption, setSelectedOption] = useState();
33
22
  return (
34
23
  <div>
35
24
  <Dropdown
36
- onSelect={(selectedItem) => setSelectedOption(selectedItem)}
37
25
  options={options}
38
26
  {...props}
39
- >
40
- {options.map((option) => (
41
- <Dropdown.Option key={option.id}
42
- option={option}
43
- />
44
- ))}
45
- </Dropdown>
27
+ />
46
28
  </div>
47
29
  )
48
30
  }
@@ -0,0 +1 @@
1
+ The Dropdown kit accepts an `options` array and renders each object from that array as a selectable option within a dropdown container. `options` is a required prop and must be an array of objects. Each object can contain as many key/value pairs as needed but MUST contain 'label' and 'value' as the only required items within each object.
@@ -0,0 +1,42 @@
1
+ import React from 'react'
2
+ import { Dropdown } from '../..'
3
+
4
+ const DropdownSubcomponentStructure = (props) => {
5
+
6
+
7
+ const options = [
8
+ {
9
+ label: "United States",
10
+ value: "United States",
11
+ },
12
+ {
13
+ label: "Canada",
14
+ value: "Canada",
15
+ },
16
+ {
17
+ label: "Pakistan",
18
+ value: "Pakistan",
19
+ }
20
+ ];
21
+
22
+
23
+ return (
24
+ <div>
25
+ <Dropdown
26
+ options={options}
27
+ {...props}
28
+ >
29
+ <Dropdown.Trigger/>
30
+ <Dropdown.Container>
31
+ {options.map((option) => (
32
+ <Dropdown.Option key={option.id}
33
+ option={option}
34
+ />
35
+ ))}
36
+ </Dropdown.Container>
37
+ </Dropdown>
38
+ </div>
39
+ )
40
+ }
41
+
42
+ export default DropdownSubcomponentStructure
@@ -0,0 +1,7 @@
1
+ The dropdown comes with the following subcomponents that can be used to achieve various levels of customization:
2
+
3
+ `Dropdown. Trigger`
4
+ `Dropdown.Container`
5
+ `Dropdown.Option`
6
+
7
+ See the code snippet below for a visual on how to use the kit with subcomponents. Each subcomponent allows for GlobalProps in addition to any subcomponent specfic props.
@@ -0,0 +1,84 @@
1
+ import React from 'react'
2
+ import { Dropdown, User, Badge, FlexItem } from '../..'
3
+
4
+ const DropdownWithAutocomplete = (props) => {
5
+
6
+ const options = [
7
+ {
8
+ label: "Jasper Furniss",
9
+ value: "Jasper Furniss",
10
+ territory: "PHL",
11
+ title: "Senior UX Engineer",
12
+ id: "jasper-furniss",
13
+ status: "Offline"
14
+ },
15
+ {
16
+ label: "Ramon Ruiz",
17
+ value: "Ramon Ruiz",
18
+ territory: "PHL",
19
+ title: "Senior UX Desinger",
20
+ id: "ramon-ruiz",
21
+ status: "Away"
22
+ },
23
+ {
24
+ label: "Jason Cypret",
25
+ value: "Jason Cypret",
26
+ territory: "PHL",
27
+ title: "VP of User Experience",
28
+ id: "jason-cypret",
29
+ status: "Online"
30
+ },
31
+ {
32
+ label: "Courtney Long",
33
+ value: "Courtney Long",
34
+ territory: "PHL",
35
+ title: "UX Design Mentor",
36
+ id: "courtney-long",
37
+ status: "Online"
38
+ }
39
+ ];
40
+
41
+
42
+ return (
43
+ <div>
44
+ <Dropdown autocomplete
45
+ options={options}
46
+ {...props}
47
+ >
48
+ {options.map((option) => (
49
+ <Dropdown.Option key={option.id}
50
+ option={option}
51
+ >
52
+ <>
53
+ <FlexItem>
54
+ <User
55
+ align="left"
56
+ avatar
57
+ name={option.label}
58
+ orientation="horizontal"
59
+ territory={option.territory}
60
+ title={option.title}
61
+ />
62
+ </FlexItem>
63
+ <FlexItem>
64
+ <Badge
65
+ rounded
66
+ text={option.status}
67
+ variant={`${
68
+ option.status === "Offline"
69
+ ? "neutral"
70
+ : option.status === "Online"
71
+ ? "success"
72
+ : "warning"
73
+ }`}
74
+ />
75
+ </FlexItem>
76
+ </>
77
+ </Dropdown.Option>
78
+ ))}
79
+ </Dropdown>
80
+ </div>
81
+ )
82
+ }
83
+
84
+ export default DropdownWithAutocomplete
@@ -0,0 +1 @@
1
+ The `autocomplete` prop can be used to add autocomplete or typeahead functionality to the Dropdown's default Trigger. This prop is set to 'false' by default.
@@ -0,0 +1,101 @@
1
+ import React, { useState } from 'react'
2
+ import { Dropdown, User, Badge, FlexItem, Avatar } from '../..'
3
+
4
+ const DropdownWithAutocompleteAndCustomDisplay = (props) => {
5
+ const [selectedOption, setSelectedOption] = useState();
6
+
7
+ const options = [
8
+ {
9
+ label: "Jasper Furniss",
10
+ value: "Jasper Furniss",
11
+ territory: "PHL",
12
+ title: "Senior UX Engineer",
13
+ id: "jasper-furniss",
14
+ status: "Offline"
15
+ },
16
+ {
17
+ label: "Ramon Ruiz",
18
+ value: "Ramon Ruiz",
19
+ territory: "PHL",
20
+ title: "Senior UX Desinger",
21
+ id: "ramon-ruiz",
22
+ status: "Away"
23
+ },
24
+ {
25
+ label: "Jason Cypret",
26
+ value: "Jason Cypret",
27
+ territory: "PHL",
28
+ title: "VP of User Experience",
29
+ id: "jason-cypret",
30
+ status: "Online"
31
+ },
32
+ {
33
+ label: "Courtney Long",
34
+ value: "Courtney Long",
35
+ territory: "PHL",
36
+ title: "UX Design Mentor",
37
+ id: "courtney-long",
38
+ status: "Online"
39
+ }
40
+ ];
41
+
42
+ const CustomDisplay = () => {
43
+ return (
44
+ <>
45
+ {
46
+ selectedOption && (
47
+ <Avatar
48
+ name={selectedOption.label}
49
+ size="xs"
50
+ />
51
+ )
52
+ }
53
+ </>
54
+ )
55
+ };
56
+
57
+ return (
58
+ <div>
59
+ <Dropdown autocomplete
60
+ onSelect={(selectedItem) => setSelectedOption(selectedItem)}
61
+ options={options}
62
+ {...props}
63
+ >
64
+ <Dropdown.Trigger customDisplay={<CustomDisplay/>} />
65
+ {options.map((option) => (
66
+ <Dropdown.Option key={option.id}
67
+ option={option}
68
+ >
69
+ <>
70
+ <FlexItem>
71
+ <User
72
+ align="left"
73
+ avatar
74
+ name={option.label}
75
+ orientation="horizontal"
76
+ territory={option.territory}
77
+ title={option.title}
78
+ />
79
+ </FlexItem>
80
+ <FlexItem>
81
+ <Badge
82
+ rounded
83
+ text={option.status}
84
+ variant={`${
85
+ option.status === "Offline"
86
+ ? "neutral"
87
+ : option.status === "Online"
88
+ ? "success"
89
+ : "warning"
90
+ }`}
91
+ />
92
+ </FlexItem>
93
+ </>
94
+ </Dropdown.Option>
95
+ ))}
96
+ </Dropdown>
97
+ </div>
98
+ )
99
+ }
100
+
101
+ export default DropdownWithAutocompleteAndCustomDisplay
@@ -0,0 +1 @@
1
+ `autocomplete` prop can also be used in conjunction with the `customDisplay` prop.
@@ -39,7 +39,7 @@ const DropdownWithCustomDisplay = (props) => {
39
39
  }
40
40
  ];
41
41
 
42
- const customDisplay = () => {
42
+ const CustomDisplay = () => {
43
43
  return (
44
44
  <>
45
45
  {
@@ -62,7 +62,9 @@ const DropdownWithCustomDisplay = (props) => {
62
62
  options={options}
63
63
  {...props}
64
64
  >
65
- <Dropdown.Trigger customDisplay={customDisplay()}/>
65
+ <Dropdown.Trigger customDisplay={<CustomDisplay/>}
66
+ placeholder="Select a User"
67
+ />
66
68
  {options.map((option) => (
67
69
  <Dropdown.Option key={option.id}
68
70
  option={option}
@@ -79,7 +81,7 @@ const DropdownWithCustomDisplay = (props) => {
79
81
  />
80
82
  </FlexItem>
81
83
  <FlexItem>
82
- <Badge
84
+ <Badge dark
83
85
  rounded
84
86
  text={option.status}
85
87
  variant={`${
@@ -0,0 +1,5 @@
1
+ The `customDisplay` prop can be used to customize the display of the selected item by allowing devs to pass in a component that will be rendered to the left of the default text-based display. In this example the Avatar kit is being used.
2
+
3
+ The `placeholder` prop can also be used to customize the placeholder text for the default Trigger.
4
+
5
+ The `onSelect` prop is a function that gives the dev one argument: the selected option. In this example we are using the `onSelect` to set a state with the selected option and using it to customize the `customDisplay`.
@@ -1,9 +1,7 @@
1
- import React, { useState } from 'react'
1
+ import React from 'react'
2
2
  import { Dropdown, Icon, Body, FlexItem, Flex } from '../..'
3
3
 
4
4
  const DropdownWithCustomOptions = (props) => {
5
- // eslint-disable-next-line no-unused-vars
6
- const [selectedOption, setSelectedOption] = useState();
7
5
 
8
6
  const options = [
9
7
  {
@@ -14,11 +12,11 @@ const DropdownWithCustomOptions = (props) => {
14
12
  id: "United-states"
15
13
  },
16
14
  {
17
- label: "Ukraine",
18
- value: "Ukraine",
19
- areaCode: "+380",
20
- icon: "πŸ‡ΊπŸ‡¦",
21
- id: "ukraine"
15
+ label: "Canada",
16
+ value: "Canada",
17
+ areaCode: "+1",
18
+ icon: "πŸ‡¨πŸ‡¦",
19
+ id: "canada"
22
20
  },
23
21
  {
24
22
  label: "Pakistan",
@@ -33,7 +31,6 @@ const DropdownWithCustomOptions = (props) => {
33
31
  return (
34
32
  <div>
35
33
  <Dropdown
36
- onSelect={(selectedItem) => setSelectedOption(selectedItem)}
37
34
  options={options}
38
35
  {...props}
39
36
  >
@@ -0,0 +1 @@
1
+ The Dropdown also allows for custom options that can be passed in as children to the `Dropdown.Option` subcomponent. If no children are passed to `Dropdown.Option`, the kit will render each option as text within a Body kit by default.
@@ -0,0 +1,48 @@
1
+ import React from 'react'
2
+ import { Dropdown } from '../..'
3
+
4
+ const DropdownWithCustomPadding = (props) => {
5
+
6
+ const options = [
7
+ {
8
+ label: "United States",
9
+ value: "United States",
10
+ areaCode: "+1",
11
+ icon: "πŸ‡ΊπŸ‡Έ",
12
+ id: "United-states"
13
+ },
14
+ {
15
+ label: "Canada",
16
+ value: "Canada",
17
+ areaCode: "+1",
18
+ icon: "πŸ‡¨πŸ‡¦",
19
+ id: "canada"
20
+ },
21
+ {
22
+ label: "Pakistan",
23
+ value: "Pakistan",
24
+ areaCode: "+92",
25
+ icon: "πŸ‡΅πŸ‡°",
26
+ id: "pakistan"
27
+ }
28
+ ];
29
+
30
+
31
+ return (
32
+ <div>
33
+ <Dropdown
34
+ options={options}
35
+ {...props}
36
+ >
37
+ {options.map((option) => (
38
+ <Dropdown.Option key={option.id}
39
+ option={option}
40
+ padding="sm"
41
+ />
42
+ ))}
43
+ </Dropdown>
44
+ </div>
45
+ )
46
+ }
47
+
48
+ export default DropdownWithCustomPadding
@@ -0,0 +1 @@
1
+ By default, the padding on each option in the dropdown is set to `xs`. The `padding` Global Props however can be used to override this default. In this example, we are setting padding to `sm`.
@@ -14,11 +14,11 @@ const [selectedOption, setSelectedOption] = useState();
14
14
  id: "United-states"
15
15
  },
16
16
  {
17
- label: "Ukraine",
18
- value: "Ukraine",
19
- areaCode: "+380",
20
- icon: "πŸ‡ΊπŸ‡¦",
21
- id: "ukraine"
17
+ label: "Canada",
18
+ value: "Canada",
19
+ areaCode: "+1",
20
+ icon: "πŸ‡¨πŸ‡¦",
21
+ id: "canada"
22
22
  },
23
23
  {
24
24
  label: "Pakistan",