playbook_ui 14.5.0 → 14.6.0.pre.alpha.PBNTR5554217

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) 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_advanced_table/index.js +60 -0
  8. data/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb +1 -9
  9. data/app/pb_kits/playbook/pb_advanced_table/table_subrow_header.html.erb +1 -9
  10. data/app/pb_kits/playbook/pb_card/_card.tsx +5 -1
  11. data/app/pb_kits/playbook/pb_currency/_currency.tsx +16 -6
  12. data/app/pb_kits/playbook/pb_currency/currency.rb +38 -11
  13. data/app/pb_kits/playbook/pb_currency/currency.test.js +35 -0
  14. data/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.html.erb +7 -0
  15. data/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.jsx +18 -0
  16. data/app/pb_kits/playbook/pb_currency/docs/example.yml +3 -1
  17. data/app/pb_kits/playbook/pb_currency/docs/index.js +1 -0
  18. data/app/pb_kits/playbook/pb_dialog/_dialog.tsx +5 -1
  19. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_loading.html.erb +30 -7
  20. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_loading.md +0 -2
  21. data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +84 -3
  22. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +28 -5
  23. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_clear_selection.jsx +45 -0
  24. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_clear_selection.md +1 -0
  25. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_separators_hidden.html.erb +9 -0
  26. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_separators_hidden.jsx +33 -0
  27. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_subtle_variant.html.erb +10 -0
  28. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_subtle_variant.jsx +34 -0
  29. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_subtle_variant.md +1 -0
  30. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +5 -0
  31. data/app/pb_kits/playbook/pb_dropdown/docs/index.js +3 -0
  32. data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +10 -1
  33. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownOption.tsx +1 -1
  34. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +2 -2
  35. data/app/pb_kits/playbook/pb_filter/Filter/FilterDouble.tsx +2 -0
  36. data/app/pb_kits/playbook/pb_filter/Filter/FilterSingle.tsx +2 -0
  37. data/app/pb_kits/playbook/pb_filter/Filter/FiltersPopover.tsx +4 -1
  38. data/app/pb_kits/playbook/pb_filter/Filter/ResultsCount.tsx +4 -2
  39. data/app/pb_kits/playbook/pb_filter/docs/_filter_default.jsx +1 -1
  40. data/app/pb_kits/playbook/pb_filter/docs/_filter_popover_props.html.erb +41 -0
  41. data/app/pb_kits/playbook/pb_filter/docs/_filter_popover_props.jsx +71 -0
  42. data/app/pb_kits/playbook/pb_filter/docs/_filter_popover_props_rails.md +1 -0
  43. data/app/pb_kits/playbook/pb_filter/docs/_filter_popover_props_react.md +1 -0
  44. data/app/pb_kits/playbook/pb_filter/docs/example.yml +3 -0
  45. data/app/pb_kits/playbook/pb_filter/docs/index.js +1 -0
  46. data/app/pb_kits/playbook/pb_filter/filter.html.erb +2 -2
  47. data/app/pb_kits/playbook/pb_filter/filter.rb +2 -0
  48. data/app/pb_kits/playbook/pb_flex/_flex.tsx +3 -1
  49. data/app/pb_kits/playbook/pb_flex/_flex_item.tsx +8 -2
  50. data/app/pb_kits/playbook/pb_flex/flex_item.html.erb +3 -6
  51. data/app/pb_kits/playbook/pb_flex/flex_item.rb +7 -2
  52. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_loading.html.erb +39 -0
  53. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_loading.md +1 -0
  54. data/app/pb_kits/playbook/pb_form/docs/example.yml +1 -0
  55. data/app/pb_kits/playbook/pb_form/form.rb +2 -0
  56. data/app/pb_kits/playbook/pb_form/formHelper.js +27 -0
  57. data/app/pb_kits/playbook/pb_form_pill/_form_pill.tsx +9 -1
  58. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.html.erb +19 -0
  59. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.jsx +27 -0
  60. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.md +1 -0
  61. data/app/pb_kits/playbook/pb_form_pill/docs/example.yml +2 -0
  62. data/app/pb_kits/playbook/pb_form_pill/docs/index.js +1 -0
  63. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +211 -227
  64. data/app/pb_kits/playbook/pb_multi_level_select/context/index.tsx +5 -0
  65. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_default.jsx +1 -1
  66. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_reset.html.erb +93 -0
  67. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_reset.md +1 -0
  68. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children.jsx +105 -0
  69. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children.md +1 -0
  70. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children_with_radios.jsx +106 -0
  71. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children_with_radios.md +1 -0
  72. data/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +4 -0
  73. data/app/pb_kits/playbook/pb_multi_level_select/docs/index.js +2 -0
  74. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select_options.tsx +149 -0
  75. data/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.scss +169 -65
  76. data/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.test.js +5 -5
  77. data/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.tsx +15 -9
  78. data/app/pb_kits/playbook/pb_multiple_users_stacked/docs/_multiple_users_stacked_size.html.erb +336 -0
  79. data/app/pb_kits/playbook/pb_multiple_users_stacked/docs/_multiple_users_stacked_size.jsx +97 -0
  80. data/app/pb_kits/playbook/pb_multiple_users_stacked/docs/example.yml +2 -0
  81. data/app/pb_kits/playbook/pb_multiple_users_stacked/docs/index.js +1 -0
  82. data/app/pb_kits/playbook/pb_multiple_users_stacked/multiple_users_stacked.html.erb +28 -6
  83. data/app/pb_kits/playbook/pb_multiple_users_stacked/multiple_users_stacked.rb +31 -1
  84. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.scss +86 -18
  85. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +15 -6
  86. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_preferred_countries.md +1 -1
  87. data/app/pb_kits/playbook/pb_phone_number_input/intlTelInput.scss +849 -931
  88. data/app/pb_kits/playbook/pb_phone_number_input/types.d.ts +4 -1
  89. data/app/pb_kits/playbook/pb_popover/_popover.tsx +6 -2
  90. data/app/pb_kits/playbook/pb_popover/docs/_popover_default.html.erb +1 -1
  91. data/app/pb_kits/playbook/pb_popover/popover.rb +3 -1
  92. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +4 -1
  93. data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +3 -1
  94. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +3 -1
  95. data/app/pb_kits/playbook/utilities/globalPropNames.mjs +3 -0
  96. data/app/pb_kits/playbook/utilities/globalProps.ts +39 -2
  97. data/dist/chunks/_typeahead-BhHnXJjy.js +22 -0
  98. data/dist/chunks/_weekday_stacked-C2icYweq.js +45 -0
  99. data/dist/chunks/{lib-CEpcaI8y.js → lib-D-mTv-kp.js} +1 -1
  100. data/dist/chunks/{pb_form_validation-D9zkwt2b.js → pb_form_validation-BkWGwJsl.js} +1 -1
  101. data/dist/chunks/vendor.js +1 -1
  102. data/dist/playbook-doc.js +1 -1
  103. data/dist/playbook-rails-react-bindings.js +1 -1
  104. data/dist/playbook-rails.js +1 -1
  105. data/dist/playbook.css +1 -1
  106. data/lib/playbook/kit_base.rb +21 -1
  107. data/lib/playbook/pb_doc_helper.rb +5 -5
  108. data/lib/playbook/pb_forms_helper.rb +3 -1
  109. data/lib/playbook/version.rb +2 -2
  110. metadata +40 -9
  111. data/dist/chunks/_typeahead-BYw0HEgO.js +0 -22
  112. data/dist/chunks/_weekday_stacked-DumiyWjh.js +0 -45
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c323706076c08d2191bb507c94cf711fbb21957412513edeb7e57c36d099037c
4
- data.tar.gz: '079b77b7edecd9ce5a8c0d252f029ec67e758c30b7d1469fb9de35ef91cd9d9e'
3
+ metadata.gz: 386f151fd01fd07dd00e2bbb98f290eb5a2d1d71fd775da6cbe748fb2967aa5c
4
+ data.tar.gz: 1b1af709f31c4e02bfa5b88bcfa9ad3bd97f8e59ebe000219030cd754d0bdfdd
5
5
  SHA512:
6
- metadata.gz: 1905e1368e12c0a3d34622c0c6d27e3c382bb37d5f6489525b31c51d43e4e6f0173c3922bf88f3ae3356d253a01730f6f413f1c4e730a53adc0e1bda7d71bd66
7
- data.tar.gz: 561bf8f8fcc2ae777fcf670b01b4969dfc47e3e406b6caea75815bd240190353a2d37abba20a14d59ca0a627adbafe2fe8bfdfe95e0eb11f192048cdcad13401
6
+ metadata.gz: 9c4dba5e3cd240d2e97629214ee5c9c592be48a507d3584e0f0bcb109e182c0434437c4d2e31059bfa4fcb68762b0fe41ffd32b6aceb2bc7d57b9b11314a8b10
7
+ data.tar.gz: 7b69c72fb69a6367826cef4b40304f4fee9df732b3ea2290f25b44ddac687c741c618ab9b628040d96f8642079ae6cd87495bf50a19963a241b032b60cabbfeb
@@ -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'
@@ -13,9 +13,20 @@ export default class PbAdvancedTable extends PbEnhancedElement {
13
13
  get target() {
14
14
  return document.querySelector(CONTENT_SELECTOR.replace("id", this.element.id))
15
15
  }
16
+
17
+ static expandedRows = new Set()
18
+ static isCollapsing = false
16
19
 
17
20
  connect() {
18
21
  this.element.addEventListener('click', () => {
22
+ if (!PbAdvancedTable.isCollapsing) {
23
+ const isExpanded = this.element.querySelector(UP_ARROW_SELECTOR).style.display === 'inline-block'
24
+ if (!isExpanded) {
25
+ PbAdvancedTable.expandedRows.add(this.element.id)
26
+ } else {
27
+ PbAdvancedTable.expandedRows.delete(this.element.id)
28
+ }
29
+ }
19
30
  this.toggleElement(this.target)
20
31
  })
21
32
  }
@@ -75,4 +86,53 @@ export default class PbAdvancedTable extends PbEnhancedElement {
75
86
  this.element.querySelector(UP_ARROW_SELECTOR).style.display = 'inline-block'
76
87
  this.element.querySelector(DOWN_ARROW_SELECTOR).style.display = 'none'
77
88
  }
89
+
90
+ static handleToggleAllHeaders(element) {
91
+ const table = element.closest('.pb_table')
92
+ const firstLevelButtons = table.querySelectorAll('.pb_advanced_table_body > .pb_table_tr [data-advanced-table]')
93
+
94
+ const expandedRows = Array.from(firstLevelButtons).filter(button =>
95
+ button.querySelector(UP_ARROW_SELECTOR).style.display === 'inline-block'
96
+ )
97
+
98
+ if (expandedRows.length === firstLevelButtons.length) {
99
+ expandedRows.forEach(button => {
100
+ button.click()
101
+ })
102
+ this.expandedRows.clear()
103
+ } else {
104
+ firstLevelButtons.forEach(button => {
105
+ if (!this.expandedRows.has(button.id)) {
106
+ button.click()
107
+ }
108
+ })
109
+ }
110
+ }
111
+ static handleToggleAllSubRows(element, rowDepth) {
112
+ const parentElement = element.closest(".toggle-content")
113
+ const subrowButtons = parentElement.querySelectorAll('.depth-sub-row-' + rowDepth + ' [data-advanced-table]')
114
+
115
+ const expandedSubRows = Array.from(subrowButtons).filter(button =>
116
+ button.querySelector(UP_ARROW_SELECTOR).style.display === 'inline-block'
117
+ )
118
+
119
+ if (expandedSubRows.length === subrowButtons.length) {
120
+ expandedSubRows.forEach(button => {
121
+ button.click()
122
+ })
123
+ } else {
124
+ subrowButtons.forEach(button => {
125
+ if (!this.expandedRows.has(button.id)) {
126
+ button.click()
127
+ }
128
+ })
129
+ }
130
+ }
131
+ }
132
+
133
+ window.expandAllRows = (element) => {
134
+ PbAdvancedTable.handleToggleAllHeaders(element)
78
135
  }
136
+ window.expandAllSubRows = (element, rowDepth) => {
137
+ PbAdvancedTable.handleToggleAllSubRows(element, rowDepth)
138
+ }
@@ -13,12 +13,4 @@
13
13
  <% end %>
14
14
  <% end %>
15
15
  <% end %>
16
- <% end %>
17
-
18
- <script type="text/javascript">
19
- var expandAllRows = (element) => {
20
- element.closest('.pb_table').querySelectorAll('.pb_advanced_table_body > .pb_table_tr [data-advanced-table]').forEach((button) => {
21
- button.dispatchEvent(new Event('click'));
22
- });
23
- };
24
- </script>
16
+ <% end %>
@@ -23,12 +23,4 @@
23
23
  <% end %>
24
24
  <% end %>
25
25
  <% end %>
26
- <% end %>
27
-
28
- <script type="text/javascript">
29
- var expandAllSubRows = (element, rowDepth) => {
30
- element.closest(".toggle-content").querySelectorAll('.depth-sub-row-' + rowDepth + ' [data-advanced-table]').forEach((button) => {
31
- button.dispatchEvent(new Event('click'));
32
- });
33
- };
34
- </script>
26
+ <% end %>
@@ -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}
@@ -26,6 +26,7 @@ type CurrencyProps = {
26
26
  variant?: 'default' | 'light' | 'bold',
27
27
  unit?: string,
28
28
  unstyled?: boolean,
29
+ commaSeparator?: boolean,
29
30
  }
30
31
 
31
32
  const sizes: {lg: 1, md: 3, sm: 4} = {
@@ -53,6 +54,7 @@ const Currency = (props: CurrencyProps): React.ReactElement => {
53
54
  variant = 'default',
54
55
  dark = false,
55
56
  unstyled = false,
57
+ commaSeparator = false,
56
58
  } = props
57
59
 
58
60
  const emphasizedClass = emphasized ? '' : '_deemphasized'
@@ -74,7 +76,7 @@ const Currency = (props: CurrencyProps): React.ReactElement => {
74
76
  className
75
77
  )
76
78
 
77
- const getFormattedNumber = (input: number | any ) => new Intl.NumberFormat('en-US', {
79
+ const getFormattedNumber = (input: number | any) => new Intl.NumberFormat('en-US', {
78
80
  notation: 'compact',
79
81
  maximumFractionDigits: 1,
80
82
  }).format(input)
@@ -88,12 +90,20 @@ const Currency = (props: CurrencyProps): React.ReactElement => {
88
90
  return isAmount ? num.slice(0, -1) : isUnit ? num.slice(-1) : ''
89
91
  }
90
92
 
91
- const getMatchingDecimalAmount = decimals === "matching" ? amount : whole,
92
- getMatchingDecimalValue = decimals === "matching" ? '' : `.${decimal}`
93
+ const getMatchingDecimalAmount = decimals === "matching" ? amount : whole
94
+ const getMatchingDecimalValue = decimals === "matching" ? '' : `.${decimal}`
93
95
 
94
- const getAmount = abbreviate ? getAbbreviatedValue('amount') : getMatchingDecimalAmount,
95
- getAbbreviation = abbreviate ? getAbbreviatedValue('unit') : null,
96
- getDecimalValue = abbreviate ? '' : getMatchingDecimalValue
96
+ const formatAmount = (amount: string) => {
97
+ if (!commaSeparator) return amount;
98
+
99
+ const [wholePart, decimalPart] = amount.split('.');
100
+ const formattedWhole = new Intl.NumberFormat('en-US').format(parseInt(wholePart));
101
+ return decimalPart ? `${formattedWhole}.${decimalPart}` : formattedWhole;
102
+ }
103
+
104
+ const getAmount = abbreviate ? getAbbreviatedValue('amount') : formatAmount(getMatchingDecimalAmount)
105
+ const getAbbreviation = abbreviate ? getAbbreviatedValue('unit') : null
106
+ const getDecimalValue = abbreviate ? '' : getMatchingDecimalValue
97
107
 
98
108
  return (
99
109
  <div
@@ -43,6 +43,9 @@ module Playbook
43
43
  prop :unstyled, type: Playbook::Props::Boolean,
44
44
  default: false
45
45
 
46
+ prop :comma_separator, type: Playbook::Props::Boolean,
47
+ default: false
48
+
46
49
  def classname
47
50
  generate_classname("pb_currency_kit", align, size, dark_class)
48
51
  end
@@ -65,7 +68,7 @@ module Playbook
65
68
  def title_props
66
69
  {
67
70
  size: size_value,
68
- text: abbreviate ? abbreviated_value : whole_value,
71
+ text: abbreviate ? abbreviated_value : formatted_amount,
69
72
  classname: "pb_currency_value",
70
73
  dark: dark,
71
74
  }
@@ -96,28 +99,38 @@ module Playbook
96
99
  private
97
100
 
98
101
  def whole_value
99
- return amount if decimals == "matching"
100
-
101
- amount.split(".").first.to_s
102
+ value = amount.split(".").first
103
+ if comma_separator
104
+ number_with_delimiter(value.gsub(",", ""))
105
+ else
106
+ value
107
+ end
102
108
  end
103
109
 
104
- def abbreviated_value(index = 0..-2)
105
- value = amount.split(".").first.split(",").join("")
106
- abbreviated_num = number_to_human(value, units: { thousand: "K", million: "M", billion: "B", trillion: "T" }).gsub(/\s+/, "").to_s
107
- abbreviated_num[index]
110
+ def decimal_value
111
+ amount.split(".")[1] || "00"
108
112
  end
109
113
 
110
114
  def units_element
111
115
  return "" if decimals == "matching" && !abbreviate && !unit
112
116
 
113
- _, decimal_part = amount.split(".")
114
- if unit.nil? && abbreviate == false
115
- decimal_part.nil? ? ".00" : ".#{decimal_part}"
117
+ if unit.nil? && !abbreviate
118
+ if decimals == "matching"
119
+ ""
120
+ else
121
+ ".#{decimal_value}"
122
+ end
116
123
  else
117
124
  abbreviate ? "#{abbreviated_value(-1)}#{unit}" : unit
118
125
  end
119
126
  end
120
127
 
128
+ def abbreviated_value(index = 0..-2)
129
+ value = amount.split(".").first.gsub(",", "").to_i
130
+ abbreviated_num = number_to_human(value, units: { thousand: "K", million: "M", billion: "B", trillion: "T" }).gsub(/\s+/, "")
131
+ abbreviated_num[index]
132
+ end
133
+
121
134
  def size_value
122
135
  case size
123
136
  when "lg"
@@ -132,6 +145,20 @@ module Playbook
132
145
  def dark_class
133
146
  dark ? "dark" : nil
134
147
  end
148
+
149
+ def formatted_amount
150
+ return abbreviated_value if abbreviate
151
+
152
+ if decimals == "matching"
153
+ if comma_separator
154
+ number_with_delimiter(amount.gsub(",", ""))
155
+ else
156
+ amount
157
+ end
158
+ else
159
+ whole_value
160
+ end
161
+ end
135
162
  end
136
163
  end
137
164
  end
@@ -61,3 +61,38 @@ test('decimals default prop returns decimals as body text', () => {
61
61
  expect(currencyKit.querySelector('.pb_currency_value')).toHaveTextContent('320')
62
62
  expect(currencyKit.querySelector('.unit')).toHaveTextContent('.20')
63
63
  })
64
+
65
+
66
+ test('commaSeparator prop returns comma separated amount', () => {
67
+ render(
68
+ <Currency
69
+ amount="1234567890"
70
+ comma_separator
71
+ data={{ testid: 'comma-test' }}
72
+ />
73
+ )
74
+ expect(screen.getByTestId('comma-test')).toHaveTextContent('1,234,567,890')
75
+ })
76
+
77
+ test('commaSeparator prop returns comma separated amount with decimals', () => {
78
+ render(
79
+ <Currency
80
+ amount="1234567890.12"
81
+ comma_separator
82
+ data={{ testid: 'comma-test-decimals' }}
83
+ />
84
+ )
85
+ expect(screen.getByTestId('comma-test-decimals')).toHaveTextContent('1,234,567,890.12')
86
+ })
87
+
88
+ test('commaSeparator prop returns comma separated amount with decimals="matching"', () => {
89
+ render(
90
+ <Currency
91
+ amount="1234567890.12"
92
+ comma_separator
93
+ data={{ testid: 'comma-test-decimals-matching' }}
94
+ decimals="matching"
95
+ />
96
+ )
97
+ expect(screen.getByTestId('comma-test-decimals-matching')).toHaveTextContent('1,234,567,890.12')
98
+ })
@@ -0,0 +1,7 @@
1
+ <%= pb_rails("currency", props: {
2
+ amount: '1234567.89',
3
+ comma_separator: true,
4
+ size: 'lg',
5
+ emphasized: false,
6
+ decimals: 'matching',
7
+ }) %>
@@ -0,0 +1,18 @@
1
+ import React from "react"
2
+
3
+ import Currency from "../_currency"
4
+
5
+ const CurrencyCommaSeparator = (props) => {
6
+ return (
7
+ <Currency
8
+ amount='1234567.89'
9
+ commaSeparator
10
+ decimals="matching"
11
+ emphasized={false}
12
+ size="lg"
13
+ {...props}
14
+ />
15
+ )
16
+ }
17
+
18
+ export default CurrencyCommaSeparator
@@ -8,7 +8,8 @@ examples:
8
8
  - currency_abbreviated: Abbreviate Larger Amounts
9
9
  - currency_matching_decimals: Matching Decimals
10
10
  - currency_unstyled: Unstyled
11
-
11
+ - currency_comma_separator: Comma Separator
12
+
12
13
  react:
13
14
  - currency_variants: Variants
14
15
  - currency_size: Size
@@ -17,6 +18,7 @@ examples:
17
18
  - currency_abbreviated: Abbreviate Larger Amounts
18
19
  - currency_matching_decimals: Matching Decimals
19
20
  - currency_unstyled: Unstyled
21
+ - currency_comma_separator: Comma Separator
20
22
 
21
23
  swift:
22
24
  - currency_size_swift: Size
@@ -5,3 +5,4 @@ export { default as CurrencyNoSymbol } from './_currency_no_symbol.jsx'
5
5
  export { default as CurrencyAbbreviated } from './_currency_abbreviated.jsx'
6
6
  export { default as CurrencyMatchingDecimals } from './_currency_matching_decimals.jsx'
7
7
  export { default as CurrencyUnstyled } from './_currency_unstyled.jsx'
8
+ export { default as CurrencyCommaSeparator } from './_currency_comma_separator.jsx'
@@ -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.