playbook_ui 15.3.0.pre.alpha.play2541phonenumberkitvalidation12084 → 15.3.0.pre.alpha.play199912019
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/pb_kits/playbook/pb_advanced_table/Components/RegularTableView.tsx +1 -2
- data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +0 -63
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_background_control_rails.html.erb +0 -4
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_background_control_rails.md +1 -1
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling.jsx +1 -3
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling.md +0 -2
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_column_headers.jsx +1 -1
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_rails.html.erb +0 -1
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_rails.md +0 -2
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_padding_control.jsx +1 -9
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_padding_control.md +1 -1
- data/app/pb_kits/playbook/pb_advanced_table/table_row.rb +2 -31
- data/app/pb_kits/playbook/pb_background/background.html.erb +2 -10
- data/app/pb_kits/playbook/pb_badge/_badge.tsx +3 -0
- data/app/pb_kits/playbook/pb_badge/badge.test.js +13 -0
- data/app/pb_kits/playbook/pb_currency/_currency.tsx +7 -20
- data/app/pb_kits/playbook/pb_currency/currency.rb +8 -35
- data/app/pb_kits/playbook/pb_currency/currency.test.js +0 -47
- data/app/pb_kits/playbook/pb_currency/docs/_currency_variants.html.erb +1 -1
- data/app/pb_kits/playbook/pb_currency/docs/_currency_variants.jsx +1 -1
- data/app/pb_kits/playbook/pb_form/pb_form_validation.js +11 -44
- data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +17 -110
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.scss +7 -0
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.test.jsx +64 -1
- data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +11 -1
- data/dist/chunks/{_line_graph-CZlP3Ci7.js → _line_graph-D-adVl2G.js} +1 -1
- data/dist/chunks/_typeahead-BM-vXmpm.js +6 -0
- data/dist/chunks/{_weekday_stacked-qGKMxoAo.js → _weekday_stacked-BQORBqJE.js} +2 -2
- data/dist/chunks/pb_form_validation-DebqlUKZ.js +1 -0
- data/dist/chunks/vendor.js +1 -1
- data/dist/playbook-doc.js +1 -1
- data/dist/playbook-rails-react-bindings.js +1 -1
- data/dist/playbook-rails.js +1 -1
- data/dist/playbook.css +1 -1
- data/lib/playbook/version.rb +1 -1
- metadata +6 -7
- data/app/pb_kits/playbook/pb_currency/docs/_currency_variants.md +0 -1
- data/dist/chunks/_typeahead-gfPOrCeR.js +0 -6
- data/dist/chunks/pb_form_validation-CbtOLNdG.js +0 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 388214df2dc5b88974e4f1828638e5c89e1b8212be4d4410e14a0aa32fcaa1fc
|
|
4
|
+
data.tar.gz: 3e3a56e4740c4eb97d7e2cc3b9876db5a9225ffab834f1a0ecc376f0b3b80d9f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ee8599340aa8e11f95157d225b52348bb40cd9204734cea8eeac72be58156e544ae8cf6eb1152ed65c01ec6e84892bb53431f673db7f59831c3de5d8ea42279e
|
|
7
|
+
data.tar.gz: 8b655b10ab1cc172c7211ba6d08ba6591e8dc5eed375187855806cb999ddbbc48e10cd4a639e66bde17610f8a6bac9bd66488415e8ac1c9b36c70770a6cc0da2
|
|
@@ -66,7 +66,6 @@ const TableCellRenderer = ({
|
|
|
66
66
|
// Find the “owning” colDefinition by accessor. Needed for multi column logic
|
|
67
67
|
const colDef = findColumnDefByAccessor(columnDefinitions ?? [], column.id)
|
|
68
68
|
const cellAlignment = colDef?.columnStyling?.cellAlignment ?? "right"
|
|
69
|
-
const cellFontColor = colDef?.columnStyling?.fontColor
|
|
70
69
|
const paddingValue = colDef?.columnStyling?.cellPadding ?? customRowStyle?.cellPadding
|
|
71
70
|
const paddingClass = paddingValue ? `p_${paddingValue}` : undefined
|
|
72
71
|
|
|
@@ -89,7 +88,7 @@ const TableCellRenderer = ({
|
|
|
89
88
|
: `${column.getStart("left")}px`
|
|
90
89
|
: undefined,
|
|
91
90
|
backgroundColor: i === 0 && customRowStyle?.backgroundColor,
|
|
92
|
-
color:
|
|
91
|
+
color: customRowStyle?.fontColor,
|
|
93
92
|
}}
|
|
94
93
|
>
|
|
95
94
|
{collapsibleTrail && i === 0 && row.depth > 0 && renderCollapsibleTrail(row.depth)}
|
|
@@ -740,69 +740,6 @@ test("columnStyling.cellPadding sets cell padding", () => {
|
|
|
740
740
|
expect(firstEnrollmentCell).toHaveClass('p_none')
|
|
741
741
|
});
|
|
742
742
|
|
|
743
|
-
test("columnStyling.fontColor sets cell font color", () => {
|
|
744
|
-
const styledColumnDefs = [
|
|
745
|
-
{
|
|
746
|
-
accessor: "year",
|
|
747
|
-
label: "Year",
|
|
748
|
-
cellAccessors: ["quarter", "month", "day"],
|
|
749
|
-
},
|
|
750
|
-
{
|
|
751
|
-
accessor: "newEnrollments",
|
|
752
|
-
label: "New Enrollments",
|
|
753
|
-
columnStyling: { fontColor: colors.category_1 },
|
|
754
|
-
},
|
|
755
|
-
{
|
|
756
|
-
accessor: "scheduledMeetings",
|
|
757
|
-
label: "Scheduled Meetings",
|
|
758
|
-
},
|
|
759
|
-
];
|
|
760
|
-
|
|
761
|
-
render(
|
|
762
|
-
<AdvancedTable
|
|
763
|
-
columnDefinitions={styledColumnDefs}
|
|
764
|
-
data={{ testid: testId }}
|
|
765
|
-
tableData={MOCK_DATA}
|
|
766
|
-
/>
|
|
767
|
-
);
|
|
768
|
-
|
|
769
|
-
const firstEnrollmentCell = screen.getAllByText("20")[0].closest("td");
|
|
770
|
-
expect(firstEnrollmentCell).toHaveStyle({ color: colors.category_1 });
|
|
771
|
-
});
|
|
772
|
-
|
|
773
|
-
test("columnStyling.fontColor works with background color", () => {
|
|
774
|
-
const styledColumnDefs = [
|
|
775
|
-
{
|
|
776
|
-
accessor: "year",
|
|
777
|
-
label: "Year",
|
|
778
|
-
cellAccessors: ["quarter", "month", "day"],
|
|
779
|
-
},
|
|
780
|
-
{
|
|
781
|
-
accessor: "newEnrollments",
|
|
782
|
-
label: "New Enrollments",
|
|
783
|
-
columnStyling: {
|
|
784
|
-
cellBackgroundColor: (row) => row.newEnrollments > 20 ? "success_secondary" : "warning_secondary",
|
|
785
|
-
fontColor: colors.white
|
|
786
|
-
},
|
|
787
|
-
},
|
|
788
|
-
{
|
|
789
|
-
accessor: "scheduledMeetings",
|
|
790
|
-
label: "Scheduled Meetings",
|
|
791
|
-
},
|
|
792
|
-
];
|
|
793
|
-
|
|
794
|
-
render(
|
|
795
|
-
<AdvancedTable
|
|
796
|
-
columnDefinitions={styledColumnDefs}
|
|
797
|
-
data={{ testid: testId }}
|
|
798
|
-
tableData={MOCK_DATA}
|
|
799
|
-
/>
|
|
800
|
-
);
|
|
801
|
-
|
|
802
|
-
const firstEnrollmentCell = screen.getAllByText("20")[0].closest("td");
|
|
803
|
-
expect(firstEnrollmentCell).toHaveStyle({ color: colors.white });
|
|
804
|
-
});
|
|
805
|
-
|
|
806
743
|
test("renders virtualized table rows and header", () => {
|
|
807
744
|
render(
|
|
808
745
|
<AdvancedTable
|
data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_background_control_rails.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
`column_styling` can also be used to control the background color on all cells in a given column via the use of the `cell_background_color` key/value pair. Use `cell_background_color` to achieve custom background colors for individual cells as seen here.
|
|
1
|
+
`column_styling` can also be used to control the background color on all cells in a given column via the use of the `cell_background_color` key/value pair. Use `cell_background_color` to achieve custom background colors for individual cells as seen here.
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import React from "react"
|
|
2
|
-
import
|
|
2
|
+
import AdvancedTable from '../../pb_advanced_table/_advanced_table'
|
|
3
3
|
import MOCK_DATA from "./advanced_table_mock_data.json"
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
const AdvancedTableColumnStyling = (props) => {
|
|
7
6
|
const columnDefinitions = [
|
|
8
7
|
{
|
|
@@ -35,7 +34,6 @@ const AdvancedTableColumnStyling = (props) => {
|
|
|
35
34
|
{
|
|
36
35
|
accessor: "graduatedStudents",
|
|
37
36
|
label: "Graduated Students",
|
|
38
|
-
columnStyling:{fontColor: colors.data_8},
|
|
39
37
|
},
|
|
40
38
|
]
|
|
41
39
|
|
|
@@ -4,6 +4,4 @@ The `columnStyling` prop is an optional item that can be used within `columnDefi
|
|
|
4
4
|
|
|
5
5
|
2) `cellAlignment`: This will allow you to control alignment of content within all cells in the given column. This is set to right aligned by default. you can set this to `left`, `right` or `center`.
|
|
6
6
|
|
|
7
|
-
3) `fontColor`: This will allow you to control the font color for a given column.
|
|
8
|
-
|
|
9
7
|
`columnStyling` can be used within the columnDefinition of all the columns or some of them, as shown. Each column has its own individual control in this way.
|
|
@@ -4,6 +4,4 @@ The `column_styling` prop is an optional item that can be used within `column_de
|
|
|
4
4
|
|
|
5
5
|
2) `cell_alignment`: This will allow you to control alignment of content within all cells in the given column. This is set to right aligned by default. you can set this to `left`, `right` or `center`.
|
|
6
6
|
|
|
7
|
-
3) `font_color`: This will allow you to control the font color for a given column.
|
|
8
|
-
|
|
9
7
|
`column_styling` can be used within the column_definition of all the columns or some of them, as shown. Each column has its own individual control in this way.
|
|
@@ -22,6 +22,7 @@ const AdvancedTablePaddingControl = (props) => {
|
|
|
22
22
|
{value}
|
|
23
23
|
</Background>
|
|
24
24
|
),
|
|
25
|
+
|
|
25
26
|
},
|
|
26
27
|
{
|
|
27
28
|
accessor: "scheduledMeetings",
|
|
@@ -38,15 +39,6 @@ const AdvancedTablePaddingControl = (props) => {
|
|
|
38
39
|
{
|
|
39
40
|
accessor: "classCompletionRate",
|
|
40
41
|
label: "Class Completion Rate",
|
|
41
|
-
columnStyling:{cellPadding: "none", fontColor: "white"},
|
|
42
|
-
customRenderer: (row, value) => (
|
|
43
|
-
<Background
|
|
44
|
-
backgroundColor={"category_1"}
|
|
45
|
-
padding="xs"
|
|
46
|
-
>
|
|
47
|
-
{value}
|
|
48
|
-
</Background>
|
|
49
|
-
),
|
|
50
42
|
},
|
|
51
43
|
{
|
|
52
44
|
accessor: "graduatedStudents",
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
`columnStyling` can also be used to control padding on all cells in a given column via the use of the `cellPadding` key/value pair. `cellPadding` lets you use 'xxs', 'xs', 'sm', 'md', 'lg', 'xl' and 'none'.
|
|
2
2
|
|
|
3
|
-
This control can be used in conjunction with the `customRenderer` item within each columnDefinition to achieve custom background colors for individual cells as seen here.
|
|
3
|
+
This control can be used in conjunction with the `customRenderer` item within each columnDefinition to achieve custom background colors for individual cells as seen here.
|
|
@@ -79,44 +79,15 @@ module Playbook
|
|
|
79
79
|
cell_background_color(column).present?
|
|
80
80
|
end
|
|
81
81
|
|
|
82
|
-
def cell_font_color(column)
|
|
83
|
-
return nil unless column[:accessor].present?
|
|
84
|
-
|
|
85
|
-
orig_def = find_column_def_by_accessor(column_definitions, column[:accessor])
|
|
86
|
-
if orig_def && orig_def[:column_styling].is_a?(Hash) && orig_def[:column_styling][:font_color].present?
|
|
87
|
-
font_color = orig_def[:column_styling][:font_color]
|
|
88
|
-
if font_color.respond_to?(:call)
|
|
89
|
-
font_color.call(row)
|
|
90
|
-
else
|
|
91
|
-
font_color
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
82
|
# Uses a regular table/table_cell component if there is no custom background color; if there is a cell_background_color uses a background component with tag "td"
|
|
97
83
|
def cell_component_info(column, index, bg_color, font_color)
|
|
98
|
-
column_font_color = cell_font_color(column)
|
|
99
|
-
effective_font_color = column_font_color || font_color
|
|
100
|
-
|
|
101
84
|
if has_custom_background_color?(column)
|
|
102
85
|
custom_bg_color = cell_background_color(column)
|
|
103
86
|
component_name = "background"
|
|
104
|
-
component_props = {
|
|
105
|
-
background_color: custom_bg_color,
|
|
106
|
-
tag: "td",
|
|
107
|
-
classname: td_classname(column, index),
|
|
108
|
-
}
|
|
109
|
-
component_props[:html_options] = { style: { color: effective_font_color } } if effective_font_color.present?
|
|
87
|
+
component_props = { background_color: custom_bg_color, tag: "td", classname: td_classname(column, index) }
|
|
110
88
|
else
|
|
111
89
|
component_name = "table/table_cell"
|
|
112
|
-
|
|
113
|
-
style_hash[:color] = effective_font_color if effective_font_color.present?
|
|
114
|
-
component_props = {
|
|
115
|
-
html_options: {
|
|
116
|
-
style: style_hash,
|
|
117
|
-
},
|
|
118
|
-
classname: td_classname(column, index),
|
|
119
|
-
}
|
|
90
|
+
component_props = { html_options: { style: { "background-color": bg_color, color: font_color } }, classname: td_classname(column, index) }
|
|
120
91
|
end
|
|
121
92
|
|
|
122
93
|
{ name: component_name, props: component_props }
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
<%
|
|
2
|
-
html_options_style = ""
|
|
3
|
-
if object.html_options[:style].is_a?(Hash)
|
|
4
|
-
html_options_style = object.html_options[:style].map { |k, v| "#{k.to_s.tr('_', '-')}: #{v}" }.join("; ")
|
|
5
|
-
elsif object.html_options[:style].is_a?(String)
|
|
6
|
-
html_options_style = object.html_options[:style]
|
|
7
|
-
end
|
|
8
|
-
%>
|
|
9
1
|
<% if object.image_url.present? %>
|
|
10
2
|
<%= pb_content_tag(object.tag,
|
|
11
3
|
style: "background-image: url('#{object.image_url}');
|
|
@@ -17,8 +9,8 @@
|
|
|
17
9
|
<% end %>
|
|
18
10
|
<% else %>
|
|
19
11
|
<%= pb_content_tag(object.tag,
|
|
20
|
-
style:
|
|
12
|
+
style: object.custom_background_color
|
|
21
13
|
) do %>
|
|
22
14
|
<%= content.presence %>
|
|
23
15
|
<% end %>
|
|
24
|
-
<% end %>
|
|
16
|
+
<% end %>
|
|
@@ -25,6 +25,7 @@ type BadgeProps = {
|
|
|
25
25
|
removeIcon?: boolean,
|
|
26
26
|
removeOnClick?: React.MouseEventHandler<HTMLSpanElement>,
|
|
27
27
|
rounded?: boolean,
|
|
28
|
+
tabIndex?: number,
|
|
28
29
|
text?: string,
|
|
29
30
|
variant?: "error" | "info" | "neutral" | "notification" | "notificationError" | "primary" | "success" | "warning",
|
|
30
31
|
} & GlobalProps
|
|
@@ -39,6 +40,7 @@ const Badge = (props: BadgeProps): React.ReactElement => {
|
|
|
39
40
|
removeIcon = false,
|
|
40
41
|
removeOnClick,
|
|
41
42
|
rounded = false,
|
|
43
|
+
tabIndex,
|
|
42
44
|
text,
|
|
43
45
|
variant = 'neutral',
|
|
44
46
|
} = props
|
|
@@ -61,6 +63,7 @@ const Badge = (props: BadgeProps): React.ReactElement => {
|
|
|
61
63
|
{...htmlProps}
|
|
62
64
|
className={css}
|
|
63
65
|
id={id}
|
|
66
|
+
tabIndex={tabIndex}
|
|
64
67
|
>
|
|
65
68
|
<span>
|
|
66
69
|
{text}
|
|
@@ -112,3 +112,16 @@ test('displays notification variants', () => {
|
|
|
112
112
|
cleanup()
|
|
113
113
|
})
|
|
114
114
|
})
|
|
115
|
+
|
|
116
|
+
test('should allow tabIndex to be set', () => {
|
|
117
|
+
render(
|
|
118
|
+
<Badge
|
|
119
|
+
data={{ testid: testId }}
|
|
120
|
+
tabIndex={0}
|
|
121
|
+
text="+1"
|
|
122
|
+
/>
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
const kit = screen.getByTestId(testId)
|
|
126
|
+
expect(kit).toHaveAttribute('tabIndex', '0')
|
|
127
|
+
})
|
|
@@ -11,7 +11,7 @@ import Title from '../pb_title/_title'
|
|
|
11
11
|
type CurrencyProps = {
|
|
12
12
|
abbreviate?: boolean,
|
|
13
13
|
align?: 'center' | 'left' | 'right',
|
|
14
|
-
amount: string
|
|
14
|
+
amount: string,
|
|
15
15
|
aria?: {[key:string]:string},
|
|
16
16
|
className?: string,
|
|
17
17
|
dark?: boolean,
|
|
@@ -59,19 +59,6 @@ const Currency = (props: CurrencyProps): React.ReactElement => {
|
|
|
59
59
|
commaSeparator = false,
|
|
60
60
|
} = props
|
|
61
61
|
|
|
62
|
-
// Convert numeric input to string format
|
|
63
|
-
const convertAmount = (input: string | number): string => {
|
|
64
|
-
if (typeof input === 'number') {
|
|
65
|
-
if (input === 0 && !nullDisplay) {
|
|
66
|
-
return ""
|
|
67
|
-
}
|
|
68
|
-
return input.toFixed(2)
|
|
69
|
-
}
|
|
70
|
-
return input
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const currencyAmount = convertAmount(amount)
|
|
74
|
-
|
|
75
62
|
const emphasizedClass = emphasized ? '' : '_deemphasized'
|
|
76
63
|
|
|
77
64
|
let variantClass
|
|
@@ -81,7 +68,7 @@ const Currency = (props: CurrencyProps): React.ReactElement => {
|
|
|
81
68
|
variantClass = '_bold'
|
|
82
69
|
}
|
|
83
70
|
|
|
84
|
-
const [whole, decimal = '00'] =
|
|
71
|
+
const [whole, decimal = '00'] = amount.split('.')
|
|
85
72
|
const ariaProps = buildAriaProps(aria)
|
|
86
73
|
const dataProps = buildDataProps(data)
|
|
87
74
|
const htmlProps = buildHtmlProps(htmlOptions)
|
|
@@ -105,19 +92,19 @@ const Currency = (props: CurrencyProps): React.ReactElement => {
|
|
|
105
92
|
return isAmount ? num.slice(0, -1) : isUnit ? num.slice(-1) : ''
|
|
106
93
|
}
|
|
107
94
|
|
|
108
|
-
const getMatchingDecimalAmount = decimals === "matching" ?
|
|
95
|
+
const getMatchingDecimalAmount = decimals === "matching" ? amount : whole
|
|
109
96
|
const getMatchingDecimalValue = decimals === "matching" ? '' : `.${decimal}`
|
|
110
97
|
|
|
111
98
|
const formatAmount = (amount: string) => {
|
|
112
99
|
if (!commaSeparator) return amount;
|
|
113
|
-
|
|
100
|
+
|
|
114
101
|
const [wholePart, decimalPart] = amount.split('.');
|
|
115
102
|
const formattedWhole = new Intl.NumberFormat('en-US').format(parseInt(wholePart));
|
|
116
103
|
return decimalPart ? `${formattedWhole}.${decimalPart}` : formattedWhole;
|
|
117
104
|
}
|
|
118
105
|
|
|
119
106
|
const swapNegative = size === "sm" && symbol !== ""
|
|
120
|
-
const handleNegative =
|
|
107
|
+
const handleNegative = amount.startsWith("-") && swapNegative ? "-" : ""
|
|
121
108
|
const getAbsoluteAmount = (amountString: string) => amountString.replace(/^-/,'')
|
|
122
109
|
const getAbbrOrFormatAmount = abbreviate ? getAbbreviatedValue('amount') : formatAmount(getMatchingDecimalAmount)
|
|
123
110
|
const getAmount = swapNegative ? getAbsoluteAmount(getAbbrOrFormatAmount) : getAbbrOrFormatAmount
|
|
@@ -165,7 +152,7 @@ const Currency = (props: CurrencyProps): React.ReactElement => {
|
|
|
165
152
|
>
|
|
166
153
|
{handleNegative}{symbol}
|
|
167
154
|
</Body>
|
|
168
|
-
|
|
155
|
+
|
|
169
156
|
<Title
|
|
170
157
|
className="pb_currency_value"
|
|
171
158
|
dark={dark}
|
|
@@ -173,7 +160,7 @@ const Currency = (props: CurrencyProps): React.ReactElement => {
|
|
|
173
160
|
>
|
|
174
161
|
{getAmount}
|
|
175
162
|
</Title>
|
|
176
|
-
|
|
163
|
+
|
|
177
164
|
<Body
|
|
178
165
|
className="unit"
|
|
179
166
|
color="light"
|
|
@@ -17,7 +17,8 @@ module Playbook
|
|
|
17
17
|
prop :symbol, type: Playbook::Props::String,
|
|
18
18
|
default: "$"
|
|
19
19
|
|
|
20
|
-
prop :amount,
|
|
20
|
+
prop :amount, type: Playbook::Props::String,
|
|
21
|
+
required: true
|
|
21
22
|
|
|
22
23
|
prop :unit, type: Playbook::Props::String,
|
|
23
24
|
required: false
|
|
@@ -91,7 +92,7 @@ module Playbook
|
|
|
91
92
|
end
|
|
92
93
|
|
|
93
94
|
def negative_sign
|
|
94
|
-
|
|
95
|
+
amount.starts_with?("-") && swap_negative ? "-" : ""
|
|
95
96
|
end
|
|
96
97
|
|
|
97
98
|
def body_props
|
|
@@ -116,32 +117,10 @@ module Playbook
|
|
|
116
117
|
end
|
|
117
118
|
end
|
|
118
119
|
|
|
119
|
-
def currency_amount
|
|
120
|
-
@currency_amount ||= convert_amount(amount)
|
|
121
|
-
end
|
|
122
|
-
|
|
123
120
|
private
|
|
124
121
|
|
|
125
|
-
# Convert numeric input to string format
|
|
126
|
-
def convert_amount(input)
|
|
127
|
-
if input.is_a?(Numeric)
|
|
128
|
-
if input.zero? && null_display.nil?
|
|
129
|
-
""
|
|
130
|
-
else
|
|
131
|
-
format("%.2f", input)
|
|
132
|
-
end
|
|
133
|
-
# Handle string representations of zero
|
|
134
|
-
elsif input.to_s.strip.match?(/^-?0+(\.0+)?$/) && null_display.nil?
|
|
135
|
-
""
|
|
136
|
-
else
|
|
137
|
-
input.to_s
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
|
|
141
122
|
def whole_value
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
value = currency_amount.split(".").first
|
|
123
|
+
value = amount.split(".").first
|
|
145
124
|
if comma_separator
|
|
146
125
|
number_with_delimiter(value.gsub(",", ""))
|
|
147
126
|
else
|
|
@@ -150,9 +129,7 @@ module Playbook
|
|
|
150
129
|
end
|
|
151
130
|
|
|
152
131
|
def decimal_value
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
currency_amount.split(".")[1] || "00"
|
|
132
|
+
amount.split(".")[1] || "00"
|
|
156
133
|
end
|
|
157
134
|
|
|
158
135
|
def units_element
|
|
@@ -170,9 +147,7 @@ module Playbook
|
|
|
170
147
|
end
|
|
171
148
|
|
|
172
149
|
def abbreviated_value(index = 0..-2)
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
value = currency_amount.split(".").first.gsub(",", "").to_i
|
|
150
|
+
value = amount.split(".").first.gsub(",", "").to_i
|
|
176
151
|
abbreviated_num = number_to_human(value, units: { thousand: "K", million: "M", billion: "B", trillion: "T" }).gsub(/\s+/, "")
|
|
177
152
|
abbreviated_num[index]
|
|
178
153
|
end
|
|
@@ -199,11 +174,9 @@ module Playbook
|
|
|
199
174
|
|
|
200
175
|
if decimals == "matching"
|
|
201
176
|
if comma_separator
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
number_with_delimiter(currency_amount.gsub(",", ""))
|
|
177
|
+
number_with_delimiter(amount.gsub(",", ""))
|
|
205
178
|
else
|
|
206
|
-
|
|
179
|
+
amount
|
|
207
180
|
end
|
|
208
181
|
else
|
|
209
182
|
whole_value
|
|
@@ -133,50 +133,3 @@ test('handles negative amounts correctly', () => {
|
|
|
133
133
|
expect(screen.getByTestId('test-negative-no-symbol')).toHaveTextContent('-400.50')
|
|
134
134
|
expect(screen.getByTestId('test-negative-medium-size')).toHaveTextContent('$-500.55')
|
|
135
135
|
})
|
|
136
|
-
|
|
137
|
-
test('handles numeric amounts correctly', () => {
|
|
138
|
-
render(
|
|
139
|
-
<>
|
|
140
|
-
<Currency
|
|
141
|
-
amount={320}
|
|
142
|
-
data={{ testid: 'test-numeric-default' }}
|
|
143
|
-
/>
|
|
144
|
-
<Currency
|
|
145
|
-
abbreviate
|
|
146
|
-
amount={3200000}
|
|
147
|
-
data={{ testid: 'test-numeric-millions' }}
|
|
148
|
-
/>
|
|
149
|
-
<Currency
|
|
150
|
-
amount={123456.78}
|
|
151
|
-
commaSeparator
|
|
152
|
-
data={{ testid: 'test-numeric-comma-decimals' }}
|
|
153
|
-
/>
|
|
154
|
-
<Currency
|
|
155
|
-
amount={400.50}
|
|
156
|
-
data={{ testid: 'test-numeric-no-symbol' }}
|
|
157
|
-
symbol=""
|
|
158
|
-
/>
|
|
159
|
-
<Currency
|
|
160
|
-
amount={500.55}
|
|
161
|
-
data={{ testid: 'test-numeric-medium-size' }}
|
|
162
|
-
size="md"
|
|
163
|
-
/>
|
|
164
|
-
<Currency
|
|
165
|
-
amount={-600.70}
|
|
166
|
-
data={{ testid: 'test-numeric-negative' }}
|
|
167
|
-
/>
|
|
168
|
-
<Currency
|
|
169
|
-
amount={0.00}
|
|
170
|
-
data={{ testid: 'test-numeric-null' }}
|
|
171
|
-
/>
|
|
172
|
-
</>
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
expect(screen.getByTestId('test-numeric-default')).toHaveTextContent('$320')
|
|
176
|
-
expect(screen.getByTestId('test-numeric-millions')).toHaveTextContent('$3.2M')
|
|
177
|
-
expect(screen.getByTestId('test-numeric-comma-decimals')).toHaveTextContent('$123,456.78')
|
|
178
|
-
expect(screen.getByTestId('test-numeric-no-symbol')).toHaveTextContent('400.50')
|
|
179
|
-
expect(screen.getByTestId('test-numeric-medium-size')).toHaveTextContent('$500.55')
|
|
180
|
-
expect(screen.getByTestId('test-numeric-negative')).toHaveTextContent('-$600.70')
|
|
181
|
-
expect(screen.getByTestId('test-numeric-null')).toHaveTextContent('$.00')
|
|
182
|
-
})
|
|
@@ -8,13 +8,13 @@ const ERROR_MESSAGE_SELECTOR = '.pb_body_kit_negative'
|
|
|
8
8
|
// Validation selectors
|
|
9
9
|
const FORM_SELECTOR = 'form[data-pb-form-validation="true"]'
|
|
10
10
|
const REQUIRED_FIELDS_SELECTOR = 'input[required],textarea[required],select[required]'
|
|
11
|
-
const PHONE_NUMBER_VALIDATION_ERROR_SELECTOR = '[data-pb-phone-validation-error="true"]'
|
|
12
11
|
|
|
13
12
|
const FIELD_EVENTS = [
|
|
14
13
|
'change',
|
|
15
14
|
'valid',
|
|
16
15
|
'invalid',
|
|
17
16
|
]
|
|
17
|
+
|
|
18
18
|
class PbFormValidation extends PbEnhancedElement {
|
|
19
19
|
static get selector() {
|
|
20
20
|
return FORM_SELECTOR
|
|
@@ -22,27 +22,12 @@ class PbFormValidation extends PbEnhancedElement {
|
|
|
22
22
|
|
|
23
23
|
connect() {
|
|
24
24
|
this.formValidationFields.forEach((field) => {
|
|
25
|
-
// Skip phone number inputs - they handle their own validation
|
|
26
|
-
const isPhoneNumberInput = field.closest('.pb_phone_number_input')
|
|
27
|
-
if (isPhoneNumberInput) return
|
|
28
|
-
|
|
29
25
|
FIELD_EVENTS.forEach((e) => {
|
|
30
26
|
field.addEventListener(e, debounce((event) => {
|
|
31
27
|
this.validateFormField(event)
|
|
32
28
|
}, 250), false)
|
|
33
29
|
})
|
|
34
30
|
})
|
|
35
|
-
|
|
36
|
-
// Add event listener to check for phone number validation errors
|
|
37
|
-
this.element.addEventListener('submit', (event) => {
|
|
38
|
-
// Use setTimeout to ensure React state updates have completed
|
|
39
|
-
setTimeout(() => {
|
|
40
|
-
if (this.hasPhoneNumberValidationErrors()) {
|
|
41
|
-
event.preventDefault()
|
|
42
|
-
return false
|
|
43
|
-
}
|
|
44
|
-
}, 0)
|
|
45
|
-
})
|
|
46
31
|
}
|
|
47
32
|
|
|
48
33
|
validateFormField(event) {
|
|
@@ -60,58 +45,40 @@ class PbFormValidation extends PbEnhancedElement {
|
|
|
60
45
|
|
|
61
46
|
showValidationMessage(target) {
|
|
62
47
|
const { parentElement } = target
|
|
63
|
-
const kitElement = parentElement.closest(KIT_SELECTOR)
|
|
64
|
-
|
|
65
|
-
// FIX: Add null check for kitElement
|
|
66
|
-
if (!kitElement) return
|
|
67
|
-
|
|
68
|
-
// Check if this is a phone number input
|
|
69
|
-
const isPhoneNumberInput = kitElement.classList.contains('pb_phone_number_input')
|
|
70
48
|
|
|
71
49
|
// ensure clean error message state
|
|
72
50
|
this.clearError(target)
|
|
73
|
-
|
|
51
|
+
parentElement.closest(KIT_SELECTOR).classList.add('error')
|
|
74
52
|
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
// set the error message element
|
|
78
|
-
const errorMessageContainer = this.errorMessageContainer
|
|
53
|
+
// set the error message element
|
|
54
|
+
const errorMessageContainer = this.errorMessageContainer
|
|
79
55
|
|
|
80
|
-
|
|
56
|
+
if (target.dataset.message) target.setCustomValidity(target.dataset.message)
|
|
81
57
|
|
|
82
|
-
|
|
58
|
+
errorMessageContainer.innerHTML = target.validationMessage
|
|
83
59
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
60
|
+
// add the error message element to the dom tree
|
|
61
|
+
parentElement.appendChild(errorMessageContainer)
|
|
87
62
|
}
|
|
88
63
|
|
|
89
64
|
clearError(target) {
|
|
90
65
|
const { parentElement } = target
|
|
91
|
-
|
|
92
|
-
// Remove error class from kit element
|
|
93
|
-
if (kitElement) kitElement.classList.remove('error')
|
|
94
|
-
// Remove error message from parent element
|
|
66
|
+
parentElement.closest(KIT_SELECTOR).classList.remove('error')
|
|
95
67
|
const errorMessageContainer = parentElement.querySelector(ERROR_MESSAGE_SELECTOR)
|
|
96
68
|
if (errorMessageContainer) errorMessageContainer.remove()
|
|
97
69
|
}
|
|
98
70
|
|
|
99
|
-
// Check if there are phone number input errors
|
|
100
|
-
hasPhoneNumberValidationErrors() {
|
|
101
|
-
const phoneNumberErrors = this.element.querySelectorAll(PHONE_NUMBER_VALIDATION_ERROR_SELECTOR)
|
|
102
|
-
return phoneNumberErrors.length > 0
|
|
103
|
-
}
|
|
104
|
-
|
|
105
71
|
get errorMessageContainer() {
|
|
106
72
|
const errorContainer = document.createElement('div')
|
|
107
73
|
const kitClassName = ERROR_MESSAGE_SELECTOR.replace(/\./, '')
|
|
108
74
|
errorContainer.classList.add(kitClassName)
|
|
109
75
|
return errorContainer
|
|
110
76
|
}
|
|
77
|
+
|
|
111
78
|
get formValidationFields() {
|
|
112
79
|
return this._formValidationFields =
|
|
113
80
|
this._formValidationFields || this.element.querySelectorAll(REQUIRED_FIELDS_SELECTOR)
|
|
114
81
|
}
|
|
115
82
|
}
|
|
116
83
|
|
|
117
|
-
window.PbFormValidation = PbFormValidation
|
|
84
|
+
window.PbFormValidation = PbFormValidation
|