playbook_ui 16.2.0 → 16.3.0.pre.rc.1
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_date_picker/docs/_date_picker_positions.md +1 -0
- data/app/pb_kits/playbook/pb_dialog/docs/_dialog_compound_components.md +3 -1
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +12 -3
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_closing_options.jsx +63 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_closing_options.md +1 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +1 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_dropdown/utilities/clickOutsideHelper.tsx +7 -1
- data/app/pb_kits/playbook/pb_form/docs/_form_form_with.html.erb +2 -1
- data/app/pb_kits/playbook/pb_form/docs/_form_with_required_indicator.html.erb +2 -0
- data/app/pb_kits/playbook/pb_link/_link.scss +16 -1
- data/app/pb_kits/playbook/pb_link/docs/_link_underline.jsx +31 -1
- data/app/pb_kits/playbook/pb_select/_select.tsx +26 -18
- data/app/pb_kits/playbook/pb_select/docs/_select_required_indicator.html.erb +24 -0
- data/app/pb_kits/playbook/pb_select/docs/_select_required_indicator.jsx +33 -0
- data/app/pb_kits/playbook/pb_select/docs/_select_required_indicator.md +3 -0
- data/app/pb_kits/playbook/pb_select/docs/example.yml +3 -2
- data/app/pb_kits/playbook/pb_select/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_select/select.html.erb +7 -1
- data/app/pb_kits/playbook/pb_select/select.rb +3 -0
- data/app/pb_kits/playbook/pb_textarea/docs/_textarea_disabled.html.erb +10 -0
- data/app/pb_kits/playbook/pb_textarea/docs/_textarea_disabled.jsx +27 -0
- data/app/pb_kits/playbook/pb_textarea/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_textarea/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_textarea/textarea.rb +3 -0
- data/dist/chunks/{_pb_line_graph-CsDClWC8.js → _pb_line_graph-CC2Ywwix.js} +1 -1
- data/dist/chunks/_typeahead-DtzfGPUT.js +1 -0
- data/dist/chunks/{globalProps-CW_a2wOO.js → globalProps-DYr2qrIf.js} +1 -1
- data/dist/chunks/{lib-DHuT-Q9y.js → lib-DgqmX9CF.js} +2 -2
- data/dist/chunks/vendor.js +3 -3
- 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/forms/builder/collection_select_field.rb +7 -1
- data/lib/playbook/forms/builder/intl_telephone_field.rb +11 -1
- data/lib/playbook/forms/builder/phone_number_field.rb +3 -2
- data/lib/playbook/forms/builder/select_field.rb +7 -1
- data/lib/playbook/forms/builder/time_zone_select_field.rb +7 -1
- data/lib/playbook/version.rb +2 -2
- metadata +13 -6
- data/dist/chunks/_typeahead-E5Ps8hIY.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: 3629bc8a253f0631e1b54a8e3938931ae90e63c559c3a625672052bc0be318f7
|
|
4
|
+
data.tar.gz: 54c575a109980430cc5e2d4e3f63195fc1de9e2500a1ff1a2c6cebfc43093e7f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4df5302f570f6cdf3adcfbbd22adad49dc36849417dff87ef46fb4f8d38265b83899b383d8d51ecb6d4d40d3c035931afeed7197b9bdd450502c7dba0836124a
|
|
7
|
+
data.tar.gz: f390fe264e647517425c11b609c5801cc36eae3db4751d0d174f6f42b167ec99d190e015e8520213eedf4693507c1140ca84dd92309bf0918bcf1f89b49db51a
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
Datepicker supports `position` options from [Flatpickr Options Documentation](https://flatpickr.js.org/options/). There are multiple positioning options to choose from.
|
|
2
2
|
|
|
3
3
|
**Note:** In order for the above prop to work properly, you must also send `staticPosition={false}` to your Datepicker kit instance.
|
|
4
|
+
If you are using the Datepicker within a Dialog, you cannot use the `staticPosition`/`static_position` prop.
|
|
4
5
|
|
|
5
6
|
#### Affix Datepicker Upon Scrolling
|
|
6
7
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
The dialog kit also supports customizing your dialog with a [compound component](https://kentcdodds.com/blog/compound-components-with-react-hooks) structure.
|
|
2
2
|
This allows for greater flexibility and more complex dialogs.
|
|
3
3
|
|
|
4
|
-
For the Rails version, when using the kit as a compound component it is necessary to pass the same value as the id for the dialog, the dialog header and the dialog footer in order for the opening and closing of the dialog to function as expected.
|
|
4
|
+
For the Rails version, when using the kit as a compound component it is necessary to pass the same value as the id for the dialog, the dialog header and the dialog footer in order for the opening and closing of the dialog to function as expected.
|
|
5
|
+
|
|
6
|
+
If you are using the datepicker within the Rails dialog, do not use the `static_position` prop on the datepicker.
|
|
@@ -38,6 +38,7 @@ type DropdownProps = {
|
|
|
38
38
|
children?: React.ReactChild[] | React.ReactChild | React.ReactElement[];
|
|
39
39
|
className?: string;
|
|
40
40
|
clearable?: boolean;
|
|
41
|
+
closeOnClick?: "outside" | "inside" | "any";
|
|
41
42
|
constrainHeight?: boolean;
|
|
42
43
|
customQuickPickDates?: CustomQuickPickDates;
|
|
43
44
|
formPillProps?: GenericObject;
|
|
@@ -80,6 +81,7 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
|
|
|
80
81
|
children,
|
|
81
82
|
className,
|
|
82
83
|
clearable = true,
|
|
84
|
+
closeOnClick = "any",
|
|
83
85
|
constrainHeight = false,
|
|
84
86
|
customQuickPickDates,
|
|
85
87
|
dark = false,
|
|
@@ -194,13 +196,14 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
|
|
|
194
196
|
setIsDropDownClosed,
|
|
195
197
|
setFocusedOptionIndex,
|
|
196
198
|
setIsInputFocused,
|
|
199
|
+
closeOnClick,
|
|
197
200
|
});
|
|
198
201
|
|
|
199
202
|
window.addEventListener("click", handleClick);
|
|
200
203
|
return () => {
|
|
201
204
|
window.removeEventListener("click", handleClick);
|
|
202
205
|
};
|
|
203
|
-
}, []);
|
|
206
|
+
}, [closeOnClick]);
|
|
204
207
|
|
|
205
208
|
useEffect(() => {
|
|
206
209
|
setHasTriggerSubcomponent(!!trigger);
|
|
@@ -276,6 +279,8 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
|
|
|
276
279
|
|
|
277
280
|
|
|
278
281
|
const handleOptionClick = (clickedItem: GenericObject) => {
|
|
282
|
+
const shouldCloseOnClick = closeOnClick === "any" || closeOnClick === "inside";
|
|
283
|
+
|
|
279
284
|
if (multiSelect) {
|
|
280
285
|
setSelected((prev) => {
|
|
281
286
|
const list = prev as GenericObject[];
|
|
@@ -287,11 +292,15 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
|
|
|
287
292
|
return next;
|
|
288
293
|
});
|
|
289
294
|
setFilterItem("");
|
|
290
|
-
|
|
295
|
+
if (shouldCloseOnClick) {
|
|
296
|
+
setIsDropDownClosed(true);
|
|
297
|
+
}
|
|
291
298
|
} else {
|
|
292
299
|
setSelected(clickedItem);
|
|
293
300
|
setFilterItem("");
|
|
294
|
-
|
|
301
|
+
if (shouldCloseOnClick) {
|
|
302
|
+
setIsDropDownClosed(true);
|
|
303
|
+
}
|
|
295
304
|
onSelect && onSelect(clickedItem);
|
|
296
305
|
|
|
297
306
|
// Sync with DatePickers if this is a quickpick variant
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import Dropdown from '../../pb_dropdown/_dropdown'
|
|
3
|
+
import Caption from '../../pb_caption/_caption'
|
|
4
|
+
|
|
5
|
+
const DropdownClosingOptions = (props) => {
|
|
6
|
+
const options = [
|
|
7
|
+
{
|
|
8
|
+
label: "United States",
|
|
9
|
+
value: "unitedStates",
|
|
10
|
+
id: "us"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
label: "Canada",
|
|
14
|
+
value: "canada",
|
|
15
|
+
id: "ca"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
label: "Pakistan",
|
|
19
|
+
value: "pakistan",
|
|
20
|
+
id: "pk"
|
|
21
|
+
}
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<>
|
|
26
|
+
<Caption
|
|
27
|
+
marginBottom="xs"
|
|
28
|
+
text="Any"
|
|
29
|
+
/>
|
|
30
|
+
<Dropdown
|
|
31
|
+
closeOnClick='any'
|
|
32
|
+
options={options}
|
|
33
|
+
{...props}
|
|
34
|
+
/>
|
|
35
|
+
|
|
36
|
+
<br />
|
|
37
|
+
|
|
38
|
+
<Caption
|
|
39
|
+
marginBottom="xs"
|
|
40
|
+
text="Outside"
|
|
41
|
+
/>
|
|
42
|
+
<Dropdown
|
|
43
|
+
closeOnClick='outside'
|
|
44
|
+
options={options}
|
|
45
|
+
{...props}
|
|
46
|
+
/>
|
|
47
|
+
|
|
48
|
+
<br />
|
|
49
|
+
|
|
50
|
+
<Caption
|
|
51
|
+
marginBottom="xs"
|
|
52
|
+
text="Inside"
|
|
53
|
+
/>
|
|
54
|
+
<Dropdown
|
|
55
|
+
closeOnClick='inside'
|
|
56
|
+
options={options}
|
|
57
|
+
{...props}
|
|
58
|
+
/>
|
|
59
|
+
</>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export default DropdownClosingOptions
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
The `closeOnClick` prop allows you to control when the Dropdown closes in response to click interactions. The value `any` reflects the default behavior, where the dropdown will close after any click. Set it to `outside` to ensure interactive elements as dropdown options are able to be interacted with or modified. Set it to `inside` for a dropdown that only closes when the input or dropdown menu is clicked.
|
|
@@ -62,6 +62,7 @@ examples:
|
|
|
62
62
|
- dropdown_with_constrain_height: Constrain Height
|
|
63
63
|
- dropdown_separators_hidden: Separators Hidden
|
|
64
64
|
- dropdown_with_external_control: useDropdown Hook
|
|
65
|
+
- dropdown_closing_options: Closing Options
|
|
65
66
|
- dropdown_quickpick: Quick Pick Variant
|
|
66
67
|
- dropdown_quickpick_range_end: Quick Pick Variant (Range Ends Today)
|
|
67
68
|
- dropdown_quickpick_default_dates: Quick Pick Variant (Default Dates)
|
|
@@ -31,4 +31,5 @@ export { default as DropdownQuickpickWithDatePickers } from './_dropdown_quickpi
|
|
|
31
31
|
export { default as DropdownQuickpickCustom } from './_dropdown_quickpick_custom.jsx'
|
|
32
32
|
export { default as DropdownWithClearable } from './_dropdown_with_clearable.jsx'
|
|
33
33
|
export { default as DropdownWithConstrainHeight } from './_dropdown_with_constrain_height.jsx'
|
|
34
|
+
export { default as DropdownClosingOptions } from './_dropdown_closing_options.jsx'
|
|
34
35
|
export { default as DropdownRequiredIndicator } from "./_dropdown_required_indicator.jsx";
|
|
@@ -4,6 +4,7 @@ type HandleClickOutsideType = {
|
|
|
4
4
|
setIsDropDownClosed?: (value: boolean) => void;
|
|
5
5
|
setFocusedOptionIndex?: (value: number) => void;
|
|
6
6
|
setIsInputFocused?: (value: boolean) => void;
|
|
7
|
+
closeOnClick?: "outside" | "inside" | "any";
|
|
7
8
|
};
|
|
8
9
|
|
|
9
10
|
export const handleClickOutside =
|
|
@@ -13,6 +14,7 @@ export const handleClickOutside =
|
|
|
13
14
|
setIsDropDownClosed,
|
|
14
15
|
setFocusedOptionIndex,
|
|
15
16
|
setIsInputFocused,
|
|
17
|
+
closeOnClick = "any",
|
|
16
18
|
}: HandleClickOutsideType) =>
|
|
17
19
|
(e: MouseEvent) => {
|
|
18
20
|
let targetElement = e.target as HTMLElement;
|
|
@@ -33,12 +35,16 @@ export const handleClickOutside =
|
|
|
33
35
|
}
|
|
34
36
|
targetElement = targetElement.parentElement as HTMLElement;
|
|
35
37
|
}
|
|
38
|
+
// Only close on outside click if closeOnClick is "outside" or "any"
|
|
39
|
+
const shouldCloseOnOutsideClick = closeOnClick === "outside" || closeOnClick === "any";
|
|
40
|
+
|
|
36
41
|
if (
|
|
37
42
|
inputWrapperRef.current &&
|
|
38
43
|
!inputWrapperRef.current.contains((e.target as HTMLElement)) &&
|
|
39
44
|
dropdownContainerRef.current &&
|
|
40
45
|
!dropdownContainerRef.current.contains((e.target as HTMLElement)) &&
|
|
41
|
-
shouldClose
|
|
46
|
+
shouldClose &&
|
|
47
|
+
shouldCloseOnOutsideClick
|
|
42
48
|
) {
|
|
43
49
|
setIsDropDownClosed(true);
|
|
44
50
|
setFocusedOptionIndex(-1);
|
|
@@ -92,7 +92,8 @@
|
|
|
92
92
|
<%= form.typeahead :example_typeahead, props: { data: { typeahead_example1: true, user: {} }, label: true, placeholder: "Search for a user" } %>
|
|
93
93
|
<%= form.text_field :example_text_field, props: { label: true } %>
|
|
94
94
|
<%= form.text_field :example_text_field_2, props: { label: "Text Field Custom Label" } %>
|
|
95
|
-
<%= form.phone_number_field :example_phone_number_field, props: { label:
|
|
95
|
+
<%= form.phone_number_field :example_phone_number_field, props: { label: true, hidden_inputs: true} %>
|
|
96
|
+
<%= form.intl_telephone :example_intl_telephone, props: { label: true, hidden_inputs: true } %>
|
|
96
97
|
<%= form.email_field :example_email_field, props: { label: true } %>
|
|
97
98
|
<%= form.number_field :example_number_field, props: { label: true } %>
|
|
98
99
|
<%= form.search_field :example_search_field, props: { label: true } %>
|
|
@@ -146,6 +146,8 @@
|
|
|
146
146
|
<%= form.time_picker :example_time_picker_required_indicator, props: { label: true, required: true, required_indicator: true } %>
|
|
147
147
|
<%= form.date_picker :example_date_picker_required_indicator, props: { label: true, required: true, required_indicator: true } %>
|
|
148
148
|
<%= form.date_picker :example_date_picker_required_indicator_custom, props: { label: "Custom Date Picker Label", required: true, required_indicator: true } %>
|
|
149
|
+
<%= form.select :example_select_required_indicator, [["Yes", 1], ["No", 2]], props: { label: true, required: true, required_indicator: true } %>
|
|
150
|
+
<%= form.select :example_select_required_indicator_2, [["Yes", 1], ["No", 2]], props: { label: "Example Select Field", required: true, required_indicator: true } %>
|
|
149
151
|
<%= form.multi_level_select :example_multi_level_select_required_indicator, props: {
|
|
150
152
|
label: true,
|
|
151
153
|
margin_y: 'sm',
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
@import "../tokens/border_radius";
|
|
5
5
|
|
|
6
6
|
// Base styles
|
|
7
|
-
.pb_link_kit
|
|
7
|
+
.pb_link_kit,
|
|
8
|
+
.pb_link_kit_underline {
|
|
8
9
|
@include pb_link($primary);
|
|
9
10
|
&:hover {
|
|
10
11
|
color: $text_lt_default;
|
|
@@ -113,6 +114,20 @@
|
|
|
113
114
|
}
|
|
114
115
|
}
|
|
115
116
|
|
|
117
|
+
// Dark mode color + underline combinations
|
|
118
|
+
@each $dark_color_name, $dark_color_value in $pb_dark_link_colors {
|
|
119
|
+
.pb_link_kit_#{$dark_color_name}_underline.dark {
|
|
120
|
+
@include pb_link($dark_color_value);
|
|
121
|
+
&:hover {
|
|
122
|
+
color: map-get($pb_dark_link_hover_colors, $dark_color_name);
|
|
123
|
+
}
|
|
124
|
+
&:visited {
|
|
125
|
+
color: darken($primary_action, 10%);
|
|
126
|
+
}
|
|
127
|
+
text-decoration: underline;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
116
131
|
// Color + disabled combinations
|
|
117
132
|
@each $color_name, $color_value in $pb_link_colors {
|
|
118
133
|
@if $color_name != "default" {
|
|
@@ -4,7 +4,37 @@ import Link from '../../pb_link/_link'
|
|
|
4
4
|
const LinkUnderline = (props) => (
|
|
5
5
|
<div>
|
|
6
6
|
<Link
|
|
7
|
-
href="#
|
|
7
|
+
href="#underline1"
|
|
8
|
+
text="link example"
|
|
9
|
+
underline
|
|
10
|
+
{...props}
|
|
11
|
+
/>
|
|
12
|
+
|
|
13
|
+
<br />
|
|
14
|
+
|
|
15
|
+
<Link
|
|
16
|
+
color="body"
|
|
17
|
+
href="#underline2"
|
|
18
|
+
text="link example"
|
|
19
|
+
underline
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
|
|
23
|
+
<br />
|
|
24
|
+
|
|
25
|
+
<Link
|
|
26
|
+
color="muted"
|
|
27
|
+
href="#underline3"
|
|
28
|
+
text="link example"
|
|
29
|
+
underline
|
|
30
|
+
{...props}
|
|
31
|
+
/>
|
|
32
|
+
|
|
33
|
+
<br />
|
|
34
|
+
|
|
35
|
+
<Link
|
|
36
|
+
color="destructive"
|
|
37
|
+
href="#underline4"
|
|
8
38
|
text="link example"
|
|
9
39
|
underline
|
|
10
40
|
{...props}
|
|
@@ -4,7 +4,7 @@ import classnames from 'classnames'
|
|
|
4
4
|
import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../utilities/props'
|
|
5
5
|
import { globalProps, GlobalProps, domSafeProps } from '../utilities/globalProps'
|
|
6
6
|
import type { InputCallback } from '../types'
|
|
7
|
-
import { getAllIcons } from
|
|
7
|
+
import { getAllIcons } from '../utilities/icons/allicons'
|
|
8
8
|
|
|
9
9
|
import Body from '../pb_body/_body'
|
|
10
10
|
import Caption from '../pb_caption/_caption'
|
|
@@ -38,6 +38,7 @@ type SelectProps = {
|
|
|
38
38
|
onChange: InputCallback<HTMLSelectElement>,
|
|
39
39
|
options: SelectOption[],
|
|
40
40
|
required?: boolean,
|
|
41
|
+
requiredIndicator?: boolean,
|
|
41
42
|
showArrow?: boolean,
|
|
42
43
|
value?: string,
|
|
43
44
|
} & GlobalProps
|
|
@@ -70,6 +71,7 @@ const Select = ({
|
|
|
70
71
|
onChange = () => undefined,
|
|
71
72
|
options = [],
|
|
72
73
|
required = false,
|
|
74
|
+
requiredIndicator = false,
|
|
73
75
|
showArrow = false,
|
|
74
76
|
value,
|
|
75
77
|
...props
|
|
@@ -121,39 +123,45 @@ const Select = ({
|
|
|
121
123
|
})()
|
|
122
124
|
|
|
123
125
|
return (
|
|
124
|
-
<div
|
|
125
|
-
{...ariaProps}
|
|
126
|
+
<div {...ariaProps}
|
|
126
127
|
{...dataProps}
|
|
127
128
|
{...htmlProps}
|
|
128
129
|
className={classes}
|
|
129
130
|
>
|
|
130
|
-
{label &&
|
|
131
|
-
<label
|
|
132
|
-
className="pb_select_kit_label"
|
|
131
|
+
{label && (
|
|
132
|
+
<label className="pb_select_kit_label"
|
|
133
133
|
htmlFor={selectId}
|
|
134
134
|
>
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
{requiredIndicator ? (
|
|
136
|
+
<Caption
|
|
137
|
+
dark={props.dark}>
|
|
138
|
+
{label}
|
|
139
|
+
<span style={{ color: "#DA0014" }}> *</span>
|
|
140
|
+
</Caption>
|
|
141
|
+
) : (
|
|
142
|
+
<Caption
|
|
143
|
+
dark={props.dark}
|
|
144
|
+
text={label}
|
|
145
|
+
/>
|
|
146
|
+
)}
|
|
139
147
|
</label>
|
|
140
|
-
}
|
|
141
|
-
<label
|
|
148
|
+
)}
|
|
149
|
+
<label
|
|
142
150
|
className={selectWrapperClass}
|
|
143
151
|
htmlFor={selectId}
|
|
144
152
|
>
|
|
145
153
|
{selectBody}
|
|
146
|
-
{ multiple !== true ?
|
|
154
|
+
{ multiple !== true ?
|
|
147
155
|
<Icon
|
|
148
156
|
className="pb_select_kit_caret svg-inline--fa"
|
|
149
157
|
customIcon={angleDown}
|
|
150
158
|
fixedWidth
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
159
|
+
/>
|
|
160
|
+
:
|
|
161
|
+
null
|
|
154
162
|
}
|
|
155
|
-
{error &&
|
|
156
|
-
<Body
|
|
163
|
+
{error &&
|
|
164
|
+
<Body
|
|
157
165
|
dark={props.dark}
|
|
158
166
|
status="negative"
|
|
159
167
|
text={error}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<%= pb_rails("select", props: {
|
|
2
|
+
label: "Favorite Snacks",
|
|
3
|
+
name: "food",
|
|
4
|
+
required_indicator: true,
|
|
5
|
+
options: [
|
|
6
|
+
{
|
|
7
|
+
value: "1",
|
|
8
|
+
value_text: "Popcorn",
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
value: "2",
|
|
12
|
+
selected: true,
|
|
13
|
+
value_text: "Chips",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
value: "3",
|
|
17
|
+
value_text: "Twizzlers",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
value: "4",
|
|
21
|
+
value_text: "Cookies",
|
|
22
|
+
},
|
|
23
|
+
]
|
|
24
|
+
}) %>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { Select } from "playbook-ui";
|
|
4
|
+
|
|
5
|
+
const SelectRequiredIndicator = () => {
|
|
6
|
+
const options = [
|
|
7
|
+
{
|
|
8
|
+
value: "1",
|
|
9
|
+
text: "Popcorn",
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
value: "2",
|
|
13
|
+
text: "Chips",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
value: "3",
|
|
17
|
+
text: "Twizzlers",
|
|
18
|
+
},
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div>
|
|
23
|
+
<Select
|
|
24
|
+
label="Favorite Snack"
|
|
25
|
+
name="food"
|
|
26
|
+
options={options}
|
|
27
|
+
requiredIndicator
|
|
28
|
+
/>
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default SelectRequiredIndicator;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
The `requiredIndicator`/`required_indicator` prop displays a red asterisk (\*) next to the label, visually indicating that the field is required. This is purely visual and does not enforce validation.
|
|
2
|
+
|
|
3
|
+
You can use `requiredIndicator`/`required_indicator` with any validation approach: HTML5 validation via the `required` prop, client-side validation, or backend validation. For this reason, it works independently and doesn't need to be paired with the `required` prop.
|
|
@@ -16,7 +16,7 @@ examples:
|
|
|
16
16
|
- select_attributes: Select W/ Attributes
|
|
17
17
|
- select_multiple: Select Multiple
|
|
18
18
|
- select_input_options: Input Options
|
|
19
|
-
|
|
19
|
+
- select_required_indicator: Required Indicator
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
react:
|
|
@@ -35,8 +35,9 @@ examples:
|
|
|
35
35
|
- select_multiple: Select Multiple
|
|
36
36
|
- select_react_hook: React Hook
|
|
37
37
|
- select_input_options: Input Options
|
|
38
|
+
- select_required_indicator: Required Indicator
|
|
38
39
|
|
|
39
40
|
swift:
|
|
40
41
|
- select_default_swift: Default
|
|
41
42
|
- select_error_swift: Error
|
|
42
|
-
- select_props_table: ""
|
|
43
|
+
- select_props_table: ""
|
|
@@ -13,3 +13,4 @@ export { default as SelectMultiple } from './_select_multiple.jsx'
|
|
|
13
13
|
export { default as SelectReactHook } from './_select_react_hook.jsx'
|
|
14
14
|
export { default as SelectCustomSelectSubheaders } from './_select_custom_select_subheaders.jsx'
|
|
15
15
|
export { default as SelectInputOptions } from './_select_input_options.jsx'
|
|
16
|
+
export { default as SelectRequiredIndicator } from './_select_required_indicator.jsx'
|
|
@@ -3,7 +3,13 @@
|
|
|
3
3
|
class: object.classnames ) do %>
|
|
4
4
|
<% if object.label %>
|
|
5
5
|
<label class="pb_select_kit_label" for="<%= object.input_options[:id] || object.name %>">
|
|
6
|
-
|
|
6
|
+
<% if object.required_indicator %>
|
|
7
|
+
<%= pb_rails("caption", props: { dark: object.dark }) do %>
|
|
8
|
+
<%= object.label %><span style="color: #DA0014;"> *</span>
|
|
9
|
+
<% end %>
|
|
10
|
+
<% else %>
|
|
11
|
+
<%= pb_rails("caption", props: { text: object.label, dark: object.dark }) %>
|
|
12
|
+
<% end %>
|
|
7
13
|
</label>
|
|
8
14
|
<% end %>
|
|
9
15
|
<label class="<%= object.select_wrapper_class %>" for="<%= object.input_options[:id] || object.name %>">
|
|
@@ -24,6 +24,8 @@ module Playbook
|
|
|
24
24
|
prop :show_arrow, type: Playbook::Props::Boolean, default: false
|
|
25
25
|
prop :required, type: Playbook::Props::Boolean, default: false
|
|
26
26
|
prop :validation_message, type: Playbook::Props::String, default: ""
|
|
27
|
+
prop :required_indicator, type: Playbook::Props::Boolean,
|
|
28
|
+
default: false
|
|
27
29
|
|
|
28
30
|
def classnames
|
|
29
31
|
([classname] + [inline_class, compact_class, show_arrow_class])
|
|
@@ -37,6 +39,7 @@ module Playbook
|
|
|
37
39
|
prompt: blank_selection,
|
|
38
40
|
disabled: disabled,
|
|
39
41
|
required: required,
|
|
42
|
+
requiredIndicator: required_indicator,
|
|
40
43
|
multiple: multiple,
|
|
41
44
|
onchange: onchange,
|
|
42
45
|
include_blank: include_blank,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<%= pb_rails("textarea", props: { label: "Disabled", rows: 4, id: "disabled-example-1", disabled: true }) %>
|
|
2
|
+
|
|
3
|
+
<br/>
|
|
4
|
+
|
|
5
|
+
<%= pb_rails("textarea", props: {
|
|
6
|
+
label: "Disabled with Placeholder",
|
|
7
|
+
placeholder: "Placeholder text",
|
|
8
|
+
id: "disabled-example-2",
|
|
9
|
+
disabled: true
|
|
10
|
+
}) %>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import Textarea from '../_textarea'
|
|
4
|
+
|
|
5
|
+
const TextareaDefault = (props) => {
|
|
6
|
+
return (
|
|
7
|
+
<>
|
|
8
|
+
<Textarea
|
|
9
|
+
disabled
|
|
10
|
+
id="disabled-example"
|
|
11
|
+
label="Disabled"
|
|
12
|
+
{...props}
|
|
13
|
+
rows={4}
|
|
14
|
+
/>
|
|
15
|
+
<Textarea
|
|
16
|
+
disabled
|
|
17
|
+
id="disabled-example-with-placeholder"
|
|
18
|
+
label="Disabled with Placeholder"
|
|
19
|
+
placeholder="Content goes here"
|
|
20
|
+
{...props}
|
|
21
|
+
rows={4}
|
|
22
|
+
/>
|
|
23
|
+
</>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default TextareaDefault
|
|
@@ -10,6 +10,7 @@ examples:
|
|
|
10
10
|
- textarea_emoji_mask: Emoji Mask
|
|
11
11
|
- textarea_required_indicator: Required Indicator
|
|
12
12
|
- textarea_input_options: Input Options
|
|
13
|
+
- textarea_disabled: Disabled
|
|
13
14
|
|
|
14
15
|
react:
|
|
15
16
|
- textarea_default: Default
|
|
@@ -20,6 +21,7 @@ examples:
|
|
|
20
21
|
- textarea_inline: Inline
|
|
21
22
|
- textarea_emoji_mask: Emoji Mask
|
|
22
23
|
- textarea_required_indicator: Required Indicator
|
|
24
|
+
- textarea_disabled: Disabled
|
|
23
25
|
|
|
24
26
|
swift:
|
|
25
27
|
- textarea_default_swift: Default
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { default as TextareaDefault } from './_textarea_default.jsx'
|
|
2
|
+
export { default as TextareaDisabled } from './_textarea_disabled.jsx'
|
|
2
3
|
export { default as TextareaResize } from './_textarea_resize.jsx'
|
|
3
4
|
export { default as TextareaCustom } from './_textarea_custom.jsx'
|
|
4
5
|
export { default as TextareaError } from './_textarea_error.jsx'
|
|
@@ -25,6 +25,8 @@ module Playbook
|
|
|
25
25
|
prop :max_characters
|
|
26
26
|
prop :required_indicator, type: Playbook::Props::Boolean,
|
|
27
27
|
default: false
|
|
28
|
+
prop :disabled, type: Playbook::Props::Boolean,
|
|
29
|
+
default: false
|
|
28
30
|
|
|
29
31
|
def classname
|
|
30
32
|
generate_classname("pb_textarea_kit") + error_class + resize_class + inline_class
|
|
@@ -49,6 +51,7 @@ module Playbook
|
|
|
49
51
|
base_attributes = {
|
|
50
52
|
'aria-describedby': error.present? ? error_id : nil,
|
|
51
53
|
'aria-invalid': error.present?,
|
|
54
|
+
disabled: disabled,
|
|
52
55
|
id: input_options[:id] || id || "object_method",
|
|
53
56
|
max_characters: max_characters,
|
|
54
57
|
name: name,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx}from"react/jsx-runtime";import{useMemo}from"react";import{b as buildAriaProps,a as buildDataProps,c as buildHtmlProps,d as classnames,e as buildCss,g as globalProps}from"./globalProps-
|
|
1
|
+
import{jsx}from"react/jsx-runtime";import{useMemo}from"react";import{b as buildAriaProps,a as buildDataProps,c as buildHtmlProps,d as classnames,e as buildCss,g as globalProps}from"./globalProps-DYr2qrIf.js";import Highcharts from"highcharts";import HighchartsReact from"highcharts-react-official";import{t as typography,c as colors}from"./lib-DgqmX9CF.js";import highchartsMore from"highcharts/highcharts-more";import solidGauge from"highcharts/modules/solid-gauge";const barGraphTheme={title:{text:"",style:{color:colors.text_lt_default,fontFamily:typography.font_family_base,fontWeight:typography.bold,fontSize:typography.heading_3}},subtitle:{text:"",style:{fontFamily:typography.font_family_base,color:colors.text_lt_light,fontWeight:typography.regular,fontSize:typography.text_base}},chart:{type:"column"},tooltip:{backgroundColor:{linearGradient:{x1:0,y1:0,x2:0,y2:1},stops:[[0,colors.bg_dark],[1,colors.bg_dark]]},style:{fontFamily:typography.font_family_base,color:colors.text_dk_default,fontWeight:typography.regular,fontSize:typography.text_smaller}},colors:[colors.data_1,colors.data_2,colors.data_3,colors.data_4,colors.data_5,colors.data_6,colors.data_7],credits:{enabled:false},legend:{enabled:false,itemStyle:{color:colors.text_lt_light,fill:colors.text_lt_light,fontSize:typography.text_smaller}},xAxis:{gridLineWidth:0,lineColor:colors.border_light,tickColor:colors.border_light,labels:{style:{fontFamily:typography.font_family_base,color:colors.text_lt_lighter,fontWeight:typography.bold,fontSize:typography.text_smaller}},title:{style:{color:colors.text_lt_default,fontFamily:typography.font_family_base,fontWeight:typography.regular,fontSize:typography.heading_4}}},yAxis:{alternateGridColor:void 0,minorTickInterval:null,gridLineColor:colors.border_light,minorGridLineColor:colors.border_light,lineWidth:0,tickWidth:0,labels:{style:{fontFamily:typography.font_family_base,color:colors.text_lt_lighter,fontWeight:typography.bold,fontSize:typography.text_smaller}},title:{style:{fontFamily:typography.font_family_base,color:colors.text_lt_lighter,fontWeight:typography.bold,fontSize:typography.text_smaller}}}};const PbBarGraph=props=>{const{aria:aria={},data:data={},id:id,htmlOptions:htmlOptions={},options:options,className:className}=props;const ariaProps=buildAriaProps(aria);const dataProps=buildDataProps(data);const htmlProps=buildHtmlProps(htmlOptions);const classes=classnames(buildCss("pb_pb_bar_graph"),globalProps(props),className);const mergedOptions=useMemo((()=>{if(!options||typeof options!=="object"){console.error("❌ Invalid options passed to <PbBarGraph />",options);return{}}return Highcharts.merge({},barGraphTheme,options)}),[options]);return jsx("div",{children:jsx(HighchartsReact,{containerProps:{className:classnames(classes),id:id,...ariaProps,...dataProps,...htmlProps},highcharts:Highcharts,options:mergedOptions})})};const pbCircleChartTheme={title:{text:"",style:{color:colors.text_lt_default,fontFamily:typography.font_family_base,fontWeight:typography.bold,fontSize:typography.heading_3}},chart:{type:"pie"},tooltip:{backgroundColor:{linearGradient:{x1:0,y1:0,x2:0,y2:1},stops:[[0,colors.bg_dark],[1,colors.bg_dark]]},pointFormat:'<span style="font-weight: bold; color:{point.color};">●</span>{point.name}: <b>{point.y}</b>',followPointer:true,shadow:false,borderWidth:0,borderRadius:10,style:{fontFamily:typography.font_family_base,color:colors.text_dk_default,fontWeight:typography.regular,fontSize:typography.text_smaller}},plotOptions:{pie:{dataLabels:{enabled:false,connectorShape:"straight",connectorWidth:3,format:"<div>{point.name}</div>",style:{fontFamily:typography.font_family_base,fontSize:typography.text_smaller,color:colors.text_lt_light,fontWeight:typography.regular,textOutline:"2px $white"}},innerSize:"50%",borderColor:"",borderWidth:null,colors:[colors.data_1,colors.data_2,colors.data_3,colors.data_4,colors.data_5,colors.data_6,colors.data_7]}},legend:{layout:"horizontal",align:"center",verticalAlign:"bottom",itemStyle:{fontFamily:typography.font_family_base,color:colors.text_lt_light,fontWeight:typography.regular,fontSize:typography.text_smaller},itemHoverStyle:{color:colors.text_lt_default},itemHiddenStyle:{color:colors.text_lt_lighter}},credits:{enabled:false}};const PbCircleChart=props=>{const{aria:aria={},className:className,data:data={},id:id,htmlOptions:htmlOptions={},options:options}=props;const ariaProps=buildAriaProps(aria);const dataProps=buildDataProps(data);const htmlProps=buildHtmlProps(htmlOptions);const classes=classnames(buildCss("pb_pb_circle_chart"),globalProps(props),className);const mergedOptions=useMemo((()=>{if(!options||typeof options!=="object"){console.error("❌ Invalid options passed to <PbCircleChart />",options);return{}}return Highcharts.merge({},pbCircleChartTheme,options)}),[options]);return jsx("div",{children:jsx(HighchartsReact,{containerProps:{className:classnames(classes),id:id,...ariaProps,...dataProps,...htmlProps},highcharts:Highcharts,options:mergedOptions})})};const pbGaugeGraphTheme={title:{text:"",style:{fontFamily:typography.font_family_base,fontSize:typography.text_larger}},chart:{type:"solidgauge",events:{render(){this.container;const arc=this.container.querySelector("path.gauge-pane");if(arc)arc.setAttribute("stroke-linejoin","round")}}},pane:{size:"90%",startAngle:-100,endAngle:100,background:[{borderWidth:20,innerRadius:"90%",outerRadius:"90%",shape:"arc",className:"gauge-pane",borderColor:colors.border_light,borderRadius:"50%"}]},tooltip:{backgroundColor:{linearGradient:{x1:0,y1:0,x2:0,y2:1},stops:[[0,colors.bg_dark],[1,colors.bg_dark]]},pointFormat:'<span style="font-weight: bold; color:{point.color};">●</span>{point.name}: <b>{point.y}</b>',followPointer:true,shadow:false,borderWidth:0,borderRadius:10,style:{fontFamily:typography.font_family_base,color:colors.text_dk_default,fontWeight:typography.regular,fontSize:typography.text_smaller}},yAxis:{min:0,max:100,lineWidth:0,tickPositions:[]},plotOptions:{solidgauge:{borderColor:colors.data_1,borderWidth:20,color:colors.data_1,radius:90,innerRadius:"90%",y:-26,dataLabels:{borderWidth:0,color:colors.text_lt_default,enabled:true,format:'<span class="fix">{y:,f}</span>',style:{fontFamily:typography.font_family_base,fontWeight:typography.regular,fontSize:typography.heading_2},y:-26}}},credits:{enabled:false}};const PbGaugeChart=props=>{const{aria:aria={},className:className,data:data={},htmlOptions:htmlOptions={},id:id,ref:ref,options:options={}}=props;const ariaProps=buildAriaProps(aria);const dataProps=buildDataProps(data);const htmlProps=buildHtmlProps(htmlOptions);const classes=classnames(buildCss("pb_pb_gauge_chart"),globalProps(props),className);const mergedOptions=useMemo((()=>{if(!options||typeof options!=="object"){console.error("❌ Invalid options passed to <PbLineGraph />",options);return{}}return Highcharts.merge({},pbGaugeGraphTheme,options)}),[options]);highchartsMore(Highcharts);solidGauge(Highcharts);return jsx("div",{children:jsx(HighchartsReact,{containerProps:{className:classnames(classes),id:id,ref:ref,...ariaProps,...dataProps,...htmlProps},highcharts:Highcharts,options:mergedOptions})})};const pbLineGraphTheme={title:{text:"",style:{color:colors.text_lt_default,fontFamily:typography.font_family_base,fontWeight:typography.bold,fontSize:typography.heading_3}},subtitle:{text:"",style:{fontFamily:typography.font_family_base,color:colors.text_lt_light,fontWeight:typography.regular,fontSize:typography.text_base}},chart:{type:"line"},tooltip:{backgroundColor:{linearGradient:{x1:0,y1:0,x2:0,y2:1},stops:[[0,colors.bg_dark],[1,colors.bg_dark]]},followPointer:true,shadow:false,borderWidth:0,borderRadius:10,style:{fontFamily:typography.font_family_base,color:colors.text_dk_default,fontWeight:typography.regular,fontSize:typography.text_smaller}},plotOptions:{line:{dataLabels:{enabled:false}}},credits:{enabled:false},legend:{enabled:false,itemStyle:{fontFamily:typography.font_family_base,color:colors.text_lt_light,fontWeight:typography.regular,fontSize:typography.text_smaller},itemHoverStyle:{color:colors.text_lt_default},itemHiddenStyle:{color:colors.text_lt_lighter}},colors:[colors.data_1,colors.data_2,colors.data_3,colors.data_4,colors.data_5,colors.data_6,colors.data_7],xAxis:{gridLineWidth:0,lineColor:colors.border_light,tickColor:colors.border_light,labels:{style:{fontFamily:typography.font_family_base,color:colors.text_lt_lighter,fontWeight:typography.bold,fontSize:typography.text_smaller}},title:{style:{color:colors.text_lt_default,fontFamily:typography.font_family_base,fontWeight:typography.regular,fontSize:typography.heading_4}}},yAxis:{alternateGridColor:void 0,minorTickInterval:null,gridLineColor:colors.border_light,minorGridLineColor:colors.border_light,lineWidth:0,tickWidth:0,tickPixelInterval:50,labels:{style:{fontFamily:typography.font_family_base,color:colors.text_lt_lighter,fontWeight:typography.bold,fontSize:typography.text_smaller}},title:{style:{fontFamily:typography.font_family_base,color:colors.text_lt_lighter,fontWeight:typography.bold,fontSize:typography.text_smaller}}}};const PbLineGraph=props=>{const{aria:aria={},className:className,data:data={},id:id,htmlOptions:htmlOptions={},options:options}=props;const ariaProps=buildAriaProps(aria);const dataProps=buildDataProps(data);const htmlProps=buildHtmlProps(htmlOptions);const classes=classnames(buildCss("pb_pb_line_graph"),globalProps(props),className);const mergedOptions=useMemo((()=>{if(!options||typeof options!=="object"){console.error("❌ Invalid options passed to <PbLineGraph />",options);return{}}return Highcharts.merge({},pbLineGraphTheme,options)}),[options]);return jsx("div",{children:jsx(HighchartsReact,{containerProps:{className:classnames(classes),id:id,...ariaProps,...dataProps,...htmlProps},highcharts:Highcharts,options:mergedOptions})})};export{PbBarGraph as P,PbCircleChart as a,PbGaugeChart as b,PbLineGraph as c};
|