playbook_ui 16.4.0.pre.alpha.PLAY2718containerfalseborderradiusresponsiveonly15164 → 16.4.0.pre.alpha.PLAY2846reactadvancedtablecalcheaderpinnedrows15356
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/Context/AdvancedTableContext.tsx +5 -2
- data/app/pb_kits/playbook/pb_advanced_table/SubKits/TableHeader.tsx +9 -11
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_table_props.html.erb +1 -31
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_table_props.jsx +0 -93
- data/app/pb_kits/playbook/pb_button/docs/_button_full_width_rails.md +19 -0
- data/app/pb_kits/playbook/pb_button/docs/_button_full_width_react.md +23 -0
- data/app/pb_kits/playbook/pb_circle_icon_button/_circle_icon_button.scss +5 -0
- data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +3 -1
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_placeholder.html.erb +109 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_placeholder.jsx +127 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_placeholder.md +1 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.rb +3 -0
- data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.test.jsx +27 -0
- data/app/pb_kits/playbook/pb_table/docs/_sections.yml +1 -0
- data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_external_filter_rails.html.erb +45 -0
- data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_external_filter_rails.md +39 -0
- data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_rails.md +2 -1
- data/app/pb_kits/playbook/pb_table/docs/example.yml +1 -0
- data/app/pb_kits/playbook/pb_table/table.html.erb +5 -2
- data/app/pb_kits/playbook/pb_table/table.rb +4 -0
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_highlight.jsx +20 -8
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_highlight.md +3 -0
- data/app/pb_kits/playbook/utilities/_hover.scss +6 -3
- data/dist/chunks/_typeahead-BNp_YiTh.js +1 -0
- data/dist/chunks/vendor.js +2 -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 +11 -4
- data/app/pb_kits/playbook/pb_button/docs/_button_full_width.md +0 -1
- data/dist/chunks/_typeahead-Bh0RF1X-.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: aec00cc0f01b4b8a03cdeb6e7ec2f557bc24f0faf27bc53e40eefaec41ce9f11
|
|
4
|
+
data.tar.gz: 2f66281990b349330568f3e150163ab5351326fa367c231b4e6055a5fa5d53fb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c28bc30a8a258ce3e8deb15673e26cf34ecc4d87c0cec6a30a811d3ecfa6a9ae06b4e171be3cfb979123275238203738260172f6eb6f95cf66e5f82af3a61a50
|
|
7
|
+
data.tar.gz: 4c1dd728c52fde23dcabc4979216465ad7e167ce628a90f2eb0af92e744078c2109d54a3cf341022cbd3296e15fe88cf71b486413e524696af5dc0161dfbbf11
|
|
@@ -33,7 +33,8 @@ export const AdvancedTableProvider = ({ children, ...props }: {
|
|
|
33
33
|
|
|
34
34
|
const measureHeights = useCallback(() => {
|
|
35
35
|
if (headerRef.current) {
|
|
36
|
-
const
|
|
36
|
+
const headerElement = headerRef.current as HTMLElement;
|
|
37
|
+
const headerRect = headerElement.getBoundingClientRect();
|
|
37
38
|
if (headerRect.height > 0) {
|
|
38
39
|
setHeaderHeight(headerRect.height);
|
|
39
40
|
}
|
|
@@ -67,9 +68,11 @@ export const AdvancedTableProvider = ({ children, ...props }: {
|
|
|
67
68
|
};
|
|
68
69
|
}, [measureHeights]);
|
|
69
70
|
|
|
71
|
+
const headerGroupCount = table?.getHeaderGroups()?.length ?? 0;
|
|
72
|
+
|
|
70
73
|
useEffect(() => {
|
|
71
74
|
measureHeights();
|
|
72
|
-
}, [table?.getRowModel().rows.length, measureHeights]);
|
|
75
|
+
}, [table?.getRowModel().rows.length, headerGroupCount, measureHeights]);
|
|
73
76
|
|
|
74
77
|
|
|
75
78
|
// Create a flattened data array that includes ALL components for virtualization
|
|
@@ -64,14 +64,12 @@ export const TableHeader = ({
|
|
|
64
64
|
);
|
|
65
65
|
|
|
66
66
|
const renderRegularTableHeader = () => (
|
|
67
|
-
<thead className={classes}
|
|
67
|
+
<thead className={classes}
|
|
68
68
|
id={id}
|
|
69
|
+
ref={headerRef}
|
|
69
70
|
>
|
|
70
|
-
{table.getHeaderGroups().map((headerGroup: HeaderGroup<GenericObject
|
|
71
|
-
<tr
|
|
72
|
-
key={`${headerGroup.id}-headerGroup`}
|
|
73
|
-
ref={index === 0 ? headerRef : null}
|
|
74
|
-
>
|
|
71
|
+
{table.getHeaderGroups().map((headerGroup: HeaderGroup<GenericObject>) => (
|
|
72
|
+
<tr key={`${headerGroup.id}-headerGroup`}>
|
|
75
73
|
{!hasAnySubRows && selectableRows && (
|
|
76
74
|
<th className={customCellClassnames}>
|
|
77
75
|
<Checkbox
|
|
@@ -105,16 +103,16 @@ export const TableHeader = ({
|
|
|
105
103
|
);
|
|
106
104
|
|
|
107
105
|
const renderVirtualizedTableHeader = () => (
|
|
108
|
-
<thead
|
|
109
|
-
className={classes}
|
|
106
|
+
<thead
|
|
107
|
+
className={classes}
|
|
110
108
|
data-virtualized="true"
|
|
111
109
|
id={id}
|
|
110
|
+
ref={headerRef}
|
|
112
111
|
>
|
|
113
|
-
{table.getHeaderGroups().map((headerGroup: HeaderGroup<GenericObject
|
|
114
|
-
<tr
|
|
112
|
+
{table.getHeaderGroups().map((headerGroup: HeaderGroup<GenericObject>) => (
|
|
113
|
+
<tr
|
|
115
114
|
className="virtualized-header-row-header"
|
|
116
115
|
key={`${headerGroup.id}-headerGroup-virtualized`}
|
|
117
|
-
ref={index === 0 ? headerRef : null}
|
|
118
116
|
>
|
|
119
117
|
{!hasAnySubRows && selectableRows && (
|
|
120
118
|
<th className={classnames(customCellClassnames, "virtualized-header-cell")}>
|
|
@@ -30,34 +30,4 @@
|
|
|
30
30
|
}
|
|
31
31
|
] %>
|
|
32
32
|
|
|
33
|
-
<%= pb_rails("
|
|
34
|
-
<%= pb_rails("advanced_table", props: { id: "table_props_table", table_data: @table_data, column_definitions: column_definitions, table_props: { vertical_border: true, container: false }}) %>
|
|
35
|
-
|
|
36
|
-
<%= pb_rails("caption", props: { text: "Advanced Table with just container: false" }) %>
|
|
37
|
-
<%= pb_rails("advanced_table", props: { id: "table_props_table_2", table_data: @table_data, column_definitions: column_definitions, table_props: { container: false }}) %>
|
|
38
|
-
|
|
39
|
-
<%= pb_rails("caption", props: { text: "Typical set up: Card and Flex around Filter, SectionSeparator, and AdvancedTable with container: false" }) %>
|
|
40
|
-
<%= pb_rails("card", props: { padding: "none", margin_bottom: "xl" }) do %>
|
|
41
|
-
<%= pb_rails("flex", props: { align: "stretch", orientation: "column", gap: "none" }) do %>
|
|
42
|
-
<%= pb_rails("filter", props: {
|
|
43
|
-
background: false,
|
|
44
|
-
max_height: (@table_data.to_a.size < 10) ? "50vh" : "none",
|
|
45
|
-
min_width: "xs",
|
|
46
|
-
popover_props: { width: "350px" },
|
|
47
|
-
id: "rails-table-props-filter-with-data",
|
|
48
|
-
template: "single",
|
|
49
|
-
results: @table_data.to_a.size,
|
|
50
|
-
}) do %>
|
|
51
|
-
<%= pb_form_with(scope: :example, method: :get, url: "", validate: true) do |form| %>
|
|
52
|
-
<%= form.number_field :new_leads_eq, props: { label: "New Leads" } %>
|
|
53
|
-
<%= form.text_field :conversion_rate_eq, props: { label: "Conversion Rate" } %>
|
|
54
|
-
<%= form.actions do |action| %>
|
|
55
|
-
<%= action.submit props: { text: "Filter" } %>
|
|
56
|
-
<%= action.button props: { type: "reset", text: "Defaults", variant: "secondary" } %>
|
|
57
|
-
<% end %>
|
|
58
|
-
<% end %>
|
|
59
|
-
<% end %>
|
|
60
|
-
<%= pb_rails("section_separator") %>
|
|
61
|
-
<%= pb_rails("advanced_table", props: { id: "rails_template_advanced_table", table_data: @table_data, column_definitions: column_definitions, table_props: { container: false } }) %>
|
|
62
|
-
<% end %>
|
|
63
|
-
<% end %>
|
|
33
|
+
<%= pb_rails("advanced_table", props: { id: "table_props_table", table_data: @table_data, column_definitions: column_definitions, table_props: { vertical_border: true, container: false }}) %>
|
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import React from "react"
|
|
2
2
|
import AdvancedTable from '../../pb_advanced_table/_advanced_table'
|
|
3
3
|
import MOCK_DATA from "./advanced_table_mock_data.json"
|
|
4
|
-
import Button from "../../pb_button/_button"
|
|
5
|
-
import Caption from "../../pb_caption/_caption"
|
|
6
|
-
import Card from "../../pb_card/_card"
|
|
7
|
-
import Filter from "../../pb_filter/_filter"
|
|
8
|
-
import Flex from "../../pb_flex/_flex"
|
|
9
|
-
import SectionSeparator from "../../pb_section_separator/_section_separator"
|
|
10
|
-
import TextInput from "../../pb_text_input/_text_input"
|
|
11
4
|
|
|
12
5
|
const AdvancedTableTableProps = (props) => {
|
|
13
6
|
const columnDefinitions = [
|
|
@@ -47,100 +40,14 @@ const AdvancedTableTableProps = (props) => {
|
|
|
47
40
|
verticalBorder: true
|
|
48
41
|
}
|
|
49
42
|
|
|
50
|
-
const filterPopoverProps = { width: "350px" }
|
|
51
|
-
|
|
52
|
-
const filterForm = (closePopover) => (
|
|
53
|
-
<form>
|
|
54
|
-
<TextInput
|
|
55
|
-
label="New Leads"
|
|
56
|
-
name="new_leads_eq"
|
|
57
|
-
placeholder="0"
|
|
58
|
-
type="number"
|
|
59
|
-
{...props}
|
|
60
|
-
/>
|
|
61
|
-
<TextInput
|
|
62
|
-
label="Conversion Rate"
|
|
63
|
-
name="conversion_rate_eq"
|
|
64
|
-
placeholder="e.g. 12%"
|
|
65
|
-
{...props}
|
|
66
|
-
/>
|
|
67
|
-
<Flex
|
|
68
|
-
spacing="between"
|
|
69
|
-
{...props}
|
|
70
|
-
>
|
|
71
|
-
<Button
|
|
72
|
-
onClick={closePopover}
|
|
73
|
-
text="Filter"
|
|
74
|
-
{...props}
|
|
75
|
-
/>
|
|
76
|
-
<Button
|
|
77
|
-
text="Defaults"
|
|
78
|
-
type="reset"
|
|
79
|
-
variant="secondary"
|
|
80
|
-
{...props}
|
|
81
|
-
/>
|
|
82
|
-
</Flex>
|
|
83
|
-
</form>
|
|
84
|
-
)
|
|
85
|
-
|
|
86
43
|
return (
|
|
87
44
|
<div>
|
|
88
|
-
<Caption
|
|
89
|
-
text="Default Advanced Table Table Props Doc Example"
|
|
90
|
-
/>
|
|
91
45
|
<AdvancedTable
|
|
92
46
|
columnDefinitions={columnDefinitions}
|
|
93
|
-
marginBottom="md"
|
|
94
47
|
tableData={MOCK_DATA}
|
|
95
48
|
tableProps={tableProps}
|
|
96
49
|
{...props}
|
|
97
50
|
/>
|
|
98
|
-
<Caption
|
|
99
|
-
text="Advanced Table with just container: false"
|
|
100
|
-
/>
|
|
101
|
-
<AdvancedTable
|
|
102
|
-
columnDefinitions={columnDefinitions}
|
|
103
|
-
marginBottom="md"
|
|
104
|
-
tableData={MOCK_DATA}
|
|
105
|
-
tableProps={{container: false}}
|
|
106
|
-
{...props}
|
|
107
|
-
/>
|
|
108
|
-
|
|
109
|
-
<Caption
|
|
110
|
-
text="Typical set up: Card and Flex around Filter, SectionSeparator, and AdvancedTable with container: false"
|
|
111
|
-
/>
|
|
112
|
-
<Card
|
|
113
|
-
marginBottom="xl"
|
|
114
|
-
padding="none"
|
|
115
|
-
{...props}
|
|
116
|
-
>
|
|
117
|
-
<Flex
|
|
118
|
-
align="stretch"
|
|
119
|
-
gap="none"
|
|
120
|
-
orientation="column"
|
|
121
|
-
{...props}
|
|
122
|
-
>
|
|
123
|
-
<Filter
|
|
124
|
-
background={false}
|
|
125
|
-
id="react-table-props-filter-with-data"
|
|
126
|
-
maxHeight={MOCK_DATA.length < 10 ? "50vh" : "none"}
|
|
127
|
-
minWidth="xs"
|
|
128
|
-
popoverProps={filterPopoverProps}
|
|
129
|
-
results={MOCK_DATA.length}
|
|
130
|
-
{...props}
|
|
131
|
-
>
|
|
132
|
-
{({ closePopover }) => filterForm(closePopover)}
|
|
133
|
-
</Filter>
|
|
134
|
-
<SectionSeparator {...props} />
|
|
135
|
-
<AdvancedTable
|
|
136
|
-
columnDefinitions={columnDefinitions}
|
|
137
|
-
id="react_template_advanced_table"
|
|
138
|
-
tableData={MOCK_DATA}
|
|
139
|
-
tableProps={{container: false}}
|
|
140
|
-
{...props}
|
|
141
|
-
/>
|
|
142
|
-
</Flex>
|
|
143
|
-
</Card>
|
|
144
51
|
</div>
|
|
145
52
|
)
|
|
146
53
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
This button is used many times for mobile or other things like cards and sidebars.
|
|
2
|
+
|
|
3
|
+
### Responsive `display` and `full_width`
|
|
4
|
+
|
|
5
|
+
`full_width` applies block styling that includes `display: flex` on the **same element** as the button. The **`display` global prop** also sets `display` (via utility classes, often with `!important`).
|
|
6
|
+
|
|
7
|
+
Putting **both** on one button means **two systems control `display` on one node**, which can cause wrong visibility (e.g. both a header and a full-width mobile button showing) or confusing cascade behavior.
|
|
8
|
+
|
|
9
|
+
**Recommended:** Put responsive `display` on a **parent** (e.g. `Flex`, `Card`, or a plain wrapper) and keep `full_width` only on the `Button` inside. The wrapper handles show/hide by breakpoint; the button only handles full-width layout.
|
|
10
|
+
|
|
11
|
+
```erb
|
|
12
|
+
<%= pb_rails("flex", props: {
|
|
13
|
+
display: { xs: "flex", default: "none" },
|
|
14
|
+
orientation: "column",
|
|
15
|
+
width: "100%",
|
|
16
|
+
}) do %>
|
|
17
|
+
<%= pb_rails("button", props: { full_width: true, text: "Add" }) %>
|
|
18
|
+
<% end %>
|
|
19
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
This button is used many times for mobile or other things like cards and sidebars.
|
|
2
|
+
|
|
3
|
+
### Responsive `display` and `full_width`
|
|
4
|
+
|
|
5
|
+
`full_width` applies block styling that includes `display: flex` on the **same element** as the button. The **`display` global prop** also sets `display` (via utility classes, often with `!important`).
|
|
6
|
+
|
|
7
|
+
Putting **both** on one button means **two systems control `display` on one node**, which can cause wrong visibility (e.g. both a header and a full-width mobile button showing) or confusing cascade behavior.
|
|
8
|
+
|
|
9
|
+
**Recommended:** Put responsive `display` on a **parent** (e.g. `Flex`, `Card`, or a plain wrapper) and keep `fullWidth` only on the `Button` inside. The wrapper handles show/hide by breakpoint; the button only handles full-width layout.
|
|
10
|
+
|
|
11
|
+
```jsx
|
|
12
|
+
import { Flex, Button } from "playbook-ui"
|
|
13
|
+
|
|
14
|
+
const Example = () => (
|
|
15
|
+
<Flex
|
|
16
|
+
display={{ xs: "flex", default: "none" }}
|
|
17
|
+
orientation="column"
|
|
18
|
+
width="100%"
|
|
19
|
+
>
|
|
20
|
+
<Button fullWidth text="Add" />
|
|
21
|
+
</Flex>
|
|
22
|
+
)
|
|
23
|
+
```
|
|
@@ -51,6 +51,7 @@ type MultiLevelSelectProps = {
|
|
|
51
51
|
treeData?: { [key: string]: string }[] | any;
|
|
52
52
|
onChange?: (event: { target: { name?: string; value: any } }) => void;
|
|
53
53
|
onSelect?: (prop: { [key: string]: any }) => void;
|
|
54
|
+
placeholder?: string;
|
|
54
55
|
selectedIds?: string[] | any;
|
|
55
56
|
variant?: "multi" | "single";
|
|
56
57
|
wrapped?: boolean;
|
|
@@ -100,6 +101,7 @@ const MultiLevelSelect = forwardRef<HTMLInputElement, MultiLevelSelectProps>(
|
|
|
100
101
|
treeData,
|
|
101
102
|
onChange = () => null,
|
|
102
103
|
onSelect = () => null,
|
|
104
|
+
placeholder: placeholderText = "Start typing...",
|
|
103
105
|
selectedIds,
|
|
104
106
|
variant = "multi",
|
|
105
107
|
wrapped,
|
|
@@ -675,7 +677,7 @@ const MultiLevelSelect = forwardRef<HTMLInputElement, MultiLevelSelectProps>(
|
|
|
675
677
|
? `${itemsSelectedLength()} ${
|
|
676
678
|
itemsSelectedLength() === 1 ? "item" : "items"
|
|
677
679
|
} selected`
|
|
678
|
-
:
|
|
680
|
+
: placeholderText
|
|
679
681
|
}
|
|
680
682
|
required={required}
|
|
681
683
|
value={singleSelectedItem.value || filterItem}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
<%
|
|
2
|
+
tree_base = [{
|
|
3
|
+
label: "Power Home Remodeling",
|
|
4
|
+
value: "powerHomeRemodeling",
|
|
5
|
+
id: "100",
|
|
6
|
+
expanded: true,
|
|
7
|
+
children: [
|
|
8
|
+
{
|
|
9
|
+
label: "People",
|
|
10
|
+
value: "people",
|
|
11
|
+
id: "101",
|
|
12
|
+
expanded: true,
|
|
13
|
+
children: [
|
|
14
|
+
{
|
|
15
|
+
label: "Talent Acquisition",
|
|
16
|
+
value: "talentAcquisition",
|
|
17
|
+
id: "102",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
label: "Business Affairs",
|
|
21
|
+
value: "business Affairs",
|
|
22
|
+
id: "103",
|
|
23
|
+
children: [
|
|
24
|
+
{
|
|
25
|
+
label: "Initiatives",
|
|
26
|
+
value: "initiatives",
|
|
27
|
+
id: "104",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
label: "Learning & Development",
|
|
31
|
+
value: "learningAndDevelopment",
|
|
32
|
+
id: "105",
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
label: "People Experience",
|
|
38
|
+
value: "peopleExperience",
|
|
39
|
+
id: "106",
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
label: "Contact Center",
|
|
45
|
+
value: "contactCenter",
|
|
46
|
+
id: "107",
|
|
47
|
+
children: [
|
|
48
|
+
{
|
|
49
|
+
label: "Appointment Management",
|
|
50
|
+
value: "appointmentManagement",
|
|
51
|
+
id: "108",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
label: "Customer Service",
|
|
55
|
+
value: "customerService",
|
|
56
|
+
id: "109",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
label: "Energy",
|
|
60
|
+
value: "energy",
|
|
61
|
+
id: "110",
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
}]
|
|
67
|
+
|
|
68
|
+
prefix_mls_ids = nil
|
|
69
|
+
prefix_mls_ids = ->(nodes, pfx) {
|
|
70
|
+
nodes.map do |n|
|
|
71
|
+
h = n.dup
|
|
72
|
+
h[:id] = "#{pfx}#{n[:id]}"
|
|
73
|
+
h[:children] = prefix_mls_ids.call(n[:children], pfx) if n[:children].present?
|
|
74
|
+
h
|
|
75
|
+
end
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
tree_multi = prefix_mls_ids.call(tree_base, "phm_")
|
|
79
|
+
tree_return_all = prefix_mls_ids.call(tree_base, "phr_")
|
|
80
|
+
tree_single = prefix_mls_ids.call(tree_base, "phs_")
|
|
81
|
+
%>
|
|
82
|
+
|
|
83
|
+
<%= pb_rails("multi_level_select", props: {
|
|
84
|
+
id: "multi-level-select-placeholder-multi-rails",
|
|
85
|
+
label: "Multi (default)",
|
|
86
|
+
margin_bottom: "sm",
|
|
87
|
+
name: "placeholder_multi",
|
|
88
|
+
tree_data: tree_multi,
|
|
89
|
+
placeholder: "Search or choose options…",
|
|
90
|
+
}) %>
|
|
91
|
+
|
|
92
|
+
<%= pb_rails("multi_level_select", props: {
|
|
93
|
+
id: "multi-level-select-placeholder-return-all-rails",
|
|
94
|
+
label: "Multi (return all selected)",
|
|
95
|
+
margin_bottom: "sm",
|
|
96
|
+
name: "placeholder_return_all",
|
|
97
|
+
placeholder: "Departments...",
|
|
98
|
+
return_all_selected: true,
|
|
99
|
+
tree_data: tree_return_all,
|
|
100
|
+
}) %>
|
|
101
|
+
|
|
102
|
+
<%= pb_rails("multi_level_select", props: {
|
|
103
|
+
id: "multi-level-select-placeholder-single-rails",
|
|
104
|
+
label: "Single",
|
|
105
|
+
name: "placeholder_single",
|
|
106
|
+
placeholder: "Select one option…",
|
|
107
|
+
tree_data: tree_single,
|
|
108
|
+
variant: "single",
|
|
109
|
+
}) %>
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import MultiLevelSelect from "../_multi_level_select";
|
|
4
|
+
|
|
5
|
+
const treeTemplate = [
|
|
6
|
+
{
|
|
7
|
+
label: "Power Home Remodeling",
|
|
8
|
+
value: "powerHomeRemodeling",
|
|
9
|
+
id: "powerhome1",
|
|
10
|
+
expanded: true,
|
|
11
|
+
children: [
|
|
12
|
+
{
|
|
13
|
+
label: "People",
|
|
14
|
+
value: "people",
|
|
15
|
+
id: "people1",
|
|
16
|
+
expanded: true,
|
|
17
|
+
children: [
|
|
18
|
+
{
|
|
19
|
+
label: "Talent Acquisition",
|
|
20
|
+
value: "talentAcquisition",
|
|
21
|
+
id: "talent1",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
label: "Business Affairs",
|
|
25
|
+
value: "businessAffairs",
|
|
26
|
+
id: "business1",
|
|
27
|
+
children: [
|
|
28
|
+
{
|
|
29
|
+
label: "Initiatives",
|
|
30
|
+
value: "initiatives",
|
|
31
|
+
id: "initiative1",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
label: "Learning & Development",
|
|
35
|
+
value: "learningAndDevelopment",
|
|
36
|
+
id: "development1",
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
label: "People Experience",
|
|
42
|
+
value: "peopleExperience",
|
|
43
|
+
id: "experience1",
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
label: "Contact Center",
|
|
49
|
+
value: "contactCenter",
|
|
50
|
+
id: "contact1",
|
|
51
|
+
children: [
|
|
52
|
+
{
|
|
53
|
+
label: "Appointment Management",
|
|
54
|
+
value: "appointmentManagement",
|
|
55
|
+
id: "appointment1",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
label: "Customer Service",
|
|
59
|
+
value: "customerService",
|
|
60
|
+
id: "customer1",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
label: "Energy",
|
|
64
|
+
value: "energy",
|
|
65
|
+
id: "energy1",
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
function prefixTreeIds(nodes, prefix) {
|
|
74
|
+
return nodes.map((node) => ({
|
|
75
|
+
...node,
|
|
76
|
+
id: `${prefix}${node.id}`,
|
|
77
|
+
children:
|
|
78
|
+
node.children && node.children.length > 0
|
|
79
|
+
? prefixTreeIds(node.children, prefix)
|
|
80
|
+
: node.children,
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const treeDataMulti = prefixTreeIds(treeTemplate, "phm_");
|
|
85
|
+
const treeDataReturnAll = prefixTreeIds(treeTemplate, "phr_");
|
|
86
|
+
const treeDataSingle = prefixTreeIds(treeTemplate, "phs_");
|
|
87
|
+
|
|
88
|
+
const MultiLevelSelectPlaceholder = () => (
|
|
89
|
+
<>
|
|
90
|
+
<MultiLevelSelect
|
|
91
|
+
id="multi-level-select-placeholder-multi"
|
|
92
|
+
label="Multi (default)"
|
|
93
|
+
marginBottom="sm"
|
|
94
|
+
name="placeholder_multi"
|
|
95
|
+
onSelect={(selectedNodes) =>
|
|
96
|
+
console.log("Multi — default", selectedNodes)
|
|
97
|
+
}
|
|
98
|
+
placeholder="Search or choose options…"
|
|
99
|
+
treeData={treeDataMulti}
|
|
100
|
+
/>
|
|
101
|
+
<MultiLevelSelect
|
|
102
|
+
id="multi-level-select-placeholder-return-all"
|
|
103
|
+
label="Multi (return all selected)"
|
|
104
|
+
marginBottom="sm"
|
|
105
|
+
name="placeholder_return_all"
|
|
106
|
+
onSelect={(selectedNodes) =>
|
|
107
|
+
console.log("Multi — return all selected", selectedNodes)
|
|
108
|
+
}
|
|
109
|
+
placeholder="Departments..."
|
|
110
|
+
returnAllSelected
|
|
111
|
+
treeData={treeDataReturnAll}
|
|
112
|
+
/>
|
|
113
|
+
<MultiLevelSelect
|
|
114
|
+
id="multi-level-select-placeholder-single"
|
|
115
|
+
label="Single"
|
|
116
|
+
name="placeholder_single"
|
|
117
|
+
onSelect={(selectedNodes) =>
|
|
118
|
+
console.log("Single", selectedNodes)
|
|
119
|
+
}
|
|
120
|
+
placeholder="Select one option…"
|
|
121
|
+
treeData={treeDataSingle}
|
|
122
|
+
variant="single"
|
|
123
|
+
/>
|
|
124
|
+
</>
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
export default MultiLevelSelectPlaceholder;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Use the `placeholder` prop to customize the initial text shown in the input when nothing is selected. The default is `Start typing...`.
|
|
@@ -18,6 +18,7 @@ examples:
|
|
|
18
18
|
- multi_level_select_disabled_options_parent: Disabled Parent Option (Return All Selected)
|
|
19
19
|
- multi_level_select_single_disabled: Disabled Options (Single Select)
|
|
20
20
|
- multi_level_select_required_indicator: Required Indicator
|
|
21
|
+
- multi_level_select_placeholder: Placeholder
|
|
21
22
|
|
|
22
23
|
react:
|
|
23
24
|
- multi_level_select_default: Default
|
|
@@ -40,3 +41,4 @@ examples:
|
|
|
40
41
|
- multi_level_select_single_disabled: Disabled Options (Single Select)
|
|
41
42
|
- multi_level_select_required_indicator: Required Indicator
|
|
42
43
|
- multi_level_select_react_reset_key: Reset with Key (React)
|
|
44
|
+
- multi_level_select_placeholder: Placeholder
|
|
@@ -18,3 +18,4 @@ export { default as MultiLevelSelectLabel } from "./_multi_level_select_label.js
|
|
|
18
18
|
export { default as MultiLevelSelectSingleDisabled } from "./_multi_level_select_single_disabled.jsx"
|
|
19
19
|
export { default as MultiLevelSelectRequiredIndicator } from "./_multi_level_select_required_indicator.jsx"
|
|
20
20
|
export { default as MultiLevelSelectReactResetKey } from "./_multi_level_select_react_reset_key.jsx"
|
|
21
|
+
export { default as MultiLevelSelectPlaceholder } from "./_multi_level_select_placeholder.jsx"
|
|
@@ -32,6 +32,8 @@ module Playbook
|
|
|
32
32
|
default: ""
|
|
33
33
|
prop :label, type: Playbook::Props::String,
|
|
34
34
|
default: ""
|
|
35
|
+
prop :placeholder, type: Playbook::Props::String,
|
|
36
|
+
default: "Start typing..."
|
|
35
37
|
prop :required_indicator, type: Playbook::Props::Boolean,
|
|
36
38
|
default: false
|
|
37
39
|
prop :show_checked_children, type: Playbook::Props::Boolean,
|
|
@@ -50,6 +52,7 @@ module Playbook
|
|
|
50
52
|
inputDisplay: input_display,
|
|
51
53
|
name: name,
|
|
52
54
|
label: label,
|
|
55
|
+
placeholder: placeholder,
|
|
53
56
|
treeData: tree_data,
|
|
54
57
|
required: required,
|
|
55
58
|
requiredIndicator: required_indicator,
|
|
@@ -173,6 +173,33 @@ describe('MultiLevelSelect', () => {
|
|
|
173
173
|
expect(label).toHaveTextContent("Select Location")
|
|
174
174
|
expect(label).not.toHaveTextContent("*")
|
|
175
175
|
})
|
|
176
|
+
|
|
177
|
+
test('should use default placeholder when none is passed', () => {
|
|
178
|
+
render(
|
|
179
|
+
<MultiLevelSelect
|
|
180
|
+
data={{ testid: testId }}
|
|
181
|
+
id="mls-placeholder-default"
|
|
182
|
+
treeData={treeData}
|
|
183
|
+
/>
|
|
184
|
+
)
|
|
185
|
+
expect(
|
|
186
|
+
screen.getByPlaceholderText('Start typing...')
|
|
187
|
+
).toBeInTheDocument()
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
test('should use custom placeholder when passed', () => {
|
|
191
|
+
render(
|
|
192
|
+
<MultiLevelSelect
|
|
193
|
+
data={{ testid: testId }}
|
|
194
|
+
id="mls-placeholder-custom"
|
|
195
|
+
placeholder="Choose items…"
|
|
196
|
+
treeData={treeData}
|
|
197
|
+
/>
|
|
198
|
+
)
|
|
199
|
+
expect(
|
|
200
|
+
screen.getByPlaceholderText('Choose items…')
|
|
201
|
+
).toBeInTheDocument()
|
|
202
|
+
})
|
|
176
203
|
})
|
|
177
204
|
|
|
178
205
|
describe('MultiLevelSelect multi variant', () => {
|
data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_external_filter_rails.html.erb
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<%# External filter: capture any filter markup and pass it via the filter prop.
|
|
2
|
+
Use your own helper (e.g. a search/filter form) or pb_rails("filter") as shown here. %>
|
|
3
|
+
<% users = [
|
|
4
|
+
{ name: "Alex", role: "Engineer" },
|
|
5
|
+
{ name: "Sam", role: "Designer" },
|
|
6
|
+
{ name: "Jordan", role: "Manager" },
|
|
7
|
+
] %>
|
|
8
|
+
|
|
9
|
+
<% filter_output = capture do %>
|
|
10
|
+
<%= pb_rails("filter", props: {
|
|
11
|
+
id: "external-filter-demo",
|
|
12
|
+
template: "single",
|
|
13
|
+
results: 3,
|
|
14
|
+
background: false,
|
|
15
|
+
sort_menu: [
|
|
16
|
+
{ item: "Name", link: "#", active: true, direction: "asc" },
|
|
17
|
+
{ item: "Role", link: "#", active: false },
|
|
18
|
+
],
|
|
19
|
+
}) do %>
|
|
20
|
+
<%= pb_rails("text_input", props: { label: "Name", placeholder: "Search by name" }) %>
|
|
21
|
+
<%= pb_rails("text_input", props: { label: "Role", placeholder: "e.g. Engineer, Designer" }) %>
|
|
22
|
+
<%= pb_rails("button", props: { text: "Apply" }) %>
|
|
23
|
+
<% end %>
|
|
24
|
+
<% end %>
|
|
25
|
+
|
|
26
|
+
<%= pb_rails("table", props: {
|
|
27
|
+
variant: "with_filter",
|
|
28
|
+
title: "Table with External Filter",
|
|
29
|
+
filter: filter_output,
|
|
30
|
+
}) do %>
|
|
31
|
+
<%= pb_rails("table/table_head") do %>
|
|
32
|
+
<%= pb_rails("table/table_row") do %>
|
|
33
|
+
<%= pb_rails("table/table_header", props: { text: "Name" }) %>
|
|
34
|
+
<%= pb_rails("table/table_header", props: { text: "Role" }) %>
|
|
35
|
+
<% end %>
|
|
36
|
+
<% end %>
|
|
37
|
+
<%= pb_rails("table/table_body") do %>
|
|
38
|
+
<% users.each do |user| %>
|
|
39
|
+
<%= pb_rails("table/table_row") do %>
|
|
40
|
+
<%= pb_rails("table/table_cell") { user[:name] } %>
|
|
41
|
+
<%= pb_rails("table/table_cell") { user[:role] } %>
|
|
42
|
+
<% end %>
|
|
43
|
+
<% end %>
|
|
44
|
+
<% end %>
|
|
45
|
+
<% end %>
|