playbook_ui 14.6.2.pre.alpha.PLAY1607dependencydisplayymlupdate4271 → 14.6.2.pre.alpha.PLAY1613darkdatetimecolors4432

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/_playbook.scss +3 -2
  3. data/app/pb_kits/playbook/pb_advanced_table/Components/CustomCell.tsx +8 -1
  4. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +21 -28
  5. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +37 -1
  6. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_custom_cell.jsx +13 -2
  7. data/app/pb_kits/playbook/pb_card/_card.tsx +11 -4
  8. data/app/pb_kits/playbook/pb_card/_card_mixin.scss +1 -2
  9. data/app/pb_kits/playbook/pb_currency/_currency.tsx +16 -6
  10. data/app/pb_kits/playbook/pb_currency/currency.rb +38 -11
  11. data/app/pb_kits/playbook/pb_currency/currency.test.js +35 -0
  12. data/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.html.erb +7 -0
  13. data/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.jsx +18 -0
  14. data/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.md +3 -0
  15. data/app/pb_kits/playbook/pb_currency/docs/example.yml +3 -1
  16. data/app/pb_kits/playbook/pb_currency/docs/index.js +1 -0
  17. data/app/pb_kits/playbook/pb_date/_date.scss +3 -0
  18. data/app/pb_kits/playbook/pb_dialog/_dialog.tsx +5 -1
  19. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +8 -7
  20. data/app/pb_kits/playbook/pb_flex/_flex.tsx +3 -1
  21. data/app/pb_kits/playbook/pb_flex/_flex_item.tsx +8 -2
  22. data/app/pb_kits/playbook/pb_flex/flex_item.html.erb +3 -6
  23. data/app/pb_kits/playbook/pb_flex/flex_item.rb +7 -2
  24. data/app/pb_kits/playbook/pb_form_pill/_form_pill.scss +6 -2
  25. data/app/pb_kits/playbook/pb_form_pill/_form_pill.tsx +30 -26
  26. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.html.erb +24 -1
  27. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.jsx +25 -2
  28. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text_rails.md +3 -0
  29. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text_react.md +1 -0
  30. data/app/pb_kits/playbook/pb_form_pill/form_pill.html.erb +46 -8
  31. data/app/pb_kits/playbook/pb_link/_link.scss +66 -0
  32. data/app/pb_kits/playbook/pb_link/_link.tsx +107 -0
  33. data/app/pb_kits/playbook/pb_link/docs/_link_color.html.erb +30 -0
  34. data/app/pb_kits/playbook/pb_link/docs/_link_color.jsx +40 -0
  35. data/app/pb_kits/playbook/pb_link/docs/_link_disabled.html.erb +5 -0
  36. data/app/pb_kits/playbook/pb_link/docs/_link_disabled.jsx +15 -0
  37. data/app/pb_kits/playbook/pb_link/docs/_link_icon.html.erb +15 -0
  38. data/app/pb_kits/playbook/pb_link/docs/_link_icon.jsx +25 -0
  39. data/app/pb_kits/playbook/pb_link/docs/_link_tag.html.erb +35 -0
  40. data/app/pb_kits/playbook/pb_link/docs/_link_tag.jsx +45 -0
  41. data/app/pb_kits/playbook/pb_link/docs/_link_underline.html.erb +5 -0
  42. data/app/pb_kits/playbook/pb_link/docs/_link_underline.jsx +15 -0
  43. data/app/pb_kits/playbook/pb_link/docs/example.yml +16 -0
  44. data/app/pb_kits/playbook/pb_link/docs/index.js +5 -0
  45. data/app/pb_kits/playbook/pb_link/link.html.erb +21 -0
  46. data/app/pb_kits/playbook/pb_link/link.rb +44 -0
  47. data/app/pb_kits/playbook/pb_link/link.test.jsx +92 -0
  48. data/app/pb_kits/playbook/pb_popover/_popover.tsx +1 -1
  49. data/app/pb_kits/playbook/pb_selectable_card/_selectable_card.scss +67 -1
  50. data/app/pb_kits/playbook/pb_selectable_card/_selectable_card.tsx +1 -0
  51. data/app/pb_kits/playbook/pb_selectable_card/selectable_card.html.erb +1 -1
  52. data/app/pb_kits/playbook/pb_selectable_card/selectable_card.rb +5 -1
  53. data/app/pb_kits/playbook/pb_skeleton_loading/_skeleton_loading.scss +37 -0
  54. data/app/pb_kits/playbook/pb_skeleton_loading/_skeleton_loading.tsx +67 -0
  55. data/app/pb_kits/playbook/pb_skeleton_loading/_skeleton_loading_mixins.scss +40 -0
  56. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_border_radius.jsx +51 -0
  57. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_border_radius.md +1 -0
  58. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_color.jsx +26 -0
  59. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_color.md +1 -0
  60. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_default.html.erb +1 -0
  61. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_default.jsx +11 -0
  62. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_height_width.jsx +59 -0
  63. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_height_width.md +3 -0
  64. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_layout.jsx +20 -0
  65. data/app/pb_kits/playbook/pb_skeleton_loading/docs/_skeleton_loading_layout.md +3 -0
  66. data/app/pb_kits/playbook/pb_skeleton_loading/docs/example.yml +13 -0
  67. data/app/pb_kits/playbook/pb_skeleton_loading/docs/index.js +5 -0
  68. data/app/pb_kits/playbook/pb_skeleton_loading/skeleton_loading.html.erb +12 -0
  69. data/app/pb_kits/playbook/pb_skeleton_loading/skeleton_loading.rb +8 -0
  70. data/app/pb_kits/playbook/pb_skeleton_loading/skeleton_loading.test.jsx +81 -0
  71. data/app/pb_kits/playbook/pb_timeline/_item.tsx +59 -23
  72. data/app/pb_kits/playbook/pb_timeline/_timeline.tsx +8 -0
  73. data/app/pb_kits/playbook/pb_timeline/detail.html.erb +3 -0
  74. data/app/pb_kits/playbook/pb_timeline/detail.rb +11 -0
  75. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_children.html.erb +43 -0
  76. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_children.jsx +68 -0
  77. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_children.md +2 -0
  78. data/app/pb_kits/playbook/pb_timeline/docs/example.yml +2 -1
  79. data/app/pb_kits/playbook/pb_timeline/docs/index.js +1 -0
  80. data/app/pb_kits/playbook/pb_timeline/item.html.erb +17 -21
  81. data/app/pb_kits/playbook/pb_timeline/item.rb +4 -0
  82. data/app/pb_kits/playbook/pb_timeline/label.html.erb +12 -0
  83. data/app/pb_kits/playbook/pb_timeline/label.rb +13 -0
  84. data/app/pb_kits/playbook/pb_timeline/step.html.erb +14 -0
  85. data/app/pb_kits/playbook/pb_timeline/step.rb +16 -0
  86. data/app/pb_kits/playbook/pb_timeline/subcomponents/Detail.tsx +29 -0
  87. data/app/pb_kits/playbook/pb_timeline/subcomponents/Label.tsx +38 -0
  88. data/app/pb_kits/playbook/pb_timeline/subcomponents/Step.tsx +42 -0
  89. data/app/pb_kits/playbook/pb_timeline/subcomponents/index.tsx +3 -0
  90. data/app/pb_kits/playbook/pb_timeline/timeline.test.js +84 -0
  91. data/app/pb_kits/playbook/tokens/_typography.scss +35 -0
  92. data/app/pb_kits/playbook/utilities/_hover.scss +46 -43
  93. data/app/pb_kits/playbook/utilities/globalPropNames.mjs +4 -0
  94. data/app/pb_kits/playbook/utilities/globalProps.ts +44 -2
  95. data/dist/chunks/_typeahead-DhLic2Fe.js +22 -0
  96. data/dist/chunks/_weekday_stacked-Mx8TYP5I.js +45 -0
  97. data/dist/chunks/vendor.js +1 -1
  98. data/dist/menu.yml +6 -315
  99. data/dist/playbook-doc.js +1 -1
  100. data/dist/playbook-rails-react-bindings.js +1 -1
  101. data/dist/playbook-rails.js +1 -1
  102. data/dist/playbook.css +1 -1
  103. data/lib/playbook/hover.rb +4 -1
  104. data/lib/playbook/kit_base.rb +43 -5
  105. data/lib/playbook/version.rb +1 -1
  106. metadata +57 -5
  107. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.md +0 -1
  108. data/dist/chunks/_typeahead-BV_n6U5W.js +0 -22
  109. data/dist/chunks/_weekday_stacked-Dh3OU4s8.js +0 -45
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9eb2e0ae2d54d55dd2211f2ebf92d6dd1bbd037e0adce6ce449806e709f2e863
4
- data.tar.gz: bfc0b7c42e262d18406574777f9888d207b991b42867a26dfd9096c2147223f2
3
+ metadata.gz: db6a77d5d0d5f2f583dec0ae055971d993b86ed35cebb3df70a3eb46c520f60e
4
+ data.tar.gz: 9f214f19a94bce463d9561600773513112947c453c8a87248c4a37740103ac47
5
5
  SHA512:
6
- metadata.gz: 180f8ff481e73e8d6674edf80d1b5fb71bae90b5f6e45f18102b96afd726fc94e8e8519bcedc580d8e625bc0c0e4632ef54272902c7d89c78a33ed05bad9190f
7
- data.tar.gz: 206c4da30889404db6bea0afce512fa0640b4cb7707e9072018acd0afdf0dd906e9c1f3df51a6982298d1f3f0e3c390d5ca700a46f4322c4215872ab4daba48c
6
+ metadata.gz: 6d30fd7598b3c3094e108bfe2f611be040ccdc4ce8bdfdf085c6757e62b386031f890e58607e825da501479e5734974f8134fb4738a0890d1bf8caf5fb923da2
7
+ data.tar.gz: 19958248e4ee7a1a3d13efc7ba1debe4891bc58bd2ff790b33fc272a739a24789d98454b6e61b61210fafb5ceef4af94afe75f27a77122fde54b91fca6e0bd76
@@ -1,4 +1,3 @@
1
-
2
1
  @import 'pb_advanced_table/advanced_table';
3
2
  @import 'pb_avatar/avatar';
4
3
  @import 'pb_avatar_action_button/avatar_action_button';
@@ -30,6 +29,7 @@
30
29
  @import 'pb_dialog/dialog';
31
30
  @import 'pb_distribution_bar/distribution_bar';
32
31
  @import 'pb_draggable/draggable';
32
+ @import 'pb_drawer/drawer';
33
33
  @import 'pb_dropdown/dropdown';
34
34
  @import 'pb_file_upload/file_upload';
35
35
  @import 'pb_filter/filter';
@@ -54,6 +54,7 @@
54
54
  @import 'pb_legend/legend';
55
55
  @import 'pb_lightbox/lightbox';
56
56
  @import 'pb_line_graph/line_graph';
57
+ @import 'pb_link/link';
57
58
  @import 'pb_list/list';
58
59
  @import 'pb_loading_inline/loading_inline';
59
60
  @import 'pb_map/map';
@@ -83,6 +84,7 @@
83
84
  @import 'pb_selectable_card_icon/selectable_card_icon';
84
85
  @import 'pb_selectable_icon/selectable_icon';
85
86
  @import 'pb_selectable_list/selectable_list';
87
+ @import 'pb_skeleton_loading/skeleton_loading';
86
88
  @import 'pb_source/source';
87
89
  @import 'pb_star_rating/star_rating';
88
90
  @import 'pb_stat_change/stat_change';
@@ -106,7 +108,6 @@
106
108
  @import 'pb_user_badge/user_badge';
107
109
  @import 'pb_walkthrough/walkthrough';
108
110
  @import 'pb_weekday_stacked/weekday_stacked';
109
- @import 'pb_drawer/drawer';
110
111
  @import 'utilities/mixins';
111
112
  @import 'utilities/spacing';
112
113
  @import 'utilities/cursor';
@@ -16,6 +16,7 @@ interface CustomCellProps {
16
16
  onRowToggleClick?: (arg: Row<GenericObject>) => void
17
17
  row: Row<GenericObject>
18
18
  value?: string
19
+ customRenderer?: (row: Row<GenericObject>, value: string | undefined) => React.ReactNode
19
20
  }
20
21
 
21
22
  export const CustomCell = ({
@@ -23,6 +24,7 @@ export const CustomCell = ({
23
24
  onRowToggleClick,
24
25
  row,
25
26
  value,
27
+ customRenderer,
26
28
  }: CustomCellProps & GlobalProps) => {
27
29
  const { setExpanded, expanded, expandedControl, inlineRowLoading } = useContext(AdvancedTableContext);
28
30
 
@@ -61,7 +63,12 @@ export const CustomCell = ({
61
63
  </button>
62
64
  ) : null}
63
65
  <FlexItem paddingLeft={renderButton? "none" : "xs"}>
64
- {row.depth === 0 ? getValue() : value}
66
+ {row.depth === 0 ? (
67
+ customRenderer ? customRenderer(row, getValue()) : getValue()
68
+ ) :(
69
+ customRenderer ? customRenderer(row, value) : value
70
+ )
71
+ }
65
72
  </FlexItem>
66
73
  </Flex>
67
74
  </div>
@@ -90,8 +90,8 @@ const AdvancedTable = (props: AdvancedTableProps) => {
90
90
 
91
91
  const columnHelper = createColumnHelper()
92
92
 
93
- //Create cells for first columns
94
- const createCellFunction = (cellAccessors: string[], customRenderer?: (row: Row<GenericObject>, value: any) => JSX.Element) => {
93
+ //Create cells for columns, with customization for first column
94
+ const createCellFunction = (cellAccessors: string[], customRenderer?: (row: Row<GenericObject>, value: any) => JSX.Element, index?: number) => {
95
95
  const columnCells = ({
96
96
  row,
97
97
  getValue,
@@ -101,19 +101,16 @@ 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
+ if (index === 0) {
109
105
  switch (row.depth) {
110
106
  case 0: {
111
107
  return (
112
- <CustomCell
113
- getValue={getValue}
114
- onRowToggleClick={onRowToggleClick}
115
- row={row}
116
- />
108
+ <CustomCell
109
+ customRenderer={customRenderer}
110
+ getValue={getValue}
111
+ onRowToggleClick={onRowToggleClick}
112
+ row={row}
113
+ />
117
114
  )
118
115
  }
119
116
  default: {
@@ -122,6 +119,7 @@ const AdvancedTable = (props: AdvancedTableProps) => {
122
119
  const accessorValue = rowData[depthAccessor]
123
120
  return accessorValue ? (
124
121
  <CustomCell
122
+ customRenderer={customRenderer}
125
123
  onRowToggleClick={onRowToggleClick}
126
124
  row={row}
127
125
  value={accessorValue}
@@ -132,11 +130,13 @@ const AdvancedTable = (props: AdvancedTableProps) => {
132
130
  }
133
131
  }
134
132
  }
135
-
133
+ return customRenderer
134
+ ? customRenderer(row, getValue())
135
+ : getValue()
136
+ }
136
137
  return columnCells
137
138
  }
138
-
139
- //Create column array in format needed by Tanstack
139
+ //Create column array in format needed by Tanstack
140
140
  const columns =
141
141
  columnDefinitions &&
142
142
  columnDefinitions.map((column, index) => {
@@ -147,19 +147,12 @@ const AdvancedTable = (props: AdvancedTableProps) => {
147
147
  }),
148
148
  }
149
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
- }
150
+ if (column.cellAccessors || column.customRenderer) {
151
+ columnStructure.cell = createCellFunction(
152
+ column.cellAccessors,
153
+ column.customRenderer,
154
+ index
155
+ )
163
156
  }
164
157
 
165
158
  return columnStructure
@@ -1,7 +1,7 @@
1
1
  import React, {useState} from "react"
2
2
  import { render, screen, waitFor } from "../utilities/test-utils"
3
3
 
4
- import { AdvancedTable } from "playbook-ui"
4
+ import { AdvancedTable, Pill } from "playbook-ui"
5
5
 
6
6
  const MOCK_DATA = [
7
7
  {
@@ -88,6 +88,28 @@ const columnDefinitions = [
88
88
  },
89
89
  ]
90
90
 
91
+ const columnDefinitionsCustomRenderer = [
92
+ {
93
+ accessor: "year",
94
+ label: "Year",
95
+ cellAccessors: ["quarter", "month", "day"],
96
+ },
97
+ {
98
+ accessor: "newEnrollments",
99
+ label: "New Enrollments",
100
+ customRenderer: (row, value) => (
101
+ <Pill text={value}
102
+ variant="success"
103
+ />
104
+ ),
105
+ },
106
+ {
107
+ accessor: "scheduledMeetings",
108
+ label: "Scheduled Meetings",
109
+ },
110
+ ]
111
+
112
+
91
113
  const subRowHeaders = ["Quarter"]
92
114
 
93
115
  const testId = "advanced_table"
@@ -463,3 +485,17 @@ test("responsive none prop functions as expected", () => {
463
485
  const kit = screen.getByTestId(testId)
464
486
  expect(kit).toHaveClass("pb_advanced_table table-responsive-none")
465
487
  })
488
+
489
+ test("customRenderer prop functions as expected", () => {
490
+ render(
491
+ <AdvancedTable
492
+ columnDefinitions={columnDefinitionsCustomRenderer}
493
+ data={{ testid: testId }}
494
+ tableData={MOCK_DATA}
495
+ />
496
+ )
497
+
498
+ const kit = screen.getByTestId(testId)
499
+ const pill = kit.querySelector(".pb_pill_kit_success_lowercase")
500
+ expect(pill).toBeInTheDocument()
501
+ })
@@ -1,5 +1,5 @@
1
1
  import React from "react"
2
- import { AdvancedTable, Pill, Body, Flex, Detail, Caption } from "playbook-ui"
2
+ import { AdvancedTable, Pill, Body, Flex, Detail, Caption, Badge, Title } from "playbook-ui"
3
3
  import MOCK_DATA from "./advanced_table_mock_data.json"
4
4
 
5
5
  const AdvancedTableCustomCell = (props) => {
@@ -8,7 +8,18 @@ const AdvancedTableCustomCell = (props) => {
8
8
  accessor: "year",
9
9
  label: "Year",
10
10
  cellAccessors: ["quarter", "month", "day"],
11
-
11
+ customRenderer: (row, value) => (
12
+ <Flex>
13
+ <Title size={4}
14
+ text={value}
15
+ />
16
+ <Badge dark
17
+ marginLeft="xxs"
18
+ text={row.original.newEnrollments > 20 ? "High" : "Low"}
19
+ variant="neutral"
20
+ />
21
+ </Flex>
22
+ ),
12
23
  },
13
24
  {
14
25
  accessor: "newEnrollments",
@@ -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,10 @@ 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);
112
+ const { style: htmlStyle = {}, ...restHtmlProps } = htmlProps as { style?: React.CSSProperties };
113
+ const mergedStyles: React.CSSProperties = { ...htmlStyle, ...dynamicInlineProps };
114
+
110
115
 
111
116
  const subComponentTags = (tagName: string) => {
112
117
  return cardChildren.filter((c: string) => (
@@ -122,7 +127,7 @@ const Card = (props: CardPropTypes): React.ReactElement => {
122
127
 
123
128
  const tagOptions = ['div', 'section', 'footer', 'header', 'article', 'aside', 'main', 'nav']
124
129
  const Tag = tagOptions.includes(tag) ? tag : 'div'
125
-
130
+
126
131
  return (
127
132
  <>
128
133
  {
@@ -133,8 +138,9 @@ const Card = (props: CardPropTypes): React.ReactElement => {
133
138
  <Tag
134
139
  {...ariaProps}
135
140
  {...dataProps}
136
- {...htmlProps}
137
141
  className={classnames(cardCss, globalProps(props), className)}
142
+ {...restHtmlProps}
143
+ style={mergedStyles}
138
144
  >
139
145
  {subComponentTags('Header')}
140
146
  {
@@ -161,8 +167,9 @@ const Card = (props: CardPropTypes): React.ReactElement => {
161
167
  <Tag
162
168
  {...ariaProps}
163
169
  {...dataProps}
164
- {...htmlProps}
165
170
  className={classnames(cardCss, globalProps(props), className)}
171
+ {...restHtmlProps}
172
+ style={mergedStyles}
166
173
  >
167
174
  {subComponentTags('Header')}
168
175
  {nonHeaderChildren}
@@ -28,8 +28,7 @@ $pb_card_header_colors: map-merge(map-merge($product_colors, $additional_colors)
28
28
 
29
29
  @mixin pb_card_selected($border_color: $primary) {
30
30
  border-color: $border_color;
31
- border-width: $pb_card_border_width;
32
- outline: 1px solid $border_color;
31
+ border-width: $pb_card_border_width * 2;
33
32
  }
34
33
 
35
34
  @mixin pb_card_selected_dark {
@@ -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
+ commaSeparator
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
+ commaSeparator
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
+ commaSeparator
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
@@ -0,0 +1,3 @@
1
+ The optional `commaSeparator` can be used to auto-format the use of commas as a thousands separator.
2
+
3
+ **NOTE:** If the value passed into the `amount` prop is already comma-dilineated, it will not add additional commas.
@@ -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'
@@ -28,5 +28,8 @@
28
28
  [class^=pb_title_kit] {
29
29
  color: $text_dk_default !important;
30
30
  }
31
+ [class^=pb_body_kit], [class^=pb_caption_kit] {
32
+ color: $text_dk_light !important;
33
+ }
31
34
  }
32
35
  }
@@ -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"
@@ -47,7 +47,7 @@ interface DropdownComponent
47
47
  Container: typeof DropdownContainer;
48
48
  }
49
49
 
50
- const Dropdown = forwardRef((props: DropdownProps, ref: any) => {
50
+ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
51
51
  const {
52
52
  aria = {},
53
53
  autocomplete = false,
@@ -260,7 +260,7 @@ const Dropdown = forwardRef((props: DropdownProps, ref: any) => {
260
260
  <DropdownContainer>
261
261
  {optionsWithBlankSelection &&
262
262
  optionsWithBlankSelection?.map((option: GenericObject) => (
263
- <Dropdown.Option key={option.id}
263
+ <DropdownOption key={option.id}
264
264
  option={option}
265
265
  />
266
266
  ))}
@@ -278,11 +278,12 @@ const Dropdown = forwardRef((props: DropdownProps, ref: any) => {
278
278
  </DropdownContext.Provider>
279
279
  </div>
280
280
  )
281
- }) as DropdownComponent
281
+ }
282
282
 
283
- Dropdown.displayName = "Dropdown";
284
- Dropdown.Option = DropdownOption;
285
- Dropdown.Trigger = DropdownTrigger;
286
- Dropdown.Container = DropdownContainer;
283
+ Dropdown = forwardRef(Dropdown) as unknown as DropdownComponent;
284
+ (Dropdown as DropdownComponent).displayName = "Dropdown";
285
+ (Dropdown as DropdownComponent).Option = DropdownOption;
286
+ (Dropdown as DropdownComponent).Trigger = DropdownTrigger;
287
+ (Dropdown as DropdownComponent).Container = DropdownContainer;
287
288
 
288
289
  export default Dropdown;
@@ -1,7 +1,7 @@
1
1
  import React from 'react'
2
2
  import classnames from 'classnames'
3
3
  import { buildCss, buildDataProps, buildHtmlProps } from '../utilities/props'
4
- import { GlobalProps, globalProps } from '../utilities/globalProps'
4
+ import { GlobalProps, globalProps, globalInlineProps } from '../utilities/globalProps'
5
5
  import { GenericObject, Sizes } from '../types'
6
6
 
7
7
  type FlexProps = {
@@ -61,6 +61,7 @@ const Flex = (props: FlexProps): React.ReactElement => {
61
61
  const alignSelfClass = alignSelf !== 'none' ? `align_self_${alignSelf}` : ''
62
62
  const dataProps = buildDataProps(data)
63
63
  const htmlProps = buildHtmlProps(htmlOptions)
64
+ const dynamicInlineProps = globalInlineProps(props)
64
65
 
65
66
 
66
67
  return (
@@ -83,6 +84,7 @@ const Flex = (props: FlexProps): React.ReactElement => {
83
84
  globalProps(props),
84
85
  className
85
86
  )}
87
+ style={dynamicInlineProps}
86
88
  {...dataProps}
87
89
  {...htmlProps}
88
90
  >