playbook_ui 14.5.0.pre.alpha.PLAY1485selectablecardoverflowoutlinebug4098 → 14.5.0.pre.alpha.PLAY1485selectablecardoverflowoutlinebug4216

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +25 -7
  3. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_custom_cell.jsx +72 -0
  4. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_custom_cell.md +5 -0
  5. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +2 -0
  6. data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +1 -0
  7. data/app/pb_kits/playbook/pb_card/_card.tsx +5 -1
  8. data/app/pb_kits/playbook/pb_dialog/_dialog.tsx +5 -1
  9. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_loading.html.erb +30 -7
  10. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_loading.md +0 -2
  11. data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +84 -3
  12. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +28 -5
  13. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_clear_selection.jsx +45 -0
  14. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_clear_selection.md +1 -0
  15. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_separators_hidden.html.erb +9 -0
  16. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_separators_hidden.jsx +33 -0
  17. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_subtle_variant.html.erb +10 -0
  18. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_subtle_variant.jsx +34 -0
  19. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_subtle_variant.md +1 -0
  20. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +5 -0
  21. data/app/pb_kits/playbook/pb_dropdown/docs/index.js +3 -0
  22. data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +10 -1
  23. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownOption.tsx +1 -1
  24. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +2 -2
  25. data/app/pb_kits/playbook/pb_filter/Filter/FilterDouble.tsx +2 -0
  26. data/app/pb_kits/playbook/pb_filter/Filter/FilterSingle.tsx +2 -0
  27. data/app/pb_kits/playbook/pb_filter/Filter/FiltersPopover.tsx +4 -1
  28. data/app/pb_kits/playbook/pb_filter/docs/_filter_popover_props.html.erb +41 -0
  29. data/app/pb_kits/playbook/pb_filter/docs/_filter_popover_props.jsx +71 -0
  30. data/app/pb_kits/playbook/pb_filter/docs/_filter_popover_props_rails.md +1 -0
  31. data/app/pb_kits/playbook/pb_filter/docs/_filter_popover_props_react.md +1 -0
  32. data/app/pb_kits/playbook/pb_filter/docs/example.yml +3 -0
  33. data/app/pb_kits/playbook/pb_filter/docs/index.js +1 -0
  34. data/app/pb_kits/playbook/pb_filter/filter.html.erb +2 -2
  35. data/app/pb_kits/playbook/pb_filter/filter.rb +2 -0
  36. data/app/pb_kits/playbook/pb_flex/_flex.tsx +3 -1
  37. data/app/pb_kits/playbook/pb_flex/_flex_item.tsx +8 -2
  38. data/app/pb_kits/playbook/pb_flex/flex_item.html.erb +3 -6
  39. data/app/pb_kits/playbook/pb_flex/flex_item.rb +7 -2
  40. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_loading.html.erb +39 -0
  41. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_loading.md +1 -0
  42. data/app/pb_kits/playbook/pb_form/docs/example.yml +1 -0
  43. data/app/pb_kits/playbook/pb_form/form.rb +2 -0
  44. data/app/pb_kits/playbook/pb_form/formHelper.js +27 -0
  45. data/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.scss +169 -65
  46. data/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.test.js +5 -5
  47. data/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.tsx +15 -9
  48. data/app/pb_kits/playbook/pb_multiple_users_stacked/docs/_multiple_users_stacked_size.html.erb +336 -0
  49. data/app/pb_kits/playbook/pb_multiple_users_stacked/docs/_multiple_users_stacked_size.jsx +97 -0
  50. data/app/pb_kits/playbook/pb_multiple_users_stacked/docs/example.yml +2 -0
  51. data/app/pb_kits/playbook/pb_multiple_users_stacked/docs/index.js +1 -0
  52. data/app/pb_kits/playbook/pb_multiple_users_stacked/multiple_users_stacked.html.erb +28 -6
  53. data/app/pb_kits/playbook/pb_multiple_users_stacked/multiple_users_stacked.rb +31 -1
  54. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.scss +86 -18
  55. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +15 -6
  56. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_preferred_countries.md +1 -1
  57. data/app/pb_kits/playbook/pb_phone_number_input/intlTelInput.scss +849 -931
  58. data/app/pb_kits/playbook/pb_phone_number_input/types.d.ts +4 -1
  59. data/app/pb_kits/playbook/pb_popover/_popover.tsx +6 -2
  60. data/app/pb_kits/playbook/pb_popover/docs/_popover_default.html.erb +1 -1
  61. data/app/pb_kits/playbook/pb_popover/popover.rb +3 -1
  62. data/app/pb_kits/playbook/pb_selectable_card/_selectable_card.scss +28 -0
  63. data/app/pb_kits/playbook/pb_selectable_card/_selectable_card.tsx +1 -0
  64. data/app/pb_kits/playbook/pb_selectable_card/selectable_card.html.erb +1 -1
  65. data/app/pb_kits/playbook/pb_selectable_card/selectable_card.rb +5 -1
  66. data/app/pb_kits/playbook/utilities/globalPropNames.mjs +3 -0
  67. data/app/pb_kits/playbook/utilities/globalProps.ts +39 -2
  68. data/dist/chunks/_typeahead-BhHnXJjy.js +22 -0
  69. data/dist/chunks/_weekday_stacked-B9Sy5PN8.js +45 -0
  70. data/dist/chunks/{lib-CEpcaI8y.js → lib-D-mTv-kp.js} +1 -1
  71. data/dist/chunks/{pb_form_validation-D9zkwt2b.js → pb_form_validation-BkWGwJsl.js} +1 -1
  72. data/dist/chunks/vendor.js +1 -1
  73. data/dist/playbook-doc.js +1 -1
  74. data/dist/playbook-rails-react-bindings.js +1 -1
  75. data/dist/playbook-rails.js +1 -1
  76. data/dist/playbook.css +1 -1
  77. data/lib/playbook/kit_base.rb +21 -1
  78. data/lib/playbook/pb_forms_helper.rb +3 -1
  79. data/lib/playbook/version.rb +1 -1
  80. metadata +24 -6
  81. data/dist/chunks/_typeahead-C9g4qCcE.js +0 -22
  82. data/dist/chunks/_weekday_stacked-B0Zid7Rv.js +0 -45
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b9228103de6d01a84145169d2ad394bf2c6b3b6df7ec0e3b16cfe518a803f579
4
- data.tar.gz: 257b1a1fdd3adc9310b52b8e513b2fe3e9974cf4429a2ae719aa8e247c74a2ce
3
+ metadata.gz: 387ed5f85511c8d3c7662523f280c230211f92f5d601045a5548cb1e946d0403
4
+ data.tar.gz: fc8c6544e1bc3a338c07200a9d4c3b1b2423b0dd4a68644e29523f91414580f8
5
5
  SHA512:
6
- metadata.gz: 925407457e1d6d8ce1c3a6012bf084ef8721040ce76e5432978a7407d1d80769126a4ea24c089bef72a4cd6af4eafe15fc86ae997d7f9b88338de01d144c43cf
7
- data.tar.gz: 79f269ceeb505d1f9b200d1088d6db340acc6a3be267f8e9fe777eb8e2e121089361729fd156df2a37535bf27a020f282d5aad6b9a3cecb5d53e4add11e93a9e
6
+ metadata.gz: e6c41d3c8ea88e41b965797d2babde70c3753449c888c51c40cbd6d9319c4127a387ad4eb9ec0e4293ab161af43522d2b1e028fbcc704f1ff7de21023793dc4c
7
+ data.tar.gz: e51dd0fde49820c22978dfa09e51f6ab85c9f1fdaa915fdda6645c50c7b1696417e7f5dd7d4e64f3052ba344dd17d2fa7dc9a865d6f5f70256edeb8a8d0a7160
@@ -91,7 +91,7 @@ const AdvancedTable = (props: AdvancedTableProps) => {
91
91
  const columnHelper = createColumnHelper()
92
92
 
93
93
  //Create cells for first columns
94
- const createCellFunction = (cellAccessors: string[]) => {
94
+ const createCellFunction = (cellAccessors: string[], customRenderer?: (row: Row<GenericObject>, value: any) => JSX.Element) => {
95
95
  const columnCells = ({
96
96
  row,
97
97
  getValue,
@@ -101,6 +101,11 @@ const AdvancedTable = (props: AdvancedTableProps) => {
101
101
  }) => {
102
102
  const rowData = row.original
103
103
 
104
+ // Use customRenderer if provided, otherwise default rendering
105
+ if (customRenderer) {
106
+ return customRenderer(row, getValue())
107
+ }
108
+
104
109
  switch (row.depth) {
105
110
  case 0: {
106
111
  return (
@@ -134,18 +139,31 @@ const AdvancedTable = (props: AdvancedTableProps) => {
134
139
  //Create column array in format needed by Tanstack
135
140
  const columns =
136
141
  columnDefinitions &&
137
- columnDefinitions.map((column) => {
142
+ columnDefinitions.map((column, index) => {
138
143
  // Define the base column structure
139
144
  const columnStructure = {
140
145
  ...columnHelper.accessor(column.accessor, {
141
146
  header: column.label,
142
147
  }),
143
148
  }
144
- if (column.cellAccessors) {
145
- columnStructure.cell = createCellFunction(column.cellAccessors)
146
- }
147
- return columnStructure
148
- })
149
+
150
+ // Use the custom renderer if provided, EXCEPT for the first column
151
+ if (index !== 0) {
152
+ if (column.cellAccessors || column.customRenderer) {
153
+ columnStructure.cell = createCellFunction(
154
+ column.cellAccessors,
155
+ column.customRenderer
156
+ )
157
+ }
158
+ } else {
159
+ // For the first column, apply createCellFunction without customRenderer
160
+ if (column.cellAccessors) {
161
+ columnStructure.cell = createCellFunction(column.cellAccessors)
162
+ }
163
+ }
164
+
165
+ return columnStructure
166
+ })
149
167
 
150
168
  //Syntax for sorting Array if we want to manage state ourselves
151
169
  const sorting = [
@@ -0,0 +1,72 @@
1
+ import React from "react"
2
+ import { AdvancedTable, Pill, Body, Flex, Detail, Caption } from "playbook-ui"
3
+ import MOCK_DATA from "./advanced_table_mock_data.json"
4
+
5
+ const AdvancedTableCustomCell = (props) => {
6
+ const columnDefinitions = [
7
+ {
8
+ accessor: "year",
9
+ label: "Year",
10
+ cellAccessors: ["quarter", "month", "day"],
11
+
12
+ },
13
+ {
14
+ accessor: "newEnrollments",
15
+ label: "New Enrollments",
16
+ customRenderer: (row, value) => (
17
+ <Pill text={value}
18
+ variant="success"
19
+ />
20
+ ),
21
+ },
22
+ {
23
+ accessor: "scheduledMeetings",
24
+ label: "Scheduled Meetings",
25
+ customRenderer: (row, value) => <Body><a href="#">{value}</a></Body>,
26
+ },
27
+ {
28
+ accessor: "attendanceRate",
29
+ label: "Attendance Rate",
30
+ customRenderer: (row, value) => (
31
+ <Flex alignItems="end"
32
+ orientation="column"
33
+ >
34
+ <Detail bold
35
+ color="default"
36
+ text={value}
37
+ />
38
+ <Caption size="xs">{row.original.graduatedStudents}</Caption>
39
+ </Flex>
40
+ ),
41
+ },
42
+ {
43
+ accessor: "completedClasses",
44
+ label: "Completed Classes",
45
+ },
46
+ {
47
+ accessor: "classCompletionRate",
48
+ label: "Class Completion Rate",
49
+ },
50
+ {
51
+ accessor: "graduatedStudents",
52
+ label: "Graduated Students",
53
+ },
54
+ ]
55
+
56
+ return (
57
+ <div>
58
+ <AdvancedTable
59
+ columnDefinitions={columnDefinitions}
60
+ enableToggleExpansion="all"
61
+ responsive="none"
62
+ tableData={MOCK_DATA}
63
+ {...props}
64
+ >
65
+ <AdvancedTable.Header enableSorting />
66
+ <AdvancedTable.Body />
67
+ </AdvancedTable>
68
+ </div>
69
+ )
70
+ }
71
+
72
+ export default AdvancedTableCustomCell
@@ -0,0 +1,5 @@
1
+ The Advanced Table also allows for rendering custom components within individual Cells. To achieve this, you can make use of the optional `customRenderer` item within each columnDefinition. This function gives you access to the current Cell's value if you just want to use that with a custom Kit, but it also gives you access to the entire `row` object. The row object provides all data for the current row. To access the data, use `row.original` which is the entire data object for the current row. See the code snippet below for 3 separate use cases and how they were acheived.
2
+
3
+ See [here](https://playbook.powerapp.cloud/kits/advanced_table/react#columnDefinitions) for more indepth information on columnDefinitions are how to use them.
4
+
5
+ See [here](https://github.com/powerhome/playbook/tree/master/playbook/app/pb_kits/playbook/pb_advanced_table#readme) for the structure of the tableData used.
@@ -3,6 +3,7 @@ examples:
3
3
  - advanced_table_beta: Default (Required Props)
4
4
  - advanced_table_beta_subrow_headers: SubRow Headers
5
5
  - advanced_table_beta_sort: Enable Sorting
6
+
6
7
  react:
7
8
  - advanced_table_default: Default (Required Props)
8
9
  - advanced_table_loading: Loading State
@@ -15,3 +16,4 @@ examples:
15
16
  - advanced_table_table_props: Table Props
16
17
  - advanced_table_inline_row_loading: Inline Row Loading
17
18
  - advanced_table_responsive: Responsive Tables
19
+ - advanced_table_custom_cell: Custom Components for Cells
@@ -9,3 +9,4 @@ export { default as AdvancedTableTableOptions } from './_advanced_table_table_op
9
9
  export { default as AdvancedTableTableProps } from './_advanced_table_table_props.jsx'
10
10
  export { default as AdvancedTableInlineRowLoading } from './_advanced_table_inline_row_loading.jsx'
11
11
  export { default as AdvancedTableResponsive } from './_advanced_table_responsive.jsx'
12
+ export { default as AdvancedTableCustomCell } from './_advanced_table_custom_cell.jsx'
@@ -5,7 +5,7 @@ import { get } from 'lodash'
5
5
  import classnames from 'classnames'
6
6
 
7
7
  import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../utilities/props'
8
- import { GlobalProps, globalProps } from '../utilities/globalProps'
8
+ import { GlobalProps, globalProps, globalInlineProps } from '../utilities/globalProps'
9
9
  import type { ProductColors, CategoryColors, BackgroundColors } from '../types/colors'
10
10
 
11
11
  import Icon from '../pb_icon/_icon'
@@ -49,6 +49,7 @@ type CardBodyProps = {
49
49
  padding?: string,
50
50
  } & GlobalProps
51
51
 
52
+
52
53
  // Header component
53
54
  const Header = (props: CardHeaderProps) => {
54
55
  const { children, className, headerColor = 'category_1', headerColorStriped = false } = props
@@ -107,6 +108,7 @@ const Card = (props: CardPropTypes): React.ReactElement => {
107
108
 
108
109
  // coerce to array
109
110
  const cardChildren = React.Children.toArray(children)
111
+ const dynamicInlineProps = globalInlineProps(props);
110
112
 
111
113
  const subComponentTags = (tagName: string) => {
112
114
  return cardChildren.filter((c: string) => (
@@ -135,6 +137,7 @@ const Card = (props: CardPropTypes): React.ReactElement => {
135
137
  {...dataProps}
136
138
  {...htmlProps}
137
139
  className={classnames(cardCss, globalProps(props), className)}
140
+ style={dynamicInlineProps}
138
141
  >
139
142
  {subComponentTags('Header')}
140
143
  {
@@ -163,6 +166,7 @@ const Card = (props: CardPropTypes): React.ReactElement => {
163
166
  {...dataProps}
164
167
  {...htmlProps}
165
168
  className={classnames(cardCss, globalProps(props), className)}
169
+ style={dynamicInlineProps}
166
170
  >
167
171
  {subComponentTags('Header')}
168
172
  {nonHeaderChildren}
@@ -6,7 +6,7 @@ import classnames from "classnames";
6
6
  import Modal from "react-modal";
7
7
 
8
8
  import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from "../utilities/props";
9
- import { globalProps } from "../utilities/globalProps";
9
+ import { globalProps, globalInlineProps } from "../utilities/globalProps";
10
10
 
11
11
  import Body from "../pb_body/_body";
12
12
  import Button from "../pb_button/_button";
@@ -91,6 +91,8 @@ const Dialog = (props: DialogProps): React.ReactElement => {
91
91
  beforeClose: "pb_dialog_overlay_before_close",
92
92
  };
93
93
 
94
+ const dynamicInlineProps = globalInlineProps(props);
95
+
94
96
  const classes = classnames(
95
97
  buildCss("pb_dialog_wrapper"),
96
98
  globalProps(props),
@@ -184,6 +186,7 @@ const Dialog = (props: DialogProps): React.ReactElement => {
184
186
  overlayClassName={overlayClassNames}
185
187
  portalClassName={portalClassName}
186
188
  shouldCloseOnOverlayClick={shouldCloseOnOverlayClick && !loading}
189
+ style={{ content: dynamicInlineProps }}
187
190
  >
188
191
  <>
189
192
  {title && !status ? <Dialog.Header>{title}</Dialog.Header> : null}
@@ -192,6 +195,7 @@ const Dialog = (props: DialogProps): React.ReactElement => {
192
195
  <Dialog.Body
193
196
  className="dialog_status_text_align"
194
197
  padding="md"
198
+
195
199
  >
196
200
  <Flex align="center"
197
201
  orientation="column"
@@ -1,13 +1,36 @@
1
1
  <%= pb_rails("button", props: { text: "Open Dialog", data: {"open-dialog": "dialog-loading"} }) %>
2
2
 
3
- <%= pb_rails("dialog", props: {
4
- id:"dialog-loading",
5
- size: "sm",
6
- title: "Loading Exmaple",
7
- text: "Make a loading request?",
8
- cancel_button: "Cancel Button",
3
+ <%= pb_rails("dialog", props: {
4
+ id:"dialog-loading",
5
+ size: "sm",
6
+ title: "Loading Example",
7
+ text: "Make a loading request?",
8
+ cancel_button: "Cancel Button",
9
9
  cancel_button_id: "cancel-button-loading",
10
- confirm_button: "Okay",
10
+ confirm_button: "Okay",
11
11
  confirm_button_id: "confirm-button-loading",
12
12
  loading: true,
13
13
  }) %>
14
+
15
+ <script>
16
+ const loadingButton = document.querySelector('[data-disable-with="Loading"]');
17
+ if (loadingButton) {
18
+ loadingButton.addEventListener("click", function() {
19
+ const okayLoadingButton = document.querySelector('[data-disable-with="Loading"]');
20
+ const cancelButton = document.querySelector('[data-disable-cancel-with="Loading"]');
21
+ let currentClass = okayLoadingButton.className;
22
+ let cancelClass = cancelButton ? cancelButton.className : "";
23
+
24
+ setTimeout(function() {
25
+ okayLoadingButton.disabled = false;
26
+ okayLoadingButton.className = currentClass.replace("_disabled_loading", "_enabled");
27
+
28
+ if (cancelButton) {
29
+ cancelButton.disabled = false;
30
+ cancelButton.className = cancelClass.replace("_disabled", "_enabled");
31
+ }
32
+ }, 5000);
33
+
34
+ });
35
+ }
36
+ </script>
@@ -1,3 +1 @@
1
1
  Pressing the "Okay" button will trigger a loading state where the button content is replaced by a spinner icon and both buttons are disabled.
2
-
3
- Currently, the loading state cannot be undone and will require a page refresh to reset the dialog.
@@ -69,15 +69,21 @@
69
69
  padding-bottom: $space_xs;
70
70
  cursor: pointer;
71
71
  &:hover {
72
- background-color: $border_light;
72
+ background-color: $bg_light;
73
73
  }
74
74
 
75
75
  &[class*="_focused"] {
76
- background-color: $border_light;
76
+ background-color: $bg_light;
77
77
  }
78
78
 
79
79
  &[class*="_list"] {
80
80
  border-bottom: 1px solid $border_light;
81
+
82
+ &:hover, &:focus {
83
+ .pb_body_kit {
84
+ color: $primary;
85
+ }
86
+ }
81
87
  }
82
88
  &[class*="selected"] {
83
89
  background-color: $primary;
@@ -87,7 +93,7 @@
87
93
  color: $white !important;
88
94
  }
89
95
  &:hover {
90
- background-color: $primary !important;
96
+ background-color: $product_1_background !important;
91
97
  }
92
98
  }
93
99
  }
@@ -125,6 +131,81 @@
125
131
  }
126
132
  }
127
133
 
134
+ &.separators_hidden {
135
+ .dropdown_wrapper {
136
+ .pb_dropdown_container {
137
+
138
+ [class*="pb_dropdown_option"] {
139
+ border: none;
140
+ }
141
+ }
142
+ }
143
+ }
144
+
145
+ &.subtle {
146
+ .dropdown_wrapper {
147
+ .pb_dropdown_container {
148
+
149
+ [class*="pb_dropdown_option"]:first-child {
150
+ margin-top: $space_xs;
151
+ }
152
+
153
+ [class*="pb_dropdown_option"]:last-child {
154
+ margin-bottom: $space_xs;
155
+ }
156
+
157
+ [class*="pb_dropdown_option"] {
158
+ margin: $space_xs;
159
+ padding: $space_xs;
160
+ border-radius: $border_radius_md;
161
+ border-bottom: none;
162
+ position: relative;
163
+
164
+ &::after {
165
+ content: "";
166
+ position: absolute;
167
+ left: -$space_xs;
168
+ right: -$space_xs;
169
+ bottom: -4.5px;
170
+ height: 1px;
171
+ background: $border_light;
172
+ }
173
+ }
174
+
175
+ [class*="pb_dropdown_option"]:last-child::after {
176
+ display: none;
177
+ }
178
+ }
179
+ }
180
+
181
+ &.separators_hidden {
182
+ .dropdown_wrapper {
183
+ .pb_dropdown_container {
184
+ [class*="pb_dropdown_option"]:first-child {
185
+ margin-top: $space_xs;
186
+ }
187
+
188
+ [class*="pb_dropdown_option"]:last-child {
189
+ margin-bottom: $space_xs;
190
+ }
191
+
192
+ [class*="pb_dropdown_option"] {
193
+ padding: $space_xxs $space_xs;
194
+ margin: $space_xxs $space_xs;
195
+
196
+ &::after {
197
+ height: 0px;
198
+ }
199
+
200
+ &[class*="selected"] {
201
+ border-bottom: none;
202
+ }
203
+ }
204
+ }
205
+ }
206
+ }
207
+ }
208
+
128
209
  &.dark {
129
210
  .dropdown_wrapper {
130
211
  [class*="dropdown_trigger_wrapper"] {
@@ -1,4 +1,4 @@
1
- import React, { useState, useRef, useEffect } from "react";
1
+ import React, { useState, useRef, useEffect, forwardRef, useImperativeHandle } 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";
@@ -35,10 +35,19 @@ type DropdownProps = {
35
35
  label?: string;
36
36
  onSelect?: (arg: GenericObject) => null;
37
37
  options: GenericObject;
38
+ separators: boolean;
38
39
  triggerRef?: any;
40
+ variant?: "default" | "subtle";
39
41
  };
40
42
 
41
- const Dropdown = (props: DropdownProps) => {
43
+ interface DropdownComponent
44
+ extends React.ForwardRefExoticComponent<DropdownProps & React.RefAttributes<unknown>> {
45
+ Option: typeof DropdownOption;
46
+ Trigger: typeof DropdownTrigger;
47
+ Container: typeof DropdownContainer;
48
+ }
49
+
50
+ const Dropdown = forwardRef((props: DropdownProps, ref: any) => {
42
51
  const {
43
52
  aria = {},
44
53
  autocomplete = false,
@@ -55,15 +64,20 @@ const Dropdown = (props: DropdownProps) => {
55
64
  label,
56
65
  onSelect,
57
66
  options,
58
- triggerRef
67
+ separators = true,
68
+ triggerRef,
69
+ variant = "default",
59
70
  } = props;
60
71
 
61
72
  const ariaProps = buildAriaProps(aria);
62
73
  const dataProps = buildDataProps(data);
63
74
  const htmlProps = buildHtmlProps(htmlOptions);
75
+ const separatorsClass = separators ? '' : 'separators_hidden'
64
76
  const classes = classnames(
65
77
  buildCss("pb_dropdown"),
66
78
  globalProps(props),
79
+ variant,
80
+ separatorsClass,
67
81
  className
68
82
  );
69
83
 
@@ -125,7 +139,7 @@ const Dropdown = (props: DropdownProps) => {
125
139
  const filteredOptions = optionsWithBlankSelection?.filter((option: GenericObject) => {
126
140
  const label = typeof option.label === 'string' ? option.label.toLowerCase() : option.label;
127
141
  return String(label).toLowerCase().includes(filterItem.toLowerCase());
128
- });
142
+ });
129
143
 
130
144
  // For keyboard accessibility: Set focus within dropdown to selected item if it exists
131
145
  useEffect(() => {
@@ -175,6 +189,14 @@ const Dropdown = (props: DropdownProps) => {
175
189
  dark
176
190
  });
177
191
 
192
+ useImperativeHandle(ref, () => ({
193
+ clearSelected: () => {
194
+ setSelected({});
195
+ setFilterItem("");
196
+ setIsDropDownClosed(true);
197
+ onSelect && onSelect(null);
198
+ },
199
+ }));
178
200
 
179
201
  return (
180
202
  <div {...ariaProps}
@@ -258,8 +280,9 @@ const Dropdown = (props: DropdownProps) => {
258
280
  </DropdownContext.Provider>
259
281
  </div>
260
282
  )
261
- };
283
+ }) as DropdownComponent
262
284
 
285
+ Dropdown.displayName = "Dropdown";
263
286
  Dropdown.Option = DropdownOption;
264
287
  Dropdown.Trigger = DropdownTrigger;
265
288
  Dropdown.Container = DropdownContainer;
@@ -0,0 +1,45 @@
1
+ import React, { useRef } from 'react'
2
+ import { Button, Dropdown } from 'playbook-ui'
3
+
4
+ const options = [
5
+ {
6
+ label: "United States",
7
+ value: "United States",
8
+ },
9
+ {
10
+ label: "Canada",
11
+ value: "Canada",
12
+ },
13
+ {
14
+ label: "Pakistan",
15
+ value: "Pakistan",
16
+ }
17
+ ]
18
+
19
+ const DropdownClearSelection = (props) => {
20
+ const dropdownRef = useRef(null)
21
+
22
+ const handleReset = () => {
23
+ if (dropdownRef.current) {
24
+ dropdownRef.current.clearSelected()
25
+ }
26
+ }
27
+
28
+ return (
29
+ <>
30
+ <Dropdown
31
+ defaultValue={options[2]}
32
+ options={options}
33
+ ref={dropdownRef}
34
+ {...props}
35
+ />
36
+ <Button
37
+ marginTop="md"
38
+ onClick={handleReset}
39
+ text="Reset"
40
+ />
41
+ </>
42
+ )
43
+ }
44
+
45
+ export default DropdownClearSelection
@@ -0,0 +1 @@
1
+ To use an external control (like a reset button) to clear Dropdown selection, you can make use of the `useRef` hook. You must pass a ref to the Dropdown component and use that ref within the onClick for the external control in the way shown in the code snippet below.
@@ -0,0 +1,9 @@
1
+ <%
2
+ options = [
3
+ { label: 'United States', value: 'United States', id: 'us' },
4
+ { label: 'Canada', value: 'Canada', id: 'ca' },
5
+ { label: 'Pakistan', value: 'Pakistan', id: 'pk' },
6
+ ]
7
+
8
+ %>
9
+ <%= pb_rails("dropdown", props: {options: options, separators: false}) %>
@@ -0,0 +1,33 @@
1
+ import React from 'react'
2
+ import { Dropdown } from 'playbook-ui'
3
+
4
+ const DropdownSeparatorsHidden = (props) => {
5
+
6
+ const options = [
7
+ {
8
+ label: "United States",
9
+ value: "United States",
10
+ },
11
+ {
12
+ label: "Canada",
13
+ value: "Canada",
14
+ },
15
+ {
16
+ label: "Pakistan",
17
+ value: "Pakistan",
18
+ }
19
+ ];
20
+
21
+
22
+ return (
23
+ <div>
24
+ <Dropdown
25
+ options={options}
26
+ separators={false}
27
+ {...props}
28
+ />
29
+ </div>
30
+ )
31
+ }
32
+
33
+ export default DropdownSeparatorsHidden
@@ -0,0 +1,10 @@
1
+ <%
2
+ options = [
3
+ { label: 'United States', value: 'United States', id: 'us' },
4
+ { label: 'Canada', value: 'Canada', id: 'ca' },
5
+ { label: 'Pakistan', value: 'Pakistan', id: 'pk' },
6
+ ]
7
+
8
+ %>
9
+
10
+ <%= pb_rails("dropdown", props: { options: options, variant: "subtle", separators: false }) %>
@@ -0,0 +1,34 @@
1
+ import React from 'react'
2
+ import { Dropdown } from 'playbook-ui'
3
+
4
+ const DropdownSubtleVariant = (props) => {
5
+
6
+ const options = [
7
+ {
8
+ label: "United States",
9
+ value: "United States",
10
+ },
11
+ {
12
+ label: "Canada",
13
+ value: "Canada",
14
+ },
15
+ {
16
+ label: "Pakistan",
17
+ value: "Pakistan",
18
+ }
19
+ ];
20
+
21
+
22
+ return (
23
+ <>
24
+ <Dropdown
25
+ options={options}
26
+ separators={false}
27
+ variant="subtle"
28
+ {...props}
29
+ />
30
+ </>
31
+ )
32
+ }
33
+
34
+ export default DropdownSubtleVariant
@@ -0,0 +1 @@
1
+ For the `subtle` variant, it is recommended that you set the `Separators` prop to `false` to remove the separator lines between the options for a more cleaner look.
@@ -1,6 +1,7 @@
1
1
  examples:
2
2
  rails:
3
3
  - dropdown_default: Default
4
+ - dropdown_subtle_variant: Subtle Variant
4
5
  - dropdown_subcomponent_structure_rails: Subcomponent Structure
5
6
  - dropdown_with_label: With Label
6
7
  - dropdown_with_custom_options_rails: Custom Options
@@ -10,9 +11,11 @@ examples:
10
11
  - dropdown_error: Dropdown with Error
11
12
  - dropdown_default_value: Default Value
12
13
  - dropdown_blank_selection: Blank Selection
14
+ - dropdown_separators_hidden: Separators Hidden
13
15
 
14
16
  react:
15
17
  - dropdown_default: Default
18
+ - dropdown_subtle_variant: Subtle Variant
16
19
  - dropdown_subcomponent_structure: Subcomponent Structure
17
20
  - dropdown_with_label: With Label
18
21
  - dropdown_with_custom_options: Custom Options
@@ -22,6 +25,8 @@ examples:
22
25
  - dropdown_error: Dropdown with Error
23
26
  - dropdown_default_value: Default Value
24
27
  - dropdown_blank_selection: Blank Selection
28
+ - dropdown_clear_selection: Clear Selection
29
+ - dropdown_separators_hidden: Separators Hidden
25
30
  # - dropdown_with_autocomplete: Autocomplete
26
31
  # - dropdown_with_autocomplete_and_custom_display: Autocomplete with Custom Display
27
32
  # - dropdown_with_external_control: useDropdown Hook
@@ -12,3 +12,6 @@ export { default as DropdownSubcomponentStructure } from './_dropdown_subcompone
12
12
  export { default as DropdownError } from './_dropdown_error.jsx'
13
13
  export { default as DropdownDefaultValue } from './_dropdown_default_value.jsx'
14
14
  export { default as DropdownBlankSelection } from './_dropdown_blank_selection.jsx'
15
+ export { default as DropdownClearSelection } from './_dropdown_clear_selection.jsx'
16
+ export { default as DropdownSubtleVariant } from './_dropdown_subtle_variant.jsx'
17
+ export { default as DropdownSeparatorsHidden } from './_dropdown_separators_hidden.jsx'