playbook_ui 15.1.0.pre.alpha.PLAY2468phonenuminputvalidation10803 → 15.1.0.pre.alpha.iconstatvaluescss10956
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_button/docs/_button_managed_disabled.html.erb +31 -0
- data/app/pb_kits/playbook/pb_button/docs/_button_managed_disabled.md +7 -0
- data/app/pb_kits/playbook/pb_button/docs/_button_managed_disabled_helper.html.erb +21 -0
- data/app/pb_kits/playbook/pb_button/docs/_button_managed_disabled_helper.md +7 -0
- data/app/pb_kits/playbook/pb_button/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_button/index.js +99 -0
- data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +1 -1
- data/app/pb_kits/playbook/pb_form/pb_form_validation.js +8 -40
- data/app/pb_kits/playbook/pb_icon_circle/_icon_circle.tsx +2 -2
- data/app/pb_kits/playbook/pb_icon_stat_value/_icon_stat_value.scss +15 -21
- data/app/pb_kits/playbook/pb_icon_stat_value/_icon_stat_value.tsx +6 -5
- data/app/pb_kits/playbook/pb_icon_stat_value/icon_stat_value.html.erb +2 -0
- data/app/pb_kits/playbook/pb_icon_stat_value/icon_stat_value.rb +11 -3
- data/app/pb_kits/playbook/pb_icon_stat_value/icon_stat_value.test.js +9 -8
- data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +2 -51
- data/dist/chunks/{_line_graph-Bkn-wx30.js → _line_graph-C9stNsP3.js} +1 -1
- data/dist/chunks/{_typeahead-DkRYiut7.js → _typeahead-D3MtsWXG.js} +1 -1
- data/dist/chunks/{_weekday_stacked-BUjWQnqn.js → _weekday_stacked-BkTDqn-r.js} +2 -2
- data/dist/chunks/pb_form_validation-CleM960_.js +1 -0
- data/dist/chunks/vendor.js +1 -1
- data/dist/menu.yml +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 +11 -6
- data/dist/chunks/pb_form_validation-B4uRmBwC.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: 023dae1c06846305a2de70d79d36fac8bb6e685e9eed4da1157a84559d3749c6
|
|
4
|
+
data.tar.gz: f8154d1b04e55366e85ddf6f2a21d2e465d49ba40d6a456ddb43450307930e91
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d7fa8e2b752601706f4aa07fc61cb6c08280c21c315faf666ef054eac38a01e6e14e17fe5015de1d6c3734ba0fae3039b01223ab85a32a305166ca0bd06983e7
|
|
7
|
+
data.tar.gz: f61864e43078b05767ba7fc2335881b00ee99799b8731ca982e00a43fc952c5b085b5712626d66b410548f5b6e83bbaf200b2343bf6ef4f7aff8ee94b046e76d
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<%= pb_rails("body", props: { text: "Click to disable the Buttons below", id: "toggle-disabled-demo", cursor: "pointer", color:"link", margin_bottom:"sm" }) %>
|
|
2
|
+
<%= pb_rails("body", props: { text: "Click to enable the Buttons below", id: "toggle-enabled-demo", cursor: "pointer", color:"link", margin_bottom:"sm" }) %>
|
|
3
|
+
|
|
4
|
+
<%= pb_rails("card", props:{display:"flex", flex_direction:"row", justify_content:"center"}) do %>
|
|
5
|
+
<%= pb_rails("button", props: { text: "I am a Button", id: "normal_managed_button", data:{pb_button_managed: true}, margin_right: "lg" }) %>
|
|
6
|
+
<%= pb_rails("button", props: { text: "I am an <a> Button", id: "a_tag_managed_button", tag:"a", data:{pb_button_managed: true}, link: "http://google.com"}) %>
|
|
7
|
+
<% end %>
|
|
8
|
+
<script>
|
|
9
|
+
document.addEventListener('DOMContentLoaded', function () {
|
|
10
|
+
const disableTrigger = document.querySelector('#toggle-disabled-demo')
|
|
11
|
+
const enableTrigger = document.querySelector('#toggle-enabled-demo')
|
|
12
|
+
|
|
13
|
+
// Find the Buttons you want to 'manage'
|
|
14
|
+
const btn = document.querySelector('#normal_managed_button');
|
|
15
|
+
const link = document.querySelector('#a_tag_managed_button');
|
|
16
|
+
|
|
17
|
+
disableTrigger.addEventListener('click', (e) => {
|
|
18
|
+
// Disable default button
|
|
19
|
+
btn.setAttribute('disabled', true)
|
|
20
|
+
// Disable a tag button
|
|
21
|
+
link.setAttribute('aria-disabled', 'true')
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
enableTrigger.addEventListener('click', (e) => {
|
|
25
|
+
// Enable default button
|
|
26
|
+
btn.removeAttribute('disabled')
|
|
27
|
+
// Enable a tag button
|
|
28
|
+
link.removeAttribute('aria-disabled')
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
</script>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
If needing to toggle the disabled state of the Button dynamically (for example, within a Turbo or Stimulus context), you can now do so in rails using the `pb-button-managed` data attribute.
|
|
2
|
+
|
|
3
|
+
1) Add the following data attribute to your button kit: `data:{ pb-button-managed: true }`
|
|
4
|
+
|
|
5
|
+
2) To toggle enabled/disabled state via attributes: for buttons set/remove disabled, for links set/remove aria-disabled="true". This will handle disabling the button, preventing clicks as well as all style changes so you don't have to.
|
|
6
|
+
|
|
7
|
+
Click to enable or disable the buttons above and view the code snippet below for details!
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<%= pb_rails("body", props: { text: "Click to disable the Button below", id: "toggle-disabled-demo-with-helper", cursor: "pointer", color:"link", margin_bottom:"sm" }) %>
|
|
2
|
+
<%= pb_rails("body", props: { text: "Click to enable the Button below", id: "toggle-enabled-demo-with-helper", cursor: "pointer", color:"link", margin_bottom:"sm" }) %>
|
|
3
|
+
<br/>
|
|
4
|
+
<%= pb_rails("card", props:{display:"flex", flex_direction:"row", justify_content:"center"}) do %>
|
|
5
|
+
<%= pb_rails("button", props: { text: "Watch me Change!", id: "managed_button_with_helper", data:{pb_button_managed: true} }) %>
|
|
6
|
+
<% end %>
|
|
7
|
+
|
|
8
|
+
<script>
|
|
9
|
+
document.addEventListener('DOMContentLoaded', function () {
|
|
10
|
+
const disable = document.querySelector('#toggle-disabled-demo-with-helper')
|
|
11
|
+
const enable = document.querySelector('#toggle-enabled-demo-with-helper')
|
|
12
|
+
|
|
13
|
+
// Find the Button you want to 'manage'
|
|
14
|
+
const demoBtn = document.querySelector('#managed_button_with_helper')
|
|
15
|
+
|
|
16
|
+
// Use the pbButton object created by the kit to call the enable/disable methods
|
|
17
|
+
disable.addEventListener('click', (e) => {demoBtn._pbButton.disable()});
|
|
18
|
+
enable.addEventListener('click', (e) => {demoBtn._pbButton.enable()});
|
|
19
|
+
|
|
20
|
+
});
|
|
21
|
+
</script>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
The disabled state for the button can also be toggled via small helpers available through the `pb-button-managed` data attribute.
|
|
2
|
+
|
|
3
|
+
1) Add the following data attribute to your button kit: `data:{ pb-button-managed: true }`
|
|
4
|
+
|
|
5
|
+
2) Toggle state via the provided `_pbButton.disable()` and `_pbButton.enable()` helpers as shown in the code snippet below.
|
|
6
|
+
|
|
7
|
+
Click to enable or disable the buttons above to see this in action!
|
|
@@ -11,6 +11,8 @@ examples:
|
|
|
11
11
|
- button_options: Button Additional Options
|
|
12
12
|
- button_size: Button Size
|
|
13
13
|
- button_form: Button Form Attribute
|
|
14
|
+
- button_managed_disabled: Button Toggle Disabled State
|
|
15
|
+
- button_managed_disabled_helper: Button Toggle Disabled State Helper
|
|
14
16
|
|
|
15
17
|
react:
|
|
16
18
|
- button_default: Button Variants
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import PbEnhancedElement from "../pb_enhanced_element"
|
|
2
|
+
|
|
3
|
+
const BUTTON_SELECTOR = "[data-pb-button-managed]"
|
|
4
|
+
|
|
5
|
+
export default class PbButton extends PbEnhancedElement {
|
|
6
|
+
static get selector() {
|
|
7
|
+
return BUTTON_SELECTOR
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
connect() {
|
|
11
|
+
this._attrManaged = this._attributesPresent()
|
|
12
|
+
this.element._pbButton = this
|
|
13
|
+
|
|
14
|
+
this._onClick = (e) => {
|
|
15
|
+
if (this.isDisabled()) {
|
|
16
|
+
e.preventDefault()
|
|
17
|
+
e.stopImmediatePropagation()
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
this.element.addEventListener("click", this._onClick, true)
|
|
21
|
+
|
|
22
|
+
if (this._attrManaged) this._syncClassesFromAttributes()
|
|
23
|
+
|
|
24
|
+
this._observer = new MutationObserver(() => {
|
|
25
|
+
this._attrManaged = true
|
|
26
|
+
this._syncClassesFromAttributes()
|
|
27
|
+
})
|
|
28
|
+
this._observer.observe(this.element, {
|
|
29
|
+
attributes: true,
|
|
30
|
+
attributeFilter: ["disabled", "aria-disabled"],
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
disconnect() {
|
|
35
|
+
this.element.removeEventListener("click", this._onClick, true)
|
|
36
|
+
this._observer?.disconnect()
|
|
37
|
+
delete this.element._pbButton
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
disable() { this.setDisabled(true) }
|
|
41
|
+
enable() { this.setDisabled(false) }
|
|
42
|
+
|
|
43
|
+
setDisabled(state) {
|
|
44
|
+
if (this._isButton()) {
|
|
45
|
+
state
|
|
46
|
+
? this.element.setAttribute("disabled", "disabled")
|
|
47
|
+
: this.element.removeAttribute("disabled")
|
|
48
|
+
} else {
|
|
49
|
+
state
|
|
50
|
+
? this.element.setAttribute("aria-disabled", "true")
|
|
51
|
+
: this.element.removeAttribute("aria-disabled")
|
|
52
|
+
}
|
|
53
|
+
this._attrManaged = true
|
|
54
|
+
this._applyClassState(state)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
isDisabled() {
|
|
58
|
+
if (this._isButton()) {
|
|
59
|
+
if (this.element.hasAttribute("disabled")) return true
|
|
60
|
+
if (this._attrManaged && !this.element.hasAttribute("disabled")) return false
|
|
61
|
+
} else {
|
|
62
|
+
const aria = this.element.getAttribute("aria-disabled")
|
|
63
|
+
if (aria === "true") return true
|
|
64
|
+
if (this._attrManaged && aria !== "true") return false
|
|
65
|
+
}
|
|
66
|
+
return this.element.classList.contains("pb_button_disabled")
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
_isButton() {
|
|
70
|
+
return this.element.tagName === "BUTTON"
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
_attributesPresent() {
|
|
74
|
+
return this.element.hasAttribute("disabled") || this.element.hasAttribute("aria-disabled")
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
_syncClassesFromAttributes() {
|
|
78
|
+
const state = this._attrDisabledState()
|
|
79
|
+
const disabled = (state === null) ? false : state
|
|
80
|
+
this._applyClassState(disabled)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
_attrDisabledState() {
|
|
84
|
+
if (this._isButton()) {
|
|
85
|
+
return this.element.hasAttribute("disabled") ? true : null
|
|
86
|
+
} else {
|
|
87
|
+
const aria = this.element.getAttribute("aria-disabled")
|
|
88
|
+
if (aria === "true") return true
|
|
89
|
+
if (aria === "false") return false
|
|
90
|
+
return this.element.hasAttribute("aria-disabled") ? false : null
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
_applyClassState(disabled) {
|
|
95
|
+
this.element.classList.toggle("pb_button_disabled", !!disabled)
|
|
96
|
+
this.element.classList.toggle("pb_button_enabled", !disabled)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
<%= pb_form_with(scope: :example, method: :get, url: "", validate: true) do |form| %>
|
|
91
91
|
<%= form.typeahead :example_typeahead_validation, props: { data: { typeahead_example2: true, user: {} }, label: true, placeholder: "Search for a user", required: true, validation: { message: "Please select a user." } } %>
|
|
92
92
|
<%= form.text_field :example_text_field_validation, props: { label: true, required: true } %>
|
|
93
|
-
<%= form.phone_number_field :example_phone_number_field_validation, props: { label: "Example phone field", hidden_inputs: true
|
|
93
|
+
<%= form.phone_number_field :example_phone_number_field_validation, props: { label: "Example phone field", hidden_inputs: true } %>
|
|
94
94
|
<%= form.email_field :example_email_field_validation, props: { label: true, required: true } %>
|
|
95
95
|
<%= form.number_field :example_number_field_validation, props: { label: true, required: true } %>
|
|
96
96
|
<%= form.search_field :example_project_number_validation, props: { label: true, required: true, validation: { pattern: "[0-9]{2}-[0-9]{5}", message: "Please enter a valid project number (example: 33-12345)." } } %>
|
|
@@ -2,14 +2,12 @@ import PbEnhancedElement from '../pb_enhanced_element'
|
|
|
2
2
|
import { debounce } from '../utilities/object'
|
|
3
3
|
|
|
4
4
|
// Kit selectors
|
|
5
|
-
const KIT_SELECTOR
|
|
6
|
-
const ERROR_MESSAGE_SELECTOR
|
|
7
|
-
const PHONE_NUMBER_INPUT_SELECTOR = '.pb_phone_number_input'
|
|
5
|
+
const KIT_SELECTOR = '[class^="pb_"][class*="_kit"]'
|
|
6
|
+
const ERROR_MESSAGE_SELECTOR = '.pb_body_kit_negative'
|
|
8
7
|
|
|
9
8
|
// Validation selectors
|
|
10
|
-
const FORM_SELECTOR
|
|
11
|
-
const REQUIRED_FIELDS_SELECTOR
|
|
12
|
-
const PHONE_NUMBER_VALIDATION_ERROR_SELECTOR = '[data-pb-phone-validation-error="true"]'
|
|
9
|
+
const FORM_SELECTOR = 'form[data-pb-form-validation="true"]'
|
|
10
|
+
const REQUIRED_FIELDS_SELECTOR = 'input[required],textarea[required],select[required]'
|
|
13
11
|
|
|
14
12
|
const FIELD_EVENTS = [
|
|
15
13
|
'change',
|
|
@@ -30,14 +28,6 @@ class PbFormValidation extends PbEnhancedElement {
|
|
|
30
28
|
}, 250), false)
|
|
31
29
|
})
|
|
32
30
|
})
|
|
33
|
-
|
|
34
|
-
// Add event listener to check for phone number validation errors
|
|
35
|
-
this.element.addEventListener('submit', (event) => {
|
|
36
|
-
if (this.hasPhoneNumberValidationErrors()) {
|
|
37
|
-
event.preventDefault()
|
|
38
|
-
return false
|
|
39
|
-
}
|
|
40
|
-
})
|
|
41
31
|
}
|
|
42
32
|
|
|
43
33
|
validateFormField(event) {
|
|
@@ -55,18 +45,10 @@ class PbFormValidation extends PbEnhancedElement {
|
|
|
55
45
|
|
|
56
46
|
showValidationMessage(target) {
|
|
57
47
|
const { parentElement } = target
|
|
58
|
-
const kitElement = parentElement.closest(KIT_SELECTOR)
|
|
59
|
-
|
|
60
|
-
// Skip error message container for Phone Number Input as it handles its own errors
|
|
61
|
-
// as the closest kitElement is "pb_text_input_kit mb_sm" for Phone Number Input,
|
|
62
|
-
// we target the parent element of kitElement to check
|
|
63
|
-
if (kitElement && kitElement.parentElement.matches(PHONE_NUMBER_INPUT_SELECTOR)) {
|
|
64
|
-
return
|
|
65
|
-
}
|
|
66
48
|
|
|
67
49
|
// ensure clean error message state
|
|
68
50
|
this.clearError(target)
|
|
69
|
-
|
|
51
|
+
parentElement.closest(KIT_SELECTOR).classList.add('error')
|
|
70
52
|
|
|
71
53
|
// set the error message element
|
|
72
54
|
const errorMessageContainer = this.errorMessageContainer
|
|
@@ -81,23 +63,9 @@ class PbFormValidation extends PbEnhancedElement {
|
|
|
81
63
|
|
|
82
64
|
clearError(target) {
|
|
83
65
|
const { parentElement } = target
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (
|
|
87
|
-
kitElement.classList.remove('error')
|
|
88
|
-
|
|
89
|
-
// Only remove error message container for non-Phone Number Input kits
|
|
90
|
-
if (!kitElement.matches(PHONE_NUMBER_INPUT_SELECTOR)) {
|
|
91
|
-
const errorMessageContainer = parentElement.querySelector(ERROR_MESSAGE_SELECTOR)
|
|
92
|
-
if (errorMessageContainer) errorMessageContainer.remove()
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Check if there are phone number input errors
|
|
98
|
-
hasPhoneNumberValidationErrors() {
|
|
99
|
-
const phoneNumberErrors = this.element.querySelectorAll(PHONE_NUMBER_VALIDATION_ERROR_SELECTOR)
|
|
100
|
-
return phoneNumberErrors.length > 0
|
|
66
|
+
parentElement.closest(KIT_SELECTOR).classList.remove('error')
|
|
67
|
+
const errorMessageContainer = parentElement.querySelector(ERROR_MESSAGE_SELECTOR)
|
|
68
|
+
if (errorMessageContainer) errorMessageContainer.remove()
|
|
101
69
|
}
|
|
102
70
|
|
|
103
71
|
get errorMessageContainer() {
|
|
@@ -3,7 +3,7 @@ import React from 'react'
|
|
|
3
3
|
import classnames from 'classnames'
|
|
4
4
|
|
|
5
5
|
import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../utilities/props'
|
|
6
|
-
import { globalProps } from '../utilities/globalProps'
|
|
6
|
+
import { globalProps, GlobalProps } from '../utilities/globalProps'
|
|
7
7
|
|
|
8
8
|
import Icon from '../pb_icon/_icon'
|
|
9
9
|
|
|
@@ -26,7 +26,7 @@ type IconCircleProps = {
|
|
|
26
26
|
| "orange"
|
|
27
27
|
| "green"
|
|
28
28
|
| "lighter",
|
|
29
|
-
}
|
|
29
|
+
} & GlobalProps
|
|
30
30
|
|
|
31
31
|
const IconCircle = (props: IconCircleProps) => {
|
|
32
32
|
const {
|
|
@@ -3,51 +3,45 @@
|
|
|
3
3
|
@import "../tokens/spacing";
|
|
4
4
|
@import "../pb_icon_circle/icon_circle";
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
.pb_icon_stat_value_kit_horizontal,
|
|
7
|
+
.pb_icon_stat_value_kit_vertical
|
|
8
|
+
{
|
|
7
9
|
display: flex;
|
|
8
10
|
align-items: baseline;
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
&.pb_icon_stat_value_kit_vertical {
|
|
11
13
|
flex-direction: column;
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
&.text_align_center {
|
|
14
16
|
align-items: center;
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
.pb_title_kit,
|
|
19
|
+
.pb_body_kit,
|
|
20
|
+
.pb_caption_kit_md {
|
|
19
21
|
text-align: center;
|
|
20
22
|
}
|
|
21
23
|
}
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
&.text_align_right {
|
|
24
26
|
align-items: flex-end;
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
.pb_title_kit,
|
|
29
|
+
.pb_body_kit,
|
|
30
|
+
.pb_caption_kit_md {
|
|
29
31
|
text-align: right;
|
|
30
32
|
}
|
|
31
33
|
}
|
|
32
|
-
|
|
33
|
-
[class^=pb_icon_circle] {
|
|
34
|
-
margin-bottom: $space-xs;
|
|
35
|
-
}
|
|
36
34
|
}
|
|
37
35
|
|
|
38
|
-
|
|
36
|
+
&.pb_icon_stat_value_kit_horizontal {
|
|
39
37
|
align-items: center;
|
|
40
38
|
|
|
41
|
-
|
|
39
|
+
&.text_align_center {
|
|
42
40
|
justify-content: center;
|
|
43
41
|
}
|
|
44
42
|
|
|
45
|
-
|
|
43
|
+
&.text_align_right {
|
|
46
44
|
justify-content: flex-end;
|
|
47
45
|
}
|
|
48
|
-
|
|
49
|
-
[class^=pb_icon_circle] {
|
|
50
|
-
margin-right: $space-sm;
|
|
51
|
-
}
|
|
52
46
|
}
|
|
53
47
|
}
|
|
@@ -2,7 +2,7 @@ import React from 'react'
|
|
|
2
2
|
import classnames from 'classnames'
|
|
3
3
|
|
|
4
4
|
import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../utilities/props'
|
|
5
|
-
import { globalProps } from '../utilities/globalProps'
|
|
5
|
+
import { globalProps, GlobalProps } from '../utilities/globalProps'
|
|
6
6
|
|
|
7
7
|
import Body from '../pb_body/_body'
|
|
8
8
|
import Caption from '../pb_caption/_caption'
|
|
@@ -33,8 +33,7 @@ type IconStatValueProps = {
|
|
|
33
33
|
| "yellow"
|
|
34
34
|
| "orange"
|
|
35
35
|
| "green"
|
|
36
|
-
|
|
37
|
-
}
|
|
36
|
+
} & GlobalProps
|
|
38
37
|
|
|
39
38
|
const IconStatValue = (props: IconStatValueProps): React.ReactElement => {
|
|
40
39
|
const {
|
|
@@ -50,13 +49,13 @@ const IconStatValue = (props: IconStatValueProps): React.ReactElement => {
|
|
|
50
49
|
text = '',
|
|
51
50
|
unit = '',
|
|
52
51
|
value = 0,
|
|
53
|
-
variant = '
|
|
52
|
+
variant = 'default',
|
|
54
53
|
} = props
|
|
55
54
|
const ariaProps = buildAriaProps(aria)
|
|
56
55
|
const dataProps = buildDataProps(data)
|
|
57
56
|
const htmlProps = buildHtmlProps(htmlOptions)
|
|
58
57
|
const classes = classnames(
|
|
59
|
-
buildCss('pb_icon_stat_value_kit', orientation
|
|
58
|
+
buildCss('pb_icon_stat_value_kit', orientation), globalProps(props),
|
|
60
59
|
className
|
|
61
60
|
)
|
|
62
61
|
const titleSize = function(size: "sm" | "md" | "lg") {
|
|
@@ -101,6 +100,8 @@ const IconStatValue = (props: IconStatValueProps): React.ReactElement => {
|
|
|
101
100
|
<IconCircle
|
|
102
101
|
dark={dark}
|
|
103
102
|
icon={icon}
|
|
103
|
+
marginBottom={orientation == 'vertical' ? 'xs' : undefined}
|
|
104
|
+
marginRight={orientation == 'horizontal' ? 'sm' : undefined}
|
|
104
105
|
size={size}
|
|
105
106
|
variant={variant}
|
|
106
107
|
/>
|
|
@@ -9,8 +9,8 @@ module Playbook
|
|
|
9
9
|
values: %w[sm md lg],
|
|
10
10
|
default: "sm"
|
|
11
11
|
prop :variant, type: Playbook::Props::Enum,
|
|
12
|
-
values: %w[default royal blue purple teal red yellow green orange
|
|
13
|
-
default: "
|
|
12
|
+
values: %w[default royal blue purple teal red yellow green orange],
|
|
13
|
+
default: "default"
|
|
14
14
|
|
|
15
15
|
prop :orientation, type: Playbook::Props::Enum,
|
|
16
16
|
values: %w[vertical horizontal],
|
|
@@ -25,7 +25,7 @@ module Playbook
|
|
|
25
25
|
prop :value
|
|
26
26
|
|
|
27
27
|
def classname
|
|
28
|
-
generate_classname("pb_icon_stat_value_kit", orientation
|
|
28
|
+
generate_classname("pb_icon_stat_value_kit", orientation)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def value_string
|
|
@@ -41,6 +41,14 @@ module Playbook
|
|
|
41
41
|
3
|
|
42
42
|
end
|
|
43
43
|
end
|
|
44
|
+
|
|
45
|
+
def icon_margin_right
|
|
46
|
+
orientation === "horizontal" && "sm"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def icon_margin_bottom
|
|
50
|
+
orientation === "vertical" && "xs"
|
|
51
|
+
end
|
|
44
52
|
end
|
|
45
53
|
end
|
|
46
54
|
end
|
|
@@ -18,7 +18,7 @@ describe("IconStatValue Kit", () => {
|
|
|
18
18
|
)
|
|
19
19
|
|
|
20
20
|
const kit = screen.getByTestId(testId)
|
|
21
|
-
expect(kit).toHaveClass("
|
|
21
|
+
expect(kit).toHaveClass("pb_icon_stat_value_kit_horizontal")
|
|
22
22
|
})
|
|
23
23
|
|
|
24
24
|
test("renders icon", () => {
|
|
@@ -99,9 +99,10 @@ describe("IconStatValue Kit", () => {
|
|
|
99
99
|
value={64.18}
|
|
100
100
|
/>
|
|
101
101
|
)
|
|
102
|
-
|
|
102
|
+
const size = sizeProp === "sm" ? "3" : sizeProp === "md" ? "2" : "1"
|
|
103
103
|
const kit = screen.getByTestId(testId)
|
|
104
|
-
|
|
104
|
+
const title = kit.querySelector(".pb_title_kit")
|
|
105
|
+
expect(title).toHaveClass(`pb_title_${size}`)
|
|
105
106
|
|
|
106
107
|
cleanup()
|
|
107
108
|
})
|
|
@@ -115,8 +116,7 @@ describe("IconStatValue Kit", () => {
|
|
|
115
116
|
"teal",
|
|
116
117
|
"red",
|
|
117
118
|
"yellow",
|
|
118
|
-
"green"
|
|
119
|
-
"lighter"].forEach(
|
|
119
|
+
"green"].forEach(
|
|
120
120
|
(colorProp) => {
|
|
121
121
|
render(
|
|
122
122
|
<IconStatValue
|
|
@@ -128,9 +128,10 @@ describe("IconStatValue Kit", () => {
|
|
|
128
128
|
variant={colorProp}
|
|
129
129
|
/>
|
|
130
130
|
)
|
|
131
|
-
|
|
131
|
+
|
|
132
132
|
const kit = screen.getByTestId(testId)
|
|
133
|
-
|
|
133
|
+
const iconCircle = kit.querySelector(`.pb_icon_circle_kit_size_sm_${colorProp}`)
|
|
134
|
+
expect(iconCircle).toBeInTheDocument()
|
|
134
135
|
|
|
135
136
|
cleanup()
|
|
136
137
|
})
|
|
@@ -149,7 +150,7 @@ describe("IconStatValue Kit", () => {
|
|
|
149
150
|
)
|
|
150
151
|
|
|
151
152
|
const kit = screen.getByTestId(testId)
|
|
152
|
-
expect(kit).toHaveClass("
|
|
153
|
+
expect(kit).toHaveClass("pb_icon_stat_value_kit_vertical")
|
|
153
154
|
})
|
|
154
155
|
|
|
155
156
|
})
|
|
@@ -110,35 +110,18 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
110
110
|
|
|
111
111
|
const inputRef = useRef<HTMLInputElement | null>(null)
|
|
112
112
|
const itiRef = useRef<any>(null);
|
|
113
|
-
const wrapperRef = useRef<HTMLDivElement | null>(null); // Add wrapper ref
|
|
114
113
|
const [inputValue, setInputValue] = useState(value)
|
|
115
114
|
const [error, setError] = useState(props.error)
|
|
116
115
|
const [dropDownIsOpen, setDropDownIsOpen] = useState(false)
|
|
117
116
|
const [selectedData, setSelectedData] = useState()
|
|
118
117
|
const [hasTyped, setHasTyped] = useState(false)
|
|
119
118
|
|
|
120
|
-
// Function to update validation state on the wrapper element
|
|
121
|
-
// Only applies when input is required
|
|
122
|
-
const updateValidationState = (hasError: boolean) => {
|
|
123
|
-
if (wrapperRef.current && required) {
|
|
124
|
-
if (hasError) {
|
|
125
|
-
wrapperRef.current.setAttribute('data-pb-phone-validation-error', 'true')
|
|
126
|
-
} else {
|
|
127
|
-
wrapperRef.current.removeAttribute('data-pb-phone-validation-error')
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
119
|
useEffect(() => {
|
|
133
|
-
|
|
134
|
-
if (hasError) {
|
|
120
|
+
if ((error ?? '').length > 0) {
|
|
135
121
|
onValidate(false)
|
|
136
122
|
} else {
|
|
137
123
|
onValidate(true)
|
|
138
124
|
}
|
|
139
|
-
|
|
140
|
-
// Update validation state whenever error changes
|
|
141
|
-
updateValidationState(hasError)
|
|
142
125
|
}, [error, onValidate])
|
|
143
126
|
|
|
144
127
|
/*
|
|
@@ -152,10 +135,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
152
135
|
setInputValue("")
|
|
153
136
|
setError("")
|
|
154
137
|
setHasTyped(false)
|
|
155
|
-
// Only clear validation state if field was required
|
|
156
|
-
if (required) {
|
|
157
|
-
updateValidationState(false)
|
|
158
|
-
}
|
|
159
138
|
},
|
|
160
139
|
inputNode() {
|
|
161
140
|
return inputRef.current
|
|
@@ -185,13 +164,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
185
164
|
|
|
186
165
|
const validateTooShortNumber = (itiInit: any) => {
|
|
187
166
|
if (!itiInit) return
|
|
188
|
-
|
|
189
|
-
// If field is empty, don't show "too short" error
|
|
190
|
-
if (!inputValue || inputValue.trim() === '') {
|
|
191
|
-
setError('')
|
|
192
|
-
return false
|
|
193
|
-
}
|
|
194
|
-
|
|
195
167
|
if (itiInit.getValidationError() === ValidationError.TooShort) {
|
|
196
168
|
return showFormattedError('too short')
|
|
197
169
|
} else {
|
|
@@ -240,26 +212,8 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
240
212
|
}
|
|
241
213
|
}
|
|
242
214
|
|
|
243
|
-
// Validation for required empty fields
|
|
244
|
-
const validateRequiredField = () => {
|
|
245
|
-
if (required && (!inputValue || inputValue.trim() === '')) {
|
|
246
|
-
setError('Missing phone number')
|
|
247
|
-
return true
|
|
248
|
-
}
|
|
249
|
-
return false
|
|
250
|
-
}
|
|
251
215
|
|
|
252
216
|
const validateErrors = () => {
|
|
253
|
-
// If field is empty, only show required field error if applicable
|
|
254
|
-
if (!inputValue || inputValue.trim() === '') {
|
|
255
|
-
if (validateRequiredField()) return
|
|
256
|
-
// Clear any existing errors if field is empty and not required
|
|
257
|
-
if (!required) {
|
|
258
|
-
setError('')
|
|
259
|
-
}
|
|
260
|
-
return
|
|
261
|
-
}
|
|
262
|
-
|
|
263
217
|
if (!hasTyped && !error) return
|
|
264
218
|
|
|
265
219
|
if (itiRef.current) isValid(itiRef.current.isValidNumber())
|
|
@@ -356,10 +310,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
356
310
|
value: inputValue
|
|
357
311
|
}
|
|
358
312
|
|
|
359
|
-
let wrapperProps: Record<string, unknown> = {
|
|
360
|
-
className: classes,
|
|
361
|
-
ref: wrapperRef // Add ref to wrapper
|
|
362
|
-
}
|
|
313
|
+
let wrapperProps: Record<string, unknown> = { className: classes }
|
|
363
314
|
|
|
364
315
|
if (!isEmpty(aria)) textInputProps = {...textInputProps, ...ariaProps}
|
|
365
316
|
if (!isEmpty(data)) wrapperProps = {...wrapperProps, ...dataProps}
|