playbook_ui 15.5.0.pre.alpha.play260612706 → 15.5.0.pre.alpha.play265012854
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_bar_graph/_bar_graph.tsx +6 -0
- data/app/pb_kits/playbook/pb_dialog/index.js +10 -15
- data/app/pb_kits/playbook/pb_home_address_street/_home_address_street.tsx +34 -22
- data/app/pb_kits/playbook/pb_home_address_street/city_emphasis.html.erb +16 -12
- data/app/pb_kits/playbook/pb_home_address_street/docs/_home_address_street_default.html.erb +1 -1
- data/app/pb_kits/playbook/pb_home_address_street/none_emphasis.html.erb +16 -12
- data/app/pb_kits/playbook/pb_home_address_street/street_emphasis.html.erb +16 -12
- data/app/pb_kits/playbook/pb_legend/_legend.tsx +6 -1
- data/app/pb_kits/playbook/pb_multiple_users/_multiple_users.scss +10 -0
- data/app/pb_kits/playbook/pb_multiple_users/_multiple_users.tsx +66 -15
- data/app/pb_kits/playbook/pb_multiple_users/docs/_multiple_users_with_tooltip.jsx +42 -0
- data/app/pb_kits/playbook/pb_multiple_users/docs/_multiple_users_with_tooltip.md +1 -0
- data/app/pb_kits/playbook/pb_multiple_users/docs/example.yml +1 -0
- data/app/pb_kits/playbook/pb_multiple_users/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_multiple_users/multiple_users.test.js +25 -0
- data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +44 -10
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.html.erb +34 -4
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.jsx +16 -7
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.test.jsx +15 -0
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +3 -0
- data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +6 -1
- data/app/pb_kits/playbook/pb_typeahead/components/ValueContainer.tsx +34 -7
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_input_display.html.erb +30 -0
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_input_display.jsx +37 -0
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_input_display.md +3 -0
- data/app/pb_kits/playbook/pb_typeahead/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_typeahead/docs/index.js +2 -1
- data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +6 -1
- data/app/pb_kits/playbook/utilities/DEPRECATION_WARNINGS.md +82 -0
- data/app/pb_kits/playbook/utilities/deprecated.ts +71 -0
- data/dist/chunks/_typeahead-EauOtKPs.js +6 -0
- data/dist/chunks/vendor.js +2 -2
- data/dist/menu.yml +3 -2
- 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 +10 -3
- data/dist/chunks/_typeahead-eXsdpMc6.js +0 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cf34a7cbdf05a7626ce21c8db3a61e0c932c67f2099d9b9ee04c04341152e063
|
|
4
|
+
data.tar.gz: 11a9f58fc3239cee3508cddf0ad875712e3192de67af4d2b147bdd4770f79e53
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 641034c37e83b9fb00058a69de651804af40d9880909344cd8678cabe625b1f791b4224b2e94df4537b79c699aa9fd4dc165a09d4679a1d5cd663778d372aea6
|
|
7
|
+
data.tar.gz: 3eff1276d02c1a41a332d770604284b7770b7b4c3c771febb3cc1dd47b2d11c248574498d064e7881c7b6c106eac2c3fa71b2bd1edba2ba83946b2b2d6611053
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useState, useEffect } from "react";
|
|
2
2
|
import { globalProps } from "../utilities/globalProps";
|
|
3
3
|
import { buildAriaProps, buildDataProps, buildHtmlProps } from "../utilities/props";
|
|
4
|
+
import { deprecatedKitWarning } from "../utilities/deprecated";
|
|
4
5
|
|
|
5
6
|
import HighchartsReact from "highcharts-react-official";
|
|
6
7
|
import Highcharts from "highcharts";
|
|
@@ -168,6 +169,11 @@ if (Array.isArray(axisTitle) && axisTitle.length > 1 && axisTitle[1].name) {
|
|
|
168
169
|
|
|
169
170
|
const [options, setOptions] = useState({});
|
|
170
171
|
|
|
172
|
+
useEffect(() => {
|
|
173
|
+
// Warn about deprecated kit (only once per page load, dev mode only)
|
|
174
|
+
deprecatedKitWarning('BarGraph', '[Playbook] The "BarGraph" kit is deprecated. Please use "PbBarGraph" instead.');
|
|
175
|
+
}, []);
|
|
176
|
+
|
|
171
177
|
useEffect(() => {
|
|
172
178
|
setOptions(merge(staticOptions, customOptions));
|
|
173
179
|
}, [chartData]);
|
|
@@ -143,30 +143,25 @@ export default class PbDialog extends PbEnhancedElement {
|
|
|
143
143
|
|
|
144
144
|
// Close dialog box on outside click
|
|
145
145
|
dialogs.forEach((dialogElement) => {
|
|
146
|
-
const
|
|
147
|
-
if (
|
|
148
|
-
|
|
146
|
+
const originalMousedownHandler = dialogElement._outsideClickHandler
|
|
147
|
+
if (originalMousedownHandler) dialogElement.removeEventListener("mousedown", originalMousedownHandler)
|
|
149
148
|
dialogElement._outsideClickHandler = (event) => {
|
|
150
149
|
const dialogParentDataset = dialogElement.parentElement.dataset
|
|
151
150
|
if (dialogParentDataset.overlayClick === "overlay_close") return
|
|
152
151
|
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
event.
|
|
157
|
-
event.
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
// Only close if clicked outside the dialog content (on the backdrop)
|
|
163
|
-
if (!clickedInDialog) {
|
|
152
|
+
const dialogModal = event.target.getBoundingClientRect()
|
|
153
|
+
const clickedOutsideDialogModal = event.clientX < dialogModal.left ||
|
|
154
|
+
event.clientX > dialogModal.right ||
|
|
155
|
+
event.clientY < dialogModal.top ||
|
|
156
|
+
event.clientY > dialogModal.bottom
|
|
157
|
+
|
|
158
|
+
if (clickedOutsideDialogModal) {
|
|
164
159
|
dialogElement.close()
|
|
165
160
|
event.stopPropagation()
|
|
166
161
|
}
|
|
167
162
|
}
|
|
168
163
|
|
|
169
|
-
dialogElement.addEventListener("
|
|
164
|
+
dialogElement.addEventListener("mousedown", dialogElement._outsideClickHandler);
|
|
170
165
|
})
|
|
171
166
|
}
|
|
172
167
|
}
|
|
@@ -96,20 +96,24 @@ const HomeAddressStreet = (props: HomeAddressStreetProps): React.ReactElement =>
|
|
|
96
96
|
{hasAllEmptyProps && '—'}
|
|
97
97
|
{emphasis == 'street' && !hasAllEmptyProps &&
|
|
98
98
|
<div>
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
99
|
+
{(address || houseStyle) && (
|
|
100
|
+
<Title
|
|
101
|
+
className="pb_home_address_street_address"
|
|
102
|
+
dark={dark}
|
|
103
|
+
size={4}
|
|
104
|
+
>
|
|
105
|
+
{joinPresent([formatStreetAdr(address), houseStyle], ' · ')}
|
|
106
|
+
</Title>
|
|
107
|
+
)}
|
|
108
|
+
{addressCont && (
|
|
109
|
+
<Title
|
|
110
|
+
className="pb_home_address_street_address"
|
|
111
|
+
dark={dark}
|
|
112
|
+
size={4}
|
|
113
|
+
>
|
|
114
|
+
{titleize(addressCont)}
|
|
115
|
+
</Title>
|
|
116
|
+
)}
|
|
113
117
|
<Body color="light">
|
|
114
118
|
{`${city ? `${titleize(city)}, ` : ''}${uppercaseState}${zipcode ? ` ${zipcode}` : ''}`}
|
|
115
119
|
</Body>
|
|
@@ -117,10 +121,14 @@ const HomeAddressStreet = (props: HomeAddressStreetProps): React.ReactElement =>
|
|
|
117
121
|
}
|
|
118
122
|
{emphasis == 'city' && !hasAllEmptyProps &&
|
|
119
123
|
<div>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
+
{(address || houseStyle) && (
|
|
125
|
+
<Body color="light">
|
|
126
|
+
{joinPresent([formatStreetAdr(address), houseStyle], ' · ')}
|
|
127
|
+
</Body>
|
|
128
|
+
)}
|
|
129
|
+
{addressCont && (
|
|
130
|
+
<Body color="light">{titleize(addressCont)}</Body>
|
|
131
|
+
)}
|
|
124
132
|
<div>
|
|
125
133
|
<Title
|
|
126
134
|
className="pb_home_address_street_address"
|
|
@@ -141,10 +149,14 @@ const HomeAddressStreet = (props: HomeAddressStreetProps): React.ReactElement =>
|
|
|
141
149
|
}
|
|
142
150
|
{emphasis == 'none' && !hasAllEmptyProps &&
|
|
143
151
|
<div>
|
|
144
|
-
|
|
145
|
-
{
|
|
146
|
-
|
|
147
|
-
|
|
152
|
+
{(address || houseStyle) && (
|
|
153
|
+
<Body dark={dark}>
|
|
154
|
+
{joinPresent([formatStreetAdr(address), houseStyle], ' · ')}
|
|
155
|
+
</Body>
|
|
156
|
+
)}
|
|
157
|
+
{addressCont && (
|
|
158
|
+
<Body dark={dark}>{formatStreetAdr(addressCont)}</Body>
|
|
159
|
+
)}
|
|
148
160
|
<div>
|
|
149
161
|
<Body
|
|
150
162
|
color="light"
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
<% if object.address_house_style.present? %>
|
|
2
|
+
<%= pb_rails "body", props: {
|
|
3
|
+
classname: "pb_home_address_street_address",
|
|
4
|
+
color: "light",
|
|
5
|
+
text: object.address_house_style,
|
|
6
|
+
dark: object.dark
|
|
7
|
+
} %>
|
|
8
|
+
<% end %>
|
|
9
|
+
<% if object.address_house_style2.present? %>
|
|
10
|
+
<%= pb_rails "body", props: {
|
|
11
|
+
classname: "pb_home_address_street_address",
|
|
12
|
+
color: "light",
|
|
13
|
+
text: object.address_house_style2,
|
|
14
|
+
dark: object.dark
|
|
15
|
+
} %>
|
|
16
|
+
<% end %>
|
|
13
17
|
<div>
|
|
14
18
|
<%= pb_rails "title", props: {
|
|
15
19
|
tag: "span",
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
<% if object.address_house_style.present? %>
|
|
2
|
+
<%= pb_rails "body", props: {
|
|
3
|
+
classname: "pb_home_address_street_address",
|
|
4
|
+
size: 4,
|
|
5
|
+
text: object.address_house_style,
|
|
6
|
+
dark: object.dark
|
|
7
|
+
} %>
|
|
8
|
+
<% end %>
|
|
9
|
+
<% if object.address_house_style2.present? %>
|
|
10
|
+
<%= pb_rails "body", props: {
|
|
11
|
+
classname: "pb_home_address_street_address",
|
|
12
|
+
size: 4,
|
|
13
|
+
text: object.address_house_style2,
|
|
14
|
+
dark: object.dark
|
|
15
|
+
} %>
|
|
16
|
+
<% end %>
|
|
13
17
|
<%= pb_rails "body", props: {
|
|
14
18
|
color: "light",
|
|
15
19
|
text: object.city_state_zip,
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
<% if object.address_house_style.present? %>
|
|
2
|
+
<%= pb_rails "title", props: {
|
|
3
|
+
classname: "pb_home_address_street_address",
|
|
4
|
+
size: 4,
|
|
5
|
+
text: object.address_house_style,
|
|
6
|
+
dark: object.dark
|
|
7
|
+
} %>
|
|
8
|
+
<% end %>
|
|
9
|
+
<% if object.address_house_style2.present? %>
|
|
10
|
+
<%= pb_rails "title", props: {
|
|
11
|
+
classname: "pb_home_address_street_address",
|
|
12
|
+
size: 4,
|
|
13
|
+
text: object.address_house_style2,
|
|
14
|
+
dark: object.dark
|
|
15
|
+
} %>
|
|
16
|
+
<% end %>
|
|
13
17
|
<%= pb_rails "body", props: {
|
|
14
18
|
color: "light",
|
|
15
19
|
text: object.city_state_zip,
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import React from 'react'
|
|
1
|
+
import React, { useEffect } from 'react'
|
|
2
2
|
import classnames from 'classnames'
|
|
3
3
|
|
|
4
4
|
import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../utilities/props'
|
|
5
5
|
import { globalProps } from '../utilities/globalProps'
|
|
6
|
+
import { deprecatedKitWarning } from '../utilities/deprecated'
|
|
6
7
|
|
|
7
8
|
import Body from '../pb_body/_body'
|
|
8
9
|
import Title from '../pb_title/_title'
|
|
@@ -32,6 +33,10 @@ const Legend = (props: LegendProps) => {
|
|
|
32
33
|
text,
|
|
33
34
|
} = props
|
|
34
35
|
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
deprecatedKitWarning('Legend')
|
|
38
|
+
}, [])
|
|
39
|
+
|
|
35
40
|
const ariaProps = buildAriaProps(aria)
|
|
36
41
|
const dataProps = buildDataProps(data)
|
|
37
42
|
const htmlProps = buildHtmlProps(htmlOptions)
|
|
@@ -36,6 +36,16 @@ $pb_multiple_users_size_xxs: map-get($avatar-sizes, "xxs");
|
|
|
36
36
|
height: $pb_multiple_users_size_xxs;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
|
|
40
|
+
.user_tooltip {
|
|
41
|
+
margin-left: $pb_multiple_users_overlap !important;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.user_count_tooltip {
|
|
45
|
+
margin-left: $pb_multiple_users_overlap !important;
|
|
46
|
+
position: relative;
|
|
47
|
+
}
|
|
48
|
+
|
|
39
49
|
.pb_multiple_users_item {
|
|
40
50
|
margin-left: $pb_multiple_users_overlap;
|
|
41
51
|
margin-right: 0;
|
|
@@ -5,17 +5,19 @@ import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../uti
|
|
|
5
5
|
import { globalProps } from '../utilities/globalProps'
|
|
6
6
|
|
|
7
7
|
import Avatar from '../pb_avatar/_avatar'
|
|
8
|
+
import Tooltip from '../pb_tooltip/_tooltip'
|
|
8
9
|
|
|
9
10
|
type MultipleUsersProps = {
|
|
10
11
|
aria?: { [key: string]: string },
|
|
11
12
|
className?: string,
|
|
12
13
|
dark?: boolean,
|
|
13
14
|
data?: { [key: string]: string },
|
|
14
|
-
htmlOptions?: {[key: string]: string | number | boolean | (() => void)},
|
|
15
|
+
htmlOptions?: { [key: string]: string | number | boolean | (() => void) },
|
|
15
16
|
id?: string,
|
|
16
17
|
maxDisplayedUsers?: number,
|
|
17
18
|
reverse?: boolean,
|
|
18
19
|
size?: "md" | "lg" | "sm" | "xl" | "xs" | "xxs",
|
|
20
|
+
withTooltip?: boolean,
|
|
19
21
|
users: Array<{ [key: string]: string }>,
|
|
20
22
|
}
|
|
21
23
|
|
|
@@ -30,6 +32,7 @@ const MultipleUsers = (props: MultipleUsersProps): React.ReactElement => {
|
|
|
30
32
|
maxDisplayedUsers = 4,
|
|
31
33
|
reverse = false,
|
|
32
34
|
size = 'xs',
|
|
35
|
+
withTooltip = false,
|
|
33
36
|
users,
|
|
34
37
|
} = props
|
|
35
38
|
|
|
@@ -62,22 +65,70 @@ const MultipleUsers = (props: MultipleUsersProps): React.ReactElement => {
|
|
|
62
65
|
className={classes}
|
|
63
66
|
id={id}
|
|
64
67
|
>
|
|
65
|
-
{
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
68
|
+
{withTooltip ?
|
|
69
|
+
<>
|
|
70
|
+
{usersToDisplay.map((avatarData, index) => (
|
|
71
|
+
<Tooltip
|
|
72
|
+
key={"user_tooltip_" + index}
|
|
73
|
+
placement='top'
|
|
74
|
+
text={avatarData.tooltip ? avatarData.tooltip : ''}
|
|
75
|
+
zIndex={10}
|
|
76
|
+
>
|
|
77
|
+
<Avatar
|
|
78
|
+
{...avatarData}
|
|
79
|
+
className={"pb_multiple_users_item" + (withTooltip ? " user_tooltip" : "")}
|
|
80
|
+
dark={dark}
|
|
81
|
+
imageAlt={avatarData.name}
|
|
82
|
+
key={index}
|
|
83
|
+
size={size}
|
|
84
|
+
/>
|
|
85
|
+
</Tooltip>
|
|
86
|
+
))}
|
|
75
87
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
88
|
+
{users.length > maxDisplayedUsers &&
|
|
89
|
+
<Tooltip
|
|
90
|
+
placement='top'
|
|
91
|
+
text={
|
|
92
|
+
<div>
|
|
93
|
+
{
|
|
94
|
+
usersToDisplay.length < users.length ?
|
|
95
|
+
users.slice(displayCount).map((u, i) => (
|
|
96
|
+
<div key={i}>{u.tooltip}</div>
|
|
97
|
+
))
|
|
98
|
+
:
|
|
99
|
+
''
|
|
100
|
+
}
|
|
101
|
+
</div>
|
|
102
|
+
}
|
|
103
|
+
zIndex={10}
|
|
104
|
+
>
|
|
105
|
+
<div className={itemClasses + (withTooltip ? " user_count_tooltip" : "")}>
|
|
106
|
+
{`+${users.length - displayCount}`}
|
|
107
|
+
</div>
|
|
108
|
+
</Tooltip>
|
|
109
|
+
}
|
|
110
|
+
</>
|
|
111
|
+
:
|
|
112
|
+
<>
|
|
113
|
+
{usersToDisplay.map((avatarData, index) => (
|
|
114
|
+
<Avatar
|
|
115
|
+
{...avatarData}
|
|
116
|
+
className="pb_multiple_users_item"
|
|
117
|
+
dark={dark}
|
|
118
|
+
imageAlt={avatarData.name}
|
|
119
|
+
key={index}
|
|
120
|
+
size={size}
|
|
121
|
+
/>
|
|
122
|
+
))}
|
|
123
|
+
|
|
124
|
+
{users.length > maxDisplayedUsers &&
|
|
125
|
+
<div className={itemClasses}>
|
|
126
|
+
{`+${users.length - 3}`}
|
|
127
|
+
</div>
|
|
128
|
+
}
|
|
129
|
+
</>
|
|
80
130
|
}
|
|
131
|
+
|
|
81
132
|
</div>
|
|
82
133
|
)
|
|
83
134
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import MultipleUsers from '../../pb_multiple_users/_multiple_users'
|
|
3
|
+
|
|
4
|
+
const MultipleUsersWithTooltip = (props) => {
|
|
5
|
+
return (
|
|
6
|
+
<div>
|
|
7
|
+
<MultipleUsers
|
|
8
|
+
users={[
|
|
9
|
+
{
|
|
10
|
+
name: 'Patrick Welch',
|
|
11
|
+
imageUrl: 'https://randomuser.me/api/portraits/men/9.jpg',
|
|
12
|
+
tooltip: "Patrick Welch - Online"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
name: 'Lucille Sanchez',
|
|
16
|
+
imageUrl: 'https://randomuser.me/api/portraits/women/6.jpg',
|
|
17
|
+
tooltip: "Lucille Sanchez - Offline"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: 'Beverly Reyes',
|
|
21
|
+
imageUrl: 'https://randomuser.me/api/portraits/women/74.jpg',
|
|
22
|
+
tooltip: "Beverly Reyes - Online"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'Keith Craig',
|
|
26
|
+
imageUrl: 'https://randomuser.me/api/portraits/men/40.jpg',
|
|
27
|
+
tooltip: "Keith Craig - Away"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'Alicia Cooper',
|
|
31
|
+
imageUrl: 'https://randomuser.me/api/portraits/women/46.jpg',
|
|
32
|
+
tooltip: "Alicia Cooper - Busy"
|
|
33
|
+
},
|
|
34
|
+
]}
|
|
35
|
+
withTooltip
|
|
36
|
+
{...props}
|
|
37
|
+
/>
|
|
38
|
+
</div>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
``
|
|
42
|
+
export default MultipleUsersWithTooltip
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Use the `withTooltip` boolean prop to enable setting user-specific tooltip content via the `tooltip` property in the users array.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { default as MultipleUsersDefault } from './_multiple_users_default.jsx'
|
|
2
2
|
export { default as MultipleUsersReverse } from './_multiple_users_reverse.jsx'
|
|
3
3
|
export { default as MultipleUsersSize } from './_multiple_users_size.jsx'
|
|
4
|
+
export { default as MultipleUsersWithTooltip } from './_multiple_users_with_tooltip.jsx'
|
|
@@ -49,4 +49,29 @@ test('should render aria-label', () => {
|
|
|
49
49
|
|
|
50
50
|
const kit = screen.getByTestId(testId)
|
|
51
51
|
expect(kit).toHaveAttribute('aria-label', testId)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test('should render withTooltip prop', () => {
|
|
55
|
+
render(
|
|
56
|
+
<MultipleUsers
|
|
57
|
+
data={{ testid: testId }}
|
|
58
|
+
users={[
|
|
59
|
+
{
|
|
60
|
+
name: 'Patrick Welch',
|
|
61
|
+
imageUrl: 'https://randomuser.me/api/portraits/men/9.jpg',
|
|
62
|
+
tooltip: "Patrick Welch - Online"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'Lucille Sanchez',
|
|
66
|
+
imageUrl: 'https://randomuser.me/api/portraits/women/6.jpg',
|
|
67
|
+
tooltip: "Lucille Sanchez - Offline"
|
|
68
|
+
},
|
|
69
|
+
]}
|
|
70
|
+
withTooltip
|
|
71
|
+
/>
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
const kit = screen.getByTestId(testId)
|
|
75
|
+
const childWithTooltip = kit.querySelector('.pb_tooltip_kit')
|
|
76
|
+
expect(childWithTooltip).not.toBeNull()
|
|
52
77
|
})
|
|
@@ -110,13 +110,25 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
110
110
|
const inputRef = useRef<HTMLInputElement | null>(null)
|
|
111
111
|
const itiRef = useRef<any>(null);
|
|
112
112
|
const wrapperRef = useRef<HTMLDivElement | null>(null);
|
|
113
|
+
const hasBlurredRef = useRef<boolean>(false);
|
|
114
|
+
const formSubmittedRef = useRef<boolean>(false);
|
|
113
115
|
const [inputValue, setInputValue] = useState(value)
|
|
114
116
|
const [error, setError] = useState(props.error || "")
|
|
115
117
|
const [dropDownIsOpen, setDropDownIsOpen] = useState(false)
|
|
116
118
|
const [selectedData, setSelectedData] = useState()
|
|
117
119
|
const [hasTyped, setHasTyped] = useState(false)
|
|
120
|
+
const [hasBlurred, setHasBlurred] = useState(false)
|
|
118
121
|
const [formSubmitted, setFormSubmitted] = useState(false)
|
|
119
122
|
const [hasStartedValidating, setHasStartedValidating] = useState(false)
|
|
123
|
+
|
|
124
|
+
// Keep refs in sync with state for use in event listeners
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
hasBlurredRef.current = hasBlurred
|
|
127
|
+
}, [hasBlurred])
|
|
128
|
+
|
|
129
|
+
useEffect(() => {
|
|
130
|
+
formSubmittedRef.current = formSubmitted
|
|
131
|
+
}, [formSubmitted])
|
|
120
132
|
|
|
121
133
|
// Only sync initial error from props, not continuous updates
|
|
122
134
|
// Once validation starts, internal validation takes over
|
|
@@ -143,8 +155,8 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
143
155
|
}
|
|
144
156
|
|
|
145
157
|
// Determine which error to display
|
|
146
|
-
// Show internal errors
|
|
147
|
-
const shouldShowInternalError = (
|
|
158
|
+
// Show internal errors only after blur (hasBlurred) or on form submission (formSubmitted)
|
|
159
|
+
const shouldShowInternalError = (hasBlurred || formSubmitted) && error
|
|
148
160
|
const displayError = shouldShowInternalError ? error : ""
|
|
149
161
|
|
|
150
162
|
useEffect(() => {
|
|
@@ -259,7 +271,9 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
259
271
|
return
|
|
260
272
|
}
|
|
261
273
|
|
|
262
|
-
if
|
|
274
|
+
// Only validate if field has been blurred or form has been submitted
|
|
275
|
+
// Use refs here since state updates are async and we need current values
|
|
276
|
+
if (!hasBlurredRef.current && !formSubmittedRef.current) return
|
|
263
277
|
|
|
264
278
|
// Run validation checks
|
|
265
279
|
if (itiRef.current) isValid(itiRef.current.isValidNumber())
|
|
@@ -280,6 +294,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
280
294
|
if (phoneNumberContainer && phoneNumberContainer === wrapperRef.current) {
|
|
281
295
|
const invalidInputName = target.name || target.getAttribute('name')
|
|
282
296
|
if (invalidInputName === name) {
|
|
297
|
+
formSubmittedRef.current = true
|
|
283
298
|
setFormSubmitted(true)
|
|
284
299
|
// Trigger validation when form is submitted
|
|
285
300
|
validateErrors()
|
|
@@ -305,6 +320,9 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
305
320
|
setInputValue("")
|
|
306
321
|
setError("")
|
|
307
322
|
setHasTyped(false)
|
|
323
|
+
hasBlurredRef.current = false
|
|
324
|
+
setHasBlurred(false)
|
|
325
|
+
formSubmittedRef.current = false
|
|
308
326
|
setFormSubmitted(false)
|
|
309
327
|
setHasStartedValidating(false)
|
|
310
328
|
// Only clear validation state if field was required
|
|
@@ -322,6 +340,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
322
340
|
|
|
323
341
|
if (required && isEmpty) {
|
|
324
342
|
setError('Missing phone number')
|
|
343
|
+
formSubmittedRef.current = true
|
|
325
344
|
setFormSubmitted(true)
|
|
326
345
|
return 'Missing phone number'
|
|
327
346
|
}
|
|
@@ -378,6 +397,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
378
397
|
|
|
379
398
|
// Set the error state so the validation attribute gets added
|
|
380
399
|
setError(errorMessage)
|
|
400
|
+
formSubmittedRef.current = true
|
|
381
401
|
setFormSubmitted(true)
|
|
382
402
|
setHasTyped(true)
|
|
383
403
|
|
|
@@ -401,6 +421,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
401
421
|
|
|
402
422
|
// Reset form submitted state when user types
|
|
403
423
|
if (formSubmitted) {
|
|
424
|
+
formSubmittedRef.current = false
|
|
404
425
|
setFormSubmitted(false)
|
|
405
426
|
}
|
|
406
427
|
|
|
@@ -416,11 +437,15 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
416
437
|
|
|
417
438
|
setSelectedData(phoneNumberData)
|
|
418
439
|
onChange(phoneNumberData)
|
|
419
|
-
|
|
440
|
+
|
|
441
|
+
// Don't call isValid callback on change - only on blur or form submission
|
|
442
|
+
// This prevents triggering validation while typing
|
|
443
|
+
// Use refs to get current values in case this is called from event listener
|
|
444
|
+
if (hasBlurredRef.current || formSubmittedRef.current) {
|
|
445
|
+
isValid(itiRef.current.isValidNumber())
|
|
446
|
+
}
|
|
420
447
|
|
|
421
|
-
//
|
|
422
|
-
// This ensures validation state is up-to-date
|
|
423
|
-
setTimeout(() => validateErrors(), 0)
|
|
448
|
+
// Don't validate on change - only validate on blur or form submission
|
|
424
449
|
}
|
|
425
450
|
|
|
426
451
|
// Separating Concerns as React Docs Recommend
|
|
@@ -482,7 +507,12 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
482
507
|
|
|
483
508
|
setSelectedData(phoneNumberData)
|
|
484
509
|
onChange(phoneNumberData)
|
|
485
|
-
|
|
510
|
+
|
|
511
|
+
// Don't call isValid callback on change - only on blur or form submission
|
|
512
|
+
// Use refs to check current blur state in the event listener (closure issue)
|
|
513
|
+
if (hasBlurredRef.current || formSubmittedRef.current) {
|
|
514
|
+
isValid(telInputInit.isValidNumber())
|
|
515
|
+
}
|
|
486
516
|
})
|
|
487
517
|
}
|
|
488
518
|
}
|
|
@@ -492,12 +522,16 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
492
522
|
dark,
|
|
493
523
|
"data-phone-number": JSON.stringify(selectedData),
|
|
494
524
|
disabled,
|
|
495
|
-
error:
|
|
525
|
+
error: displayError || props.error || "",
|
|
496
526
|
type: 'tel',
|
|
497
527
|
id,
|
|
498
528
|
label,
|
|
499
529
|
name,
|
|
500
|
-
onBlur:
|
|
530
|
+
onBlur: () => {
|
|
531
|
+
hasBlurredRef.current = true
|
|
532
|
+
setHasBlurred(true)
|
|
533
|
+
validateErrors()
|
|
534
|
+
},
|
|
501
535
|
onChange: formatAsYouType ? undefined : handleOnChange,
|
|
502
536
|
value: inputValue
|
|
503
537
|
}
|