playbook_ui 13.25.0.pre.alpha.barchartfix2766 β†’ 13.26.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/index.js +1 -0
  3. data/app/pb_kits/playbook/pb_checkbox/_checkbox.scss +49 -0
  4. data/app/pb_kits/playbook/pb_checkbox/_checkbox.tsx +3 -0
  5. data/app/pb_kits/playbook/pb_checkbox/checkbox.rb +2 -1
  6. data/app/pb_kits/playbook/pb_checkbox/checkbox.test.js +14 -0
  7. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_disabled.html.erb +23 -0
  8. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_disabled.jsx +29 -0
  9. data/app/pb_kits/playbook/pb_checkbox/docs/example.yml +2 -0
  10. data/app/pb_kits/playbook/pb_checkbox/docs/index.js +1 -0
  11. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_on_change.md +3 -1
  12. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_on_close.md +3 -1
  13. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_range_limit.md +1 -1
  14. data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +92 -31
  15. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +60 -21
  16. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_default.jsx +2 -20
  17. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_default.md +1 -0
  18. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_subcomponent_structure.jsx +42 -0
  19. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_subcomponent_structure.md +7 -0
  20. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_autocomplete.jsx +1 -4
  21. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_autocomplete_and_custom_display.jsx +0 -1
  22. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_display.jsx +1 -1
  23. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_display.md +3 -1
  24. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_options.jsx +1 -4
  25. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_options.md +1 -1
  26. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_padding.jsx +1 -4
  27. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_external_control.jsx +59 -0
  28. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_hook.jsx +72 -0
  29. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_label.jsx +39 -0
  30. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_label.md +1 -0
  31. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +6 -2
  32. data/app/pb_kits/playbook/pb_dropdown/docs/index.js +5 -1
  33. data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +200 -10
  34. data/app/pb_kits/playbook/pb_dropdown/hooks/useDropdown.tsx +2 -2
  35. data/app/pb_kits/playbook/pb_dropdown/hooks/useHandleOnKeydown.tsx +4 -4
  36. data/app/pb_kits/playbook/pb_dropdown/scss_partials/_dropdown_animation.scss +18 -0
  37. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownContainer.tsx +17 -8
  38. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownOption.tsx +25 -15
  39. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +96 -78
  40. data/app/pb_kits/playbook/pb_dropdown/utilities/clickOutsideHelper.tsx +41 -0
  41. data/app/pb_kits/playbook/pb_dropdown/utilities/index.ts +2 -0
  42. data/app/pb_kits/playbook/pb_dropdown/utilities/subComponentHelper.tsx +9 -7
  43. data/app/pb_kits/playbook/pb_progress_simple/docs/_progress_simple_flex.html.erb +3 -0
  44. data/app/pb_kits/playbook/pb_progress_simple/docs/_progress_simple_flex.jsx +16 -0
  45. data/app/pb_kits/playbook/pb_progress_simple/docs/_progress_simple_flex.md +1 -0
  46. data/app/pb_kits/playbook/pb_progress_simple/docs/example.yml +2 -0
  47. data/app/pb_kits/playbook/pb_progress_simple/docs/index.js +1 -0
  48. data/app/pb_kits/playbook/pb_progress_simple/progress_simple.rb +1 -1
  49. data/app/pb_kits/playbook/pb_radio/_radio.scss +35 -0
  50. data/app/pb_kits/playbook/pb_radio/_radio.tsx +3 -0
  51. data/app/pb_kits/playbook/pb_radio/docs/_radio_alignment.jsx +4 -1
  52. data/app/pb_kits/playbook/pb_radio/docs/_radio_default.jsx +4 -1
  53. data/app/pb_kits/playbook/pb_radio/docs/_radio_disabled.html.erb +26 -0
  54. data/app/pb_kits/playbook/pb_radio/docs/_radio_disabled.jsx +31 -0
  55. data/app/pb_kits/playbook/pb_radio/docs/_radio_error.jsx +2 -1
  56. data/app/pb_kits/playbook/pb_radio/docs/example.yml +2 -0
  57. data/app/pb_kits/playbook/pb_radio/docs/index.js +1 -0
  58. data/app/pb_kits/playbook/pb_radio/radio.rb +5 -0
  59. data/app/pb_kits/playbook/pb_radio/radio.test.js +17 -0
  60. data/dist/playbook-rails.js +3 -3
  61. data/lib/playbook/version.rb +1 -1
  62. metadata +21 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 316498c60328f47c61c0c80097cd58e075a3a5c3011679fb2f932570e1d3429a
4
- data.tar.gz: 1ef2cf350e7c11fb2b108a5de7d7a3b6cf604e27d42764fb97d521be63a98954
3
+ metadata.gz: 513dedb641f87bf07610bf4bd6234671546cd7dad41022efad3019d85934b45c
4
+ data.tar.gz: 2ef935c0bfaba7bd80dc47bbaa1b8b4e89aac61c731ec8fbd88440e2557d57fd
5
5
  SHA512:
6
- metadata.gz: aaf75bdae9c9ab9f5197f91162698e97462532ccc9a87b1d76d8379da4cfbdd59c7441a9fb97dd30dea06d2b153d0c1c033761ec830a915ca0a594b1c455c128
7
- data.tar.gz: 8c703bb58ce255fb29513b79b6353d6025689514537e6c9fde9608d69b54868a8b37ea14d83131605ed50cdae55dfd3aea65e647f8ebd8a0a2a582361c8c70b1
6
+ metadata.gz: 1d0e17718945981e3002ebc05b9e9490be10884531df9e1f8ee65515e60d76a6edea5c27e68c50d205c5963047c74786f8d6e3e584b8bc0a76a8373636cb4049
7
+ data.tar.gz: 1ea1ae2275760805735801a334037a13dc60527fd431a4b985bca64cf85cf1f55ca95f89432c79a5f5593cad4ceed3967d5d1615d71f35daf6dc7d59f79ef991
@@ -133,6 +133,7 @@ export { default as dialogHelper } from './pb_dialog/dialogHelper'
133
133
  //Theming
134
134
  export {default as mapTheme} from './pb_map/pbMapTheme'
135
135
  export {default as useCollapsible} from './pb_collapsible/useCollapsible'
136
+ export {default as useDropdown } from './pb_dropdown/hooks/useDropdown'
136
137
 
137
138
  // CSS Tokens
138
139
  export { default as borderRadius } from './tokens/exports/_border_radius.scss'
@@ -74,6 +74,31 @@ $transition: $transition_cubic;
74
74
  opacity: $opacity_10;
75
75
  }
76
76
  }
77
+
78
+ &:disabled ~ .pb_checkbox_checkmark {
79
+ cursor: not-allowed;
80
+ background-color: mix($charcoal, $white, 5%);
81
+ border-color: $border_light;
82
+
83
+ & ~ .pb_checkbox_label {
84
+ cursor: not-allowed;
85
+ color: $text_lt_light;
86
+ }
87
+ & ~ .pb_checkbox_label .pb_body_kit {
88
+ cursor: not-allowed;
89
+ color: $text_lt_light;
90
+ }
91
+ }
92
+
93
+ &:disabled:checked ~ .pb_checkbox_checkmark {
94
+ background-color: $neutral;
95
+ border-color: $neutral;
96
+ }
97
+
98
+ &:disabled:checked:hover ~ .pb_checkbox_checkmark {
99
+ background-color: $neutral;
100
+ border-color: $neutral;
101
+ }
77
102
  }
78
103
 
79
104
  &.dark {
@@ -81,6 +106,30 @@ $transition: $transition_cubic;
81
106
  &:focus ~ .pb_checkbox_checkmark {
82
107
  box-shadow: 0px 0px 0px 2px $bg_dark_card, 0px 0px 0px 4px $primary;
83
108
  }
109
+
110
+ &:disabled ~ .pb_checkbox_checkmark {
111
+ cursor: not-allowed;
112
+ background-color: mix($charcoal, $white, 80%);
113
+ border: 2px solid $border_dark;
114
+
115
+ & ~ .pb_checkbox_label {
116
+ cursor: not-allowed;
117
+ color: $text_dk_light;
118
+ }
119
+ & ~ .pb_checkbox_label .pb_body_kit {
120
+ cursor: not-allowed;
121
+ color: $text_dk_light;
122
+ }
123
+ }
124
+
125
+ &:disabled:checked ~ .pb_checkbox_checkmark {
126
+ background-color: $border_dark;
127
+ }
128
+
129
+ &:disabled:checked:hover ~ .pb_checkbox_checkmark {
130
+ background-color: $border_dark;
131
+ border-color: $border_dark;
132
+ }
84
133
  }
85
134
 
86
135
  @media (hover:hover) {
@@ -12,6 +12,7 @@ type CheckboxProps = {
12
12
  className?: string,
13
13
  dark?: boolean,
14
14
  data?: {[key: string]: string},
15
+ disabled?: boolean,
15
16
  error?: boolean,
16
17
  htmlOptions?: {[key: string]: string | number | boolean | (() => void)},
17
18
  id?: string,
@@ -31,6 +32,7 @@ const Checkbox = (props: CheckboxProps): React.ReactElement => {
31
32
  className,
32
33
  dark = false,
33
34
  data = {},
35
+ disabled = false,
34
36
  error = false,
35
37
  htmlOptions = {},
36
38
  id,
@@ -67,6 +69,7 @@ const Checkbox = (props: CheckboxProps): React.ReactElement => {
67
69
  return (
68
70
  <input
69
71
  defaultChecked={checked}
72
+ disabled={disabled}
70
73
  name={name}
71
74
  onChange={onChange}
72
75
  ref={checkRef}
@@ -9,6 +9,7 @@ module Playbook
9
9
  prop :text
10
10
  prop :value
11
11
  prop :name
12
+ prop :disabled, type: Playbook::Props::Boolean, default: false
12
13
 
13
14
  prop :input_options, type: Playbook::Props::HashProp,
14
15
  default: {}
@@ -26,7 +27,7 @@ module Playbook
26
27
  end
27
28
 
28
29
  def input
29
- check_box_tag(name, value, checked, input_options)
30
+ check_box_tag(name, value, checked, input_options.merge(disabled: disabled))
30
31
  end
31
32
 
32
33
  def checkbox_label_status
@@ -92,3 +92,17 @@ test('has checked attribute', () => {
92
92
  const kit = screen.getByTestId(testId)
93
93
  expect(kit.getElementsByTagName('input')[0]).toHaveAttribute('checked')
94
94
  })
95
+
96
+ test('has disabled attribute', () => {
97
+ render(
98
+ <Checkbox
99
+ data={{ testid: testId }}
100
+ disabled
101
+ name="checkbox-name"
102
+ text="Checkbox"
103
+ value="check-box value"
104
+ />)
105
+ const kit = screen.getByTestId(testId)
106
+ const input = kit.querySelector('input')
107
+ expect(input).toHaveAttribute('disabled')
108
+ })
@@ -0,0 +1,23 @@
1
+ <%= pb_rails("flex", props: {orientation: "column"}) do %>
2
+ <%= pb_rails("flex/flex_item") do %>
3
+ <%= pb_rails("checkbox" , props: {
4
+ input_options: { tabindex: 0 },
5
+ margin_bottom: "xs",
6
+ text: "Disabled unchecked",
7
+ value: "checkbox-value",
8
+ disabled: true,
9
+ name: "checkbox-name"
10
+ }) %>
11
+ <% end %>
12
+ <%= pb_rails("flex/flex_item") do %>
13
+ <%= pb_rails("checkbox" , props: {
14
+ input_options: { tabindex: 0 },
15
+ text: "Disabled checked",
16
+ value: "checkbox-value",
17
+ disabled: true,
18
+ checked: true,
19
+ name: "checkbox-name"
20
+ }) %>
21
+ <% end %>
22
+ <% end %>
23
+
@@ -0,0 +1,29 @@
1
+ import React from 'react'
2
+
3
+ import Checkbox from '../_checkbox'
4
+
5
+ const CheckboxDisabled = (props) => {
6
+ return (
7
+ <div style={{ display: "flex", flexDirection: "column" }}>
8
+ <Checkbox
9
+ disabled
10
+ marginBottom="xs"
11
+ name="default name"
12
+ tabIndex={0}
13
+ text="Disabled unchecked"
14
+ value="default value"
15
+ {...props}
16
+ />
17
+ <Checkbox
18
+ checked
19
+ disabled
20
+ name="checkbox-name"
21
+ text="Disabled checked"
22
+ value="check-box value"
23
+ {...props}
24
+ />
25
+ </div>
26
+ )
27
+ }
28
+
29
+ export default CheckboxDisabled
@@ -6,6 +6,7 @@ examples:
6
6
  - checkbox_error: Default w/ Error
7
7
  - checkbox_options: Checkbox w/ Options
8
8
  - checkbox_indeterminate: Indeterminate Checkbox
9
+ - checkbox_disabled: Disabled Checkbox
9
10
 
10
11
  react:
11
12
  - checkbox_default: Default
@@ -13,6 +14,7 @@ examples:
13
14
  - checkbox_custom: Custom Checkbox
14
15
  - checkbox_error: Default w/ Error
15
16
  - checkbox_indeterminate: Indeterminate Checkbox
17
+ - checkbox_disabled: Disabled Checkbox
16
18
 
17
19
  swift:
18
20
  - checkbox_default_swift: Default
@@ -3,3 +3,4 @@ export { default as CheckboxCustom } from './_checkbox_custom.jsx'
3
3
  export { default as CheckboxError } from './_checkbox_error.jsx'
4
4
  export { default as CheckboxChecked } from './_checkbox_checked.jsx'
5
5
  export { default as CheckboxIndeterminate } from './_checkbox_indeterminate.jsx'
6
+ export { default as CheckboxDisabled } from './_checkbox_disabled.jsx'
@@ -1,3 +1,5 @@
1
1
  Your change handler function has access to two arguments: `dateStr` and `selectedDates`.
2
2
 
3
- The first, `dateStr`, is a string of the chosen date. The second, `selectedDates`, is an array of selected date objects. In many use cases `selectedDates` will have only one value but you'll still need to access it from index 0.
3
+ The first, `dateStr`, is a string of the chosen date. The second, `selectedDates`, is an array of selected date objects. In many use cases `selectedDates` will have only one value but you'll still need to access it from index 0.
4
+
5
+ NOTE: On Change does not account for manual input by users, so if your date picker sets `allowInput`, you should use the `onClose` method instead.
@@ -1,3 +1,5 @@
1
1
  The `onClose` handler function has access to two arguments: `dateStr` and `selectedDates`.
2
2
 
3
- The first, `dateStr`, is a string of the chosen date. The second, `selectedDates`, is an array of selected date objects. In many use cases `selectedDates` will have only one value but you'll still need to access it from index 0.
3
+ The first, `dateStr`, is a string of the chosen date. The second, `selectedDates`, is an array of selected date objects. In many use cases `selectedDates` will have only one value but you'll still need to access it from index 0.
4
+
5
+ NOTE: `onClose` is the ideal handler function to use when `allowInput` is enabled.
@@ -1 +1 @@
1
- Applying `this_ranges_end_today` (Rails) or `thisRangesEndToday` (React) causes all β€œThis” preset ranges (i.e., this week, this month, this quarter, this year) to use an endDate of today, instead of their natural end date in the future.
1
+ Because the Quick Pick variant has `allowInput` set to `true` by default, use the `onClose` handler function to access the startDate and endDate values. See the `onClose` example for details.
@@ -5,11 +5,13 @@
5
5
  @import "../tokens/shadows";
6
6
  @import "../tokens/positioning";
7
7
  @import "../pb_body/body_mixins";
8
+ @import "../pb_textarea/textarea_mixin";
9
+
10
+ @import "./scss_partials/dropdown_animation";
8
11
 
9
12
  .pb_dropdown {
10
13
  .dropdown_wrapper {
11
- position: relative;
12
- .dropdown_trigger_wrapper {
14
+ [class*="dropdown_trigger_wrapper"] {
13
15
  @include pb_body;
14
16
  border: 1px solid $border_light;
15
17
  background-color: $white;
@@ -28,7 +30,6 @@
28
30
  .dropdown_input {
29
31
  @include pb_body;
30
32
  border: unset;
31
- border-radius: $border_rad_heavier;
32
33
  padding: 0;
33
34
  background-color: $white;
34
35
  &:focus-visible {
@@ -40,15 +41,15 @@
40
41
  outline: unset;
41
42
  transition: box-shadow 0.15s ease-in-out;
42
43
  }
43
- }
44
44
 
45
- .dropdown_trigger_wrapper_select_only {
46
- box-shadow: inset 0 -11px 20px rgba($primary, 0.05);
47
- }
45
+ &[class*="_select_only"] {
46
+ box-shadow: inset 0 -11px 20px rgba($primary, 0.05);
47
+ }
48
48
 
49
- .dropdown_trigger_wrapper_focus {
50
- box-shadow: 0px 0px 0 1px $primary;
51
- transition: box-shadow 0.1s ease-in-out;
49
+ &[class*="_focus"] {
50
+ box-shadow: 0px 0px 0 1px $primary !important;
51
+ transition: box-shadow 0.1s ease-in-out;
52
+ }
52
53
  }
53
54
 
54
55
  .pb_dropdown_container {
@@ -58,46 +59,106 @@
58
59
  border-radius: $border_rad_heavier;
59
60
  border: 1px solid $border_light;
60
61
  margin-top: $space_xs;
61
- position: absolute;
62
62
  z-index: $z_1;
63
63
  width: 100%;
64
- transition: opacity 0.25s ease-in-out;
65
64
 
66
- .pb_dropdown_option {
65
+ [class*="pb_dropdown_option"] {
67
66
  cursor: pointer;
68
67
  &:hover {
69
68
  background-color: $border_light;
70
69
  }
71
- }
72
-
73
- .dropdown_option_focused {
74
- background-color: $border_light;
75
- }
76
70
 
77
- .dropdown_option {
78
- width: 100%;
79
- }
71
+ &[class*="_focused"] {
72
+ background-color: $border_light;
73
+ }
80
74
 
81
- .dropdown_option_list {
82
- border-bottom: 1px solid $border_light;
83
- }
84
- .dropdown_option_selected {
85
- background-color: $primary;
86
- [class^="pb_body"],
87
- [class^="pb_title_kit"] {
88
- color: $white !important;
75
+ &[class*="_list"] {
76
+ border-bottom: 1px solid $border_light;
89
77
  }
90
- &:hover {
91
- background-color: $primary !important;
78
+ &[class*="selected"] {
79
+ background-color: $primary;
80
+ [class^="pb_body"],
81
+ [class^="pb_title_kit"] {
82
+ color: $white !important;
83
+ }
84
+ &:hover {
85
+ background-color: $primary !important;
86
+ }
92
87
  }
93
88
  }
89
+
90
+ .dropdown_option_wrapper {
91
+ width: 100%;
92
+ }
94
93
  }
95
94
  .close {
96
95
  display: none;
96
+ animation-name: fadeOut;
97
+ animation-duration: 150ms;
98
+ animation-timing-function: linear;
99
+ animation-fill-mode: forwards;
97
100
  }
98
101
 
99
102
  .open {
100
103
  display: block;
104
+ animation-name: fadeIn;
105
+ animation-duration: 150ms;
106
+ animation-timing-function: linear;
107
+ animation-fill-mode: forwards;
108
+ }
109
+ }
110
+
111
+ &.dark {
112
+ .dropdown_wrapper {
113
+ [class*="dropdown_trigger_wrapper"] {
114
+ @include pb_body_light_dark;
115
+ background-color: rgba($white, 0.1) !important;
116
+ background: none;
117
+ border-color: rgba($white, 0.15);
118
+ @media (hover: hover) {
119
+ &:hover,
120
+ &:active,
121
+ &:focus {
122
+ background-color: rgba($white, 0.05) !important;
123
+ }
124
+ }
125
+ [class^="pb_body"] {
126
+ color: $white;
127
+ }
128
+ &[class*="_select_only"] {
129
+ box-shadow: inset 0 -11px 20px rgba($white, 0.05);
130
+ }
131
+ .dropdown_input {
132
+ background-color: unset;
133
+ color: $white;
134
+ }
135
+ }
136
+ }
137
+ .pb_dropdown_container {
138
+ background-color: $bg_dark !important;
139
+ border-color: rgba($white, 0.15);
140
+ color: $white;
141
+ [class^="pb_body"],
142
+ [class^="pb_title_kit"] {
143
+ color: $white !important;
144
+ }
145
+
146
+ [class*="pb_dropdown_option"] {
147
+ &:hover {
148
+ background-color: $hover_dark;
149
+ }
150
+
151
+ &[class*="_focused"] {
152
+ background-color: $hover_dark;
153
+ }
154
+
155
+ &[class*="_list"] {
156
+ border-color: rgba($white, 0.15);
157
+ }
158
+ &[class*="selected"] {
159
+ background-color: $primary;
160
+ }
161
+ }
101
162
  }
102
163
  }
103
164
  }
@@ -1,32 +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
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 };
25
29
  htmlOptions?: {[key: string]: string | number | boolean | (() => void)},
26
30
  id?: string;
27
- children?: React.ReactChild[] | React.ReactChild | ReactElement[];
28
- options: GenericObject;
31
+ isClosed?: boolean;
32
+ label?: string;
29
33
  onSelect?: (arg: GenericObject) => null;
34
+ options: GenericObject;
35
+ triggerRef?: any;
30
36
  };
31
37
 
32
38
  const Dropdown = (props: DropdownProps) => {
@@ -35,11 +41,15 @@ const Dropdown = (props: DropdownProps) => {
35
41
  autocomplete = false,
36
42
  children,
37
43
  className,
44
+ dark = false,
38
45
  data = {},
39
46
  htmlOptions = {},
40
47
  id,
41
- options,
48
+ isClosed = true,
49
+ label,
42
50
  onSelect,
51
+ options,
52
+ triggerRef
43
53
  } = props;
44
54
 
45
55
  const ariaProps = buildAriaProps(aria);
@@ -51,7 +61,7 @@ const Dropdown = (props: DropdownProps) => {
51
61
  className
52
62
  );
53
63
 
54
- const [isDropDownClosed, setIsDropDownClosed, toggleDropdown] = useDropdown();
64
+ const [isDropDownClosed, setIsDropDownClosed, toggleDropdown] = useDropdown(isClosed);
55
65
 
56
66
  const [filterItem, setFilterItem] = useState("");
57
67
  const [selected, setSelected] = useState<GenericObject>({});
@@ -59,28 +69,38 @@ const Dropdown = (props: DropdownProps) => {
59
69
  const [hasTriggerSubcomponent, setHasTriggerSubcomponent] = useState(true);
60
70
  const [hasContainerSubcomponent, setHasContainerSubcomponent] =
61
71
  useState(true);
62
-
63
72
  //state for keyboard events
64
73
  const [focusedOptionIndex, setFocusedOptionIndex] = useState(-1);
65
74
 
66
75
  const dropdownRef = useRef(null);
67
76
  const inputRef = useRef(null);
77
+ const inputWrapperRef = useRef(null);
78
+ const dropdownContainerRef = useRef(null);
68
79
 
69
80
  const { trigger, container, otherChildren } =
70
81
  separateChildComponents(children);
71
82
 
72
- // useEffect to handle clicks outside the dropdown
73
83
  useEffect(() => {
74
- const handleClickOutside = (e: MouseEvent) => {
75
- if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
76
- setIsDropDownClosed(true);
77
- setFocusedOptionIndex(-1)
78
- 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';
79
90
  }
80
- };
81
- 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);
82
102
  return () => {
83
- window.removeEventListener("click", handleClickOutside);
103
+ window.removeEventListener("click", handleClick);
84
104
  };
85
105
  }, []);
86
106
 
@@ -89,11 +109,16 @@ const Dropdown = (props: DropdownProps) => {
89
109
  setHasContainerSubcomponent(!!container);
90
110
  }, []);
91
111
 
112
+ // dropdown to toggle with external control
113
+ useEffect(()=> {
114
+ setIsDropDownClosed(isClosed)
115
+ },[isClosed])
92
116
 
93
117
  const filteredOptions = options?.filter((option: GenericObject) =>
94
118
  option.label.toLowerCase().includes(filterItem.toLowerCase())
95
119
  );
96
120
 
121
+ // For keyboard accessibility: Set focus within dropdown to selected item if it exists
97
122
  useEffect(() => {
98
123
  if (!isDropDownClosed) {
99
124
  let newIndex = 0;
@@ -107,6 +132,7 @@ const Dropdown = (props: DropdownProps) => {
107
132
  }
108
133
  }, [isDropDownClosed]);
109
134
 
135
+
110
136
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
111
137
  setFilterItem(e.target.value);
112
138
  setIsDropDownClosed(false);
@@ -116,7 +142,7 @@ const Dropdown = (props: DropdownProps) => {
116
142
  setSelected(selectedItem);
117
143
  setFilterItem("");
118
144
  setIsDropDownClosed(true);
119
- onSelect(selectedItem);
145
+ onSelect && onSelect(selectedItem);
120
146
  };
121
147
 
122
148
  const handleWrapperClick = () => {
@@ -126,7 +152,7 @@ const Dropdown = (props: DropdownProps) => {
126
152
 
127
153
  const handleBackspace = () => {
128
154
  setSelected({});
129
- onSelect(null);
155
+ onSelect && onSelect(null);
130
156
  setFocusedOptionIndex(-1);
131
157
  };
132
158
 
@@ -137,18 +163,22 @@ const Dropdown = (props: DropdownProps) => {
137
163
  trigger,
138
164
  container,
139
165
  otherChildren,
166
+ dark
140
167
  });
141
168
 
169
+
142
170
  return (
143
171
  <div {...ariaProps}
144
172
  {...dataProps}
145
173
  {...htmlProps}
146
174
  className={classes}
147
175
  id={id}
176
+ style={triggerRef ? { position: "absolute"} : { position: "relative"}}
148
177
  >
149
178
  <DropdownContext.Provider
150
179
  value={{
151
180
  autocomplete,
181
+ dropdownContainerRef,
152
182
  filteredOptions,
153
183
  filterItem,
154
184
  focusedOptionIndex,
@@ -157,6 +187,7 @@ const Dropdown = (props: DropdownProps) => {
157
187
  handleOptionClick,
158
188
  handleWrapperClick,
159
189
  inputRef,
190
+ inputWrapperRef,
160
191
  isDropDownClosed,
161
192
  isInputFocused,
162
193
  options,
@@ -166,8 +197,16 @@ const Dropdown = (props: DropdownProps) => {
166
197
  setIsInputFocused,
167
198
  setSelected,
168
199
  toggleDropdown,
200
+ triggerRef
169
201
  }}
170
202
  >
203
+ {label &&
204
+ <Caption
205
+ dark={dark}
206
+ marginBottom="xs"
207
+ text={label}
208
+ />
209
+ }
171
210
  <div className="dropdown_wrapper"
172
211
  onBlur={() => {
173
212
  // Debounce to delay the execution to prevent jumpiness in Focus state
@@ -204,7 +243,7 @@ const Dropdown = (props: DropdownProps) => {
204
243
  </div>
205
244
  </DropdownContext.Provider>
206
245
  </div>
207
- );
246
+ )
208
247
  };
209
248
 
210
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
12
  label: "Canada",
18
13
  value: "Canada",
19
- areaCode: "+1",
20
- icon: "πŸ‡¨πŸ‡¦",
21
- id: "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
  }