playbook_ui 12.5.0 → 12.6.0.pre.alpha.collapsible1
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/_playbook.scss +2 -0
- data/app/pb_kits/playbook/data/menu.yml +2 -1
- data/app/pb_kits/playbook/index.js +1 -0
- data/app/pb_kits/playbook/pb_button/_button_mixins.scss +2 -2
- data/app/pb_kits/playbook/pb_circle_icon_button/_circle_icon_button.scss +1 -1
- data/app/pb_kits/playbook/pb_collapsible/_collapsible.tsx +5 -1
- data/app/pb_kits/playbook/pb_collapsible/child_kits/CollapsibleMain.tsx +10 -4
- data/app/pb_kits/playbook/pb_collapsible/collapsible_main.html.erb +2 -2
- data/app/pb_kits/playbook/pb_collapsible/collapsible_main.rb +7 -0
- data/app/pb_kits/playbook/pb_collapsible/docs/_collapsible_main_flex.html.erb +10 -0
- data/app/pb_kits/playbook/pb_collapsible/docs/_collapsible_main_flex.jsx +23 -0
- data/app/pb_kits/playbook/pb_collapsible/docs/_collapsible_main_flex.md +1 -0
- data/app/pb_kits/playbook/pb_collapsible/docs/_collapsible_main_flex_item.html.erb +23 -0
- data/app/pb_kits/playbook/pb_collapsible/docs/_collapsible_main_flex_item.jsx +48 -0
- data/app/pb_kits/playbook/pb_collapsible/docs/_collapsible_main_flex_item.md +1 -0
- data/app/pb_kits/playbook/pb_collapsible/docs/example.yml +6 -1
- data/app/pb_kits/playbook/pb_collapsible/docs/index.js +2 -0
- data/app/pb_kits/playbook/pb_filter/Filter/CurrentFilters.tsx +72 -0
- data/app/pb_kits/playbook/pb_filter/Filter/{FilterBackground.jsx → FilterBackground.tsx} +12 -14
- data/app/pb_kits/playbook/pb_filter/Filter/{FilterDouble.jsx → FilterDouble.tsx} +7 -8
- data/app/pb_kits/playbook/pb_filter/Filter/{FilterSingle.jsx → FilterSingle.tsx} +25 -25
- data/app/pb_kits/playbook/pb_filter/Filter/{FiltersPopover.jsx → FiltersPopover.tsx} +13 -11
- data/app/pb_kits/playbook/pb_filter/Filter/{ResultsCount.jsx → ResultsCount.tsx} +39 -14
- data/app/pb_kits/playbook/pb_filter/Filter/{SortMenu.jsx → SortMenu.tsx} +6 -6
- data/app/pb_kits/playbook/pb_filter/Filter/{index.jsx → index.tsx} +17 -10
- data/app/pb_kits/playbook/pb_filter/{_filter.jsx → _filter.tsx} +0 -2
- data/app/pb_kits/playbook/pb_list/_list.tsx +2 -2
- data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.scss +86 -0
- data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +81 -0
- data/app/pb_kits/playbook/pb_multi_level_select/_multi_select_helper.tsx +30 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_default.jsx +86 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_default.md +3 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +6 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_multi_level_select/helper_functions.ts +60 -0
- data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.test.jsx +40 -0
- data/app/pb_kits/playbook/pb_person_contact/{_person_contact.jsx → _person_contact.tsx} +19 -22
- data/app/pb_kits/playbook/pb_person_contact/person_contact.test.js +112 -0
- data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +61 -47
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_initial_country.html.erb +3 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_only_countries.html.erb +4 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_preferred_countries.html.erb +4 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/example.yml +4 -1
- data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.html.erb +1 -15
- data/app/pb_kits/playbook/pb_popover/_popover.tsx +33 -32
- data/app/pb_kits/playbook/playbook-doc.js +2 -0
- data/app/pb_kits/playbook/tokens/_animation-curves.scss +30 -30
- data/app/pb_kits/playbook/tokens/_border_radius.scss +15 -16
- data/app/pb_kits/playbook/tokens/_colors.scss +3 -1
- data/app/pb_kits/playbook/tokens/_display.scss +6 -6
- data/app/pb_kits/playbook/tokens/_line_height.scss +7 -7
- data/app/pb_kits/playbook/tokens/_opacity.scss +10 -10
- data/app/pb_kits/playbook/tokens/_positioning.scss +11 -11
- data/app/pb_kits/playbook/tokens/_screen_sizes.scss +10 -10
- data/app/pb_kits/playbook/tokens/_shadows.scss +4 -4
- data/app/pb_kits/playbook/tokens/_spacing.scss +6 -6
- data/app/pb_kits/playbook/tokens/_transition.scss +3 -3
- data/app/pb_kits/playbook/tokens/_typography.scss +35 -46
- data/lib/playbook/version.rb +2 -2
- metadata +33 -14
- data/app/pb_kits/playbook/pb_filter/Filter/CurrentFilters.jsx +0 -76
@@ -1,5 +1,3 @@
|
|
1
|
-
/* @flow */
|
2
|
-
|
3
1
|
import React from 'react'
|
4
2
|
|
5
3
|
import Caption from '../../pb_caption/_caption'
|
@@ -9,31 +7,58 @@ const resultsText = (results: number): string => results == 1 ? 'Result' : 'Resu
|
|
9
7
|
|
10
8
|
type ResultsCountProps = {
|
11
9
|
dark?: boolean,
|
12
|
-
results?:
|
10
|
+
results?: number | null,
|
13
11
|
title?: boolean,
|
14
12
|
}
|
15
|
-
const ResultsCount = ({ dark, results, title }: ResultsCountProps) =>
|
16
|
-
|
17
|
-
|
13
|
+
const ResultsCount = ({ dark, results, title }: ResultsCountProps): React.ReactElement => {
|
14
|
+
|
15
|
+
const resultTitle = () => {
|
16
|
+
return (
|
18
17
|
<TitleCount
|
19
18
|
align="center"
|
20
19
|
count={results}
|
21
20
|
dark={dark}
|
22
21
|
title={`${resultsText(results)}:`}
|
23
22
|
/>
|
24
|
-
|
25
|
-
|
23
|
+
)
|
24
|
+
}
|
25
|
+
|
26
|
+
const justResults = () => {
|
27
|
+
return (
|
26
28
|
<Caption
|
27
29
|
className="filter-results"
|
28
30
|
dark={dark}
|
29
31
|
size="xs"
|
30
32
|
text={`${results} ${resultsText(results)}`}
|
31
33
|
/>
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
)
|
35
|
+
}
|
36
|
+
|
37
|
+
const displayResultsCount = () => {
|
38
|
+
if (results && title) {
|
39
|
+
return (
|
40
|
+
<>
|
41
|
+
{resultTitle()}
|
42
|
+
</>
|
43
|
+
)
|
44
|
+
} else if (results) {
|
45
|
+
return (
|
46
|
+
<>
|
47
|
+
{justResults()}
|
48
|
+
</>
|
49
|
+
)
|
50
|
+
} else {
|
51
|
+
return (
|
52
|
+
<div />
|
53
|
+
)
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
return (
|
58
|
+
<>
|
59
|
+
{displayResultsCount()}
|
60
|
+
</>
|
61
|
+
)
|
62
|
+
}
|
38
63
|
|
39
64
|
export default ResultsCount
|
@@ -1,5 +1,3 @@
|
|
1
|
-
/* @flow */
|
2
|
-
|
3
1
|
import React, { useState } from 'react'
|
4
2
|
import { find, map, partial } from 'lodash'
|
5
3
|
|
@@ -12,9 +10,9 @@ import PbReactPopover from '../../pb_popover/_popover'
|
|
12
10
|
export type Direction = 'asc' | 'desc'
|
13
11
|
export type SortOptions = { [name: string]: string }
|
14
12
|
export type SortValue = { name: string, dir: Direction }
|
15
|
-
export type SortingChangeCallback = (SortValue[]) => void
|
13
|
+
export type SortingChangeCallback = (value: SortValue[]) => void
|
16
14
|
|
17
|
-
const nextValue = (value: SortValue[], name: string):
|
15
|
+
const nextValue = (value: SortValue[], name: string): SortValue => {
|
18
16
|
const current = find(value, { name })
|
19
17
|
return {
|
20
18
|
name,
|
@@ -26,12 +24,13 @@ const directionIcon = (dir: Direction) => (
|
|
26
24
|
dir == 'asc' ? 'sort-amount-up' : 'sort-amount-down'
|
27
25
|
)
|
28
26
|
|
29
|
-
const renderOptions = (options: SortOptions, value: SortValue[], handleChange: (SortValue) => void) => (
|
27
|
+
const renderOptions = (options: SortOptions, value: SortValue[], handleChange: (arg0: SortValue) => void) => (
|
30
28
|
map(options, (label, name) => {
|
31
29
|
const next = nextValue(value, name)
|
32
30
|
return (
|
33
31
|
<ListItem key={`option-${next.name}-${next.dir}`}>
|
34
32
|
<Button
|
33
|
+
htmlType={undefined}
|
35
34
|
onClick={partial(handleChange, next)}
|
36
35
|
text={` ${label}`}
|
37
36
|
variant="link"
|
@@ -47,7 +46,7 @@ export type SortMenuProps = {
|
|
47
46
|
options: SortOptions,
|
48
47
|
value?: SortValue[],
|
49
48
|
}
|
50
|
-
const SortMenu = ({ dark, options, value, onChange }: SortMenuProps) => {
|
49
|
+
const SortMenu = ({ dark, options, value, onChange }: SortMenuProps): React.ReactElement => {
|
51
50
|
const [hide, updateHide] = useState(true)
|
52
51
|
const toggle = () => updateHide(!hide)
|
53
52
|
const handleChange = (value: SortValue) => {
|
@@ -58,6 +57,7 @@ const SortMenu = ({ dark, options, value, onChange }: SortMenuProps) => {
|
|
58
57
|
const sortButton = (
|
59
58
|
<Button
|
60
59
|
dark={dark}
|
60
|
+
htmlType={undefined}
|
61
61
|
id="sort-button"
|
62
62
|
onClick={toggle}
|
63
63
|
paddingRight="none"
|
@@ -1,5 +1,3 @@
|
|
1
|
-
/* @flow */
|
2
|
-
|
3
1
|
import React from 'react'
|
4
2
|
import FilterSingle, { FilterSingleProps } from './FilterSingle'
|
5
3
|
import FilterDouble, { FilterDoubleProps } from './FilterDouble'
|
@@ -10,16 +8,25 @@ type FilterProps =
|
|
10
8
|
double?: boolean,
|
11
9
|
})
|
12
10
|
|
13
|
-
const Filter = ({
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
const Filter = ({
|
12
|
+
double = false,
|
13
|
+
...templateProps
|
14
|
+
}: FilterProps): React.ReactElement => {
|
15
|
+
const displayFilter = () => {
|
16
|
+
if (double === true) {
|
17
|
+
return (
|
17
18
|
<FilterDouble {...templateProps} />
|
18
|
-
|
19
|
-
|
19
|
+
)
|
20
|
+
} else {
|
21
|
+
return (
|
20
22
|
<FilterSingle {...templateProps} />
|
21
|
-
|
22
|
-
|
23
|
+
)
|
24
|
+
}
|
25
|
+
}
|
26
|
+
return (
|
27
|
+
<>
|
28
|
+
{displayFilter()}
|
29
|
+
</>
|
23
30
|
)
|
24
31
|
}
|
25
32
|
|
@@ -11,14 +11,14 @@ type ListProps = {
|
|
11
11
|
dark?: boolean;
|
12
12
|
data?: object;
|
13
13
|
id?: string;
|
14
|
-
layout
|
14
|
+
layout?: "" | "left" | "right";
|
15
15
|
ordered?: boolean;
|
16
16
|
role?: string;
|
17
17
|
tabIndex?: number;
|
18
18
|
text?: string;
|
19
19
|
size?: string;
|
20
20
|
variant?: string;
|
21
|
-
xpadding
|
21
|
+
xpadding?: boolean;
|
22
22
|
};
|
23
23
|
|
24
24
|
const List = (props: ListProps) => {
|
@@ -0,0 +1,86 @@
|
|
1
|
+
@import "../pb_textarea/textarea_mixin";
|
2
|
+
@import "../tokens/colors";
|
3
|
+
@import "../tokens/border_radius";
|
4
|
+
@import "../tokens/shadows";
|
5
|
+
@import "../tokens/spacing";
|
6
|
+
|
7
|
+
.pb_multi_level_select {
|
8
|
+
.dropdown {
|
9
|
+
width: 100%;
|
10
|
+
.search {
|
11
|
+
border-bottom: none;
|
12
|
+
}
|
13
|
+
|
14
|
+
.toggle.expanded:after {
|
15
|
+
border-style: solid;
|
16
|
+
border-width: 0.1em 0.1em 0 0;
|
17
|
+
content: "";
|
18
|
+
display: inline-block;
|
19
|
+
height: 0.6em;
|
20
|
+
left: 0.1em;
|
21
|
+
position: relative;
|
22
|
+
vertical-align: top;
|
23
|
+
width: 0.6em;
|
24
|
+
top: 10px;
|
25
|
+
transform: rotate(135deg);
|
26
|
+
font-size: 0.5em;
|
27
|
+
}
|
28
|
+
.toggle.collapsed:after {
|
29
|
+
border-style: solid;
|
30
|
+
border-width: 0.1em 0.1em 0 0;
|
31
|
+
content: "";
|
32
|
+
display: inline-block;
|
33
|
+
height: 0.6em;
|
34
|
+
left: 0.1em;
|
35
|
+
position: relative;
|
36
|
+
vertical-align: top;
|
37
|
+
width: 0.6em;
|
38
|
+
top: 10px;
|
39
|
+
transform: rotate(135deg);
|
40
|
+
font-size: 0.5em;
|
41
|
+
|
42
|
+
transform: rotate(-45deg);
|
43
|
+
top: 12px;
|
44
|
+
}
|
45
|
+
|
46
|
+
.tag {
|
47
|
+
height: 37px;
|
48
|
+
display: inline-flex;
|
49
|
+
justify-content: center;
|
50
|
+
align-items: center;
|
51
|
+
padding: 0 $space_xxs 0 $space_xs;
|
52
|
+
border-radius: 18px;
|
53
|
+
background-color: rgba($primary, 0.1);
|
54
|
+
color: $primary;
|
55
|
+
border: none;
|
56
|
+
font-size: 16px;
|
57
|
+
font-weight: 700;
|
58
|
+
|
59
|
+
.tag-remove {
|
60
|
+
color: $primary;
|
61
|
+
padding-left: $space_xs;
|
62
|
+
padding-right: $space_xxs;
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
66
|
+
.react-dropdown-tree-select .dropdown .dropdown-trigger {
|
67
|
+
width: 100%;
|
68
|
+
@include pb_textarea_light;
|
69
|
+
}
|
70
|
+
.react-dropdown-tree-select .dropdown .dropdown-trigger.arrow.bottom:after {
|
71
|
+
content: none;
|
72
|
+
}
|
73
|
+
.react-dropdown-tree-select .dropdown .dropdown-trigger.arrow.top:after {
|
74
|
+
content: none;
|
75
|
+
}
|
76
|
+
|
77
|
+
.react-dropdown-tree-select .dropdown .dropdown-content {
|
78
|
+
width: 100%;
|
79
|
+
border: 1px solid $border_light;
|
80
|
+
border-radius: $border_rad_heavier;
|
81
|
+
box-shadow: $shadow_deeper;
|
82
|
+
}
|
83
|
+
.node.leaf.collapsed {
|
84
|
+
display: block !important;
|
85
|
+
}
|
86
|
+
}
|
@@ -0,0 +1,81 @@
|
|
1
|
+
import React, { useState, useEffect } from "react";
|
2
|
+
import classnames from "classnames";
|
3
|
+
import { buildAriaProps, buildCss, buildDataProps } from "../utilities/props";
|
4
|
+
import { globalProps } from "../utilities/globalProps";
|
5
|
+
import { findItemById, checkIt, unCheckIt } from "./helper_functions";
|
6
|
+
import MultiSelectHelper from "./_multi_select_helper";
|
7
|
+
|
8
|
+
type MultiLevelSelectProps = {
|
9
|
+
aria?: { [key: string]: string };
|
10
|
+
className?: string;
|
11
|
+
data?: { [key: string]: string };
|
12
|
+
id?: string;
|
13
|
+
treeData?: { [key: string]: string }[];
|
14
|
+
onChange?: any;
|
15
|
+
onSelect?: (SelectedNodes: { [key: string]: any }) => void;
|
16
|
+
};
|
17
|
+
|
18
|
+
const MultiLevelSelect = (props: MultiLevelSelectProps) => {
|
19
|
+
const { aria = {}, className, data = {}, id, treeData, onSelect } = props;
|
20
|
+
|
21
|
+
const ariaProps = buildAriaProps(aria);
|
22
|
+
const dataProps = buildDataProps(data);
|
23
|
+
const classes = classnames(
|
24
|
+
buildCss("pb_multi_level_select"),
|
25
|
+
globalProps(props),
|
26
|
+
className
|
27
|
+
);
|
28
|
+
|
29
|
+
const [formattedData, setFormattedData] = useState(treeData);
|
30
|
+
const [selectedItems, setSelectedItems] = useState([]);
|
31
|
+
|
32
|
+
const onChange = (currentNode: { [key: string]: any }) => {
|
33
|
+
const updatedData = formattedData.map((item: any) => {
|
34
|
+
if (item.id === currentNode._id) {
|
35
|
+
if (currentNode.checked) {
|
36
|
+
checkIt(item, selectedItems, setSelectedItems);
|
37
|
+
} else {
|
38
|
+
unCheckIt(item, selectedItems, setSelectedItems);
|
39
|
+
}
|
40
|
+
} else if (item.children) {
|
41
|
+
const foundItem = findItemById(item.children, currentNode._id);
|
42
|
+
if (foundItem) {
|
43
|
+
if (currentNode.checked) {
|
44
|
+
checkIt(foundItem, selectedItems, setSelectedItems);
|
45
|
+
} else {
|
46
|
+
unCheckIt(foundItem, selectedItems, setSelectedItems);
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
return item;
|
52
|
+
});
|
53
|
+
|
54
|
+
setFormattedData(updatedData);
|
55
|
+
};
|
56
|
+
|
57
|
+
useEffect(() => {
|
58
|
+
const selected = selectedItems.filter(
|
59
|
+
(item: { [key: string]: any }) => item.checked
|
60
|
+
);
|
61
|
+
//filter to remove duplicate items
|
62
|
+
const uniqueSelected = selected.filter(
|
63
|
+
(obj, index, self) => index === self.findIndex((t) => t.id === obj.id)
|
64
|
+
);
|
65
|
+
onSelect(uniqueSelected);
|
66
|
+
}, [selectedItems]);
|
67
|
+
|
68
|
+
return (
|
69
|
+
<div {...ariaProps} {...dataProps} className={classes} id={id}>
|
70
|
+
<MultiSelectHelper
|
71
|
+
treeData={formattedData}
|
72
|
+
id={id}
|
73
|
+
onChange={onChange}
|
74
|
+
onSelect={onSelect}
|
75
|
+
{...props}
|
76
|
+
/>
|
77
|
+
</div>
|
78
|
+
);
|
79
|
+
};
|
80
|
+
|
81
|
+
export default MultiLevelSelect;
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import React from "react"
|
2
|
+
import DropdownTreeSelect from "react-dropdown-tree-select"
|
3
|
+
import "react-dropdown-tree-select/dist/styles.css"
|
4
|
+
|
5
|
+
type HelperProps = {
|
6
|
+
id?: string
|
7
|
+
treeData?: { [key: string]: string }[]
|
8
|
+
onChange?: () => {}
|
9
|
+
|
10
|
+
}
|
11
|
+
|
12
|
+
const MultiSelectHelper = (props: HelperProps) => {
|
13
|
+
const { id, treeData, onChange } = props
|
14
|
+
|
15
|
+
|
16
|
+
return (
|
17
|
+
<DropdownTreeSelect
|
18
|
+
data={treeData}
|
19
|
+
id={id}
|
20
|
+
keepOpenOnSelect
|
21
|
+
keepTreeOnSearch
|
22
|
+
keepChildrenOnSearch
|
23
|
+
onChange={onChange}
|
24
|
+
texts={{ placeholder: "Select..." }}
|
25
|
+
mode='hierarchical'
|
26
|
+
/>
|
27
|
+
)
|
28
|
+
}
|
29
|
+
|
30
|
+
export default MultiSelectHelper
|
@@ -0,0 +1,86 @@
|
|
1
|
+
import React from "react";
|
2
|
+
import MultiLevelSelect from "../_multi_level_select";
|
3
|
+
|
4
|
+
const treeData = [
|
5
|
+
{
|
6
|
+
label: "Power Home Remodeling",
|
7
|
+
value: "Power Home Remodeling",
|
8
|
+
id: "powerhome1",
|
9
|
+
expanded: true,
|
10
|
+
children: [
|
11
|
+
{
|
12
|
+
label: "People",
|
13
|
+
value: "People",
|
14
|
+
id: "people1",
|
15
|
+
children: [
|
16
|
+
{
|
17
|
+
label: "Talent Acquisition",
|
18
|
+
value: "Talent Acquisition",
|
19
|
+
id: "talent1",
|
20
|
+
},
|
21
|
+
{
|
22
|
+
label: "Business Affairs",
|
23
|
+
value: "Business Affairs",
|
24
|
+
id: "business1",
|
25
|
+
children: [
|
26
|
+
{
|
27
|
+
label: "Initiatives",
|
28
|
+
value: "Initiatives",
|
29
|
+
id: "initiative1",
|
30
|
+
},
|
31
|
+
{
|
32
|
+
label: "Learning & Development",
|
33
|
+
value: "Learning & Development",
|
34
|
+
id: "development1",
|
35
|
+
},
|
36
|
+
],
|
37
|
+
},
|
38
|
+
{
|
39
|
+
label: "People Experience",
|
40
|
+
value: "People Experience",
|
41
|
+
id: "experience1",
|
42
|
+
},
|
43
|
+
],
|
44
|
+
},
|
45
|
+
{
|
46
|
+
label: "Contact Center",
|
47
|
+
value: "Contact Center",
|
48
|
+
id: "contact1",
|
49
|
+
children: [
|
50
|
+
{
|
51
|
+
label: "Appointment Management",
|
52
|
+
value: "Appointment Management",
|
53
|
+
id: "appointment1",
|
54
|
+
},
|
55
|
+
{
|
56
|
+
label: "Customer Service",
|
57
|
+
value: "Customer Service",
|
58
|
+
id: "customer1",
|
59
|
+
},
|
60
|
+
{
|
61
|
+
label: "Energy",
|
62
|
+
value: "Energy",
|
63
|
+
id: "energy1",
|
64
|
+
},
|
65
|
+
],
|
66
|
+
},
|
67
|
+
],
|
68
|
+
},
|
69
|
+
];
|
70
|
+
|
71
|
+
const MultiLevelSelectDefault = (props) => {
|
72
|
+
return (
|
73
|
+
<div>
|
74
|
+
<MultiLevelSelect
|
75
|
+
id="multiselect-default"
|
76
|
+
onSelect={(selectedNodes) =>
|
77
|
+
console.log("Selected Items", selectedNodes)
|
78
|
+
}
|
79
|
+
treeData={treeData}
|
80
|
+
{...props}
|
81
|
+
/>
|
82
|
+
</div>
|
83
|
+
);
|
84
|
+
};
|
85
|
+
|
86
|
+
export default MultiLevelSelectDefault;
|
@@ -0,0 +1,3 @@
|
|
1
|
+
The MultiLevelSelect kit renders a multi leveled select dropdown based on data from the user. `treeData` is a required prop that is expected to contain the data in the form of an array of objects. See code snippet for an example data array.
|
2
|
+
|
3
|
+
The `onSelect` prop returns an array of all checked items, irrespective of whether it is a parent, child or grandchild. Open the console on this example and check and uncheck checkboxes to see this is action!
|
@@ -0,0 +1 @@
|
|
1
|
+
export { default as MultiLevelSelectDefault } from './_multi_level_select_default.jsx'
|
@@ -0,0 +1,60 @@
|
|
1
|
+
export const findItemById = (
|
2
|
+
items: { [key: string]: any }[],
|
3
|
+
id: string
|
4
|
+
): any => {
|
5
|
+
for (const item of items) {
|
6
|
+
if (item.id === id) {
|
7
|
+
return item;
|
8
|
+
}
|
9
|
+
if (item.children) {
|
10
|
+
const found = findItemById(item.children, id);
|
11
|
+
if (found) {
|
12
|
+
return found;
|
13
|
+
}
|
14
|
+
}
|
15
|
+
}
|
16
|
+
return null;
|
17
|
+
};
|
18
|
+
|
19
|
+
export const checkIt = (
|
20
|
+
foundItem: { [key: string]: any },
|
21
|
+
selectedItems: any[],
|
22
|
+
setSelectedItems: Function
|
23
|
+
) => {
|
24
|
+
if (!foundItem) {
|
25
|
+
return;
|
26
|
+
}
|
27
|
+
|
28
|
+
foundItem.checked = true;
|
29
|
+
foundItem.expanded = true;
|
30
|
+
selectedItems.push(foundItem);
|
31
|
+
|
32
|
+
if (foundItem.children) {
|
33
|
+
foundItem.children.map((x: any) => {
|
34
|
+
checkIt(x, selectedItems, setSelectedItems);
|
35
|
+
});
|
36
|
+
}
|
37
|
+
|
38
|
+
setSelectedItems([...selectedItems]);
|
39
|
+
};
|
40
|
+
|
41
|
+
export const unCheckIt = (
|
42
|
+
foundItem: { [key: string]: any },
|
43
|
+
selectedItems: any,
|
44
|
+
setSelectedItems: any
|
45
|
+
) => {
|
46
|
+
if (!foundItem) {
|
47
|
+
return;
|
48
|
+
}
|
49
|
+
|
50
|
+
foundItem.checked = false;
|
51
|
+
const newSelectedItems = selectedItems.filter(
|
52
|
+
(item: any) => item.id !== foundItem.id
|
53
|
+
);
|
54
|
+
if (foundItem.children) {
|
55
|
+
foundItem.children.map((x: any) => {
|
56
|
+
unCheckIt(x, selectedItems, setSelectedItems);
|
57
|
+
});
|
58
|
+
}
|
59
|
+
setSelectedItems([...newSelectedItems]);
|
60
|
+
};
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import React from 'react'
|
2
|
+
import { render, screen } from '../utilities/test-utils'
|
3
|
+
|
4
|
+
import { MultiLevelSelect } from '../'
|
5
|
+
|
6
|
+
const treeData = {
|
7
|
+
label: 'search me',
|
8
|
+
value: 'searchme',
|
9
|
+
id:'default1',
|
10
|
+
children: [
|
11
|
+
{
|
12
|
+
label: 'search me too',
|
13
|
+
value: 'searchmetoo',
|
14
|
+
id:'default2',
|
15
|
+
children: [
|
16
|
+
{
|
17
|
+
label: 'No one can get me',
|
18
|
+
value: 'anonymous',
|
19
|
+
id:'default2',
|
20
|
+
},
|
21
|
+
],
|
22
|
+
},
|
23
|
+
],
|
24
|
+
}
|
25
|
+
|
26
|
+
const testId = "multiselect-test"
|
27
|
+
test('should render custom class', () => {
|
28
|
+
render(
|
29
|
+
<MultiLevelSelect
|
30
|
+
className='custom-class'
|
31
|
+
data={{ testid: testId}}
|
32
|
+
onSelect={()=> console.log("hello")}
|
33
|
+
treeData={treeData}
|
34
|
+
/>
|
35
|
+
)
|
36
|
+
|
37
|
+
const kit = screen.getByTestId(testId)
|
38
|
+
expect(kit).toHaveClass('custom-class')
|
39
|
+
})
|
40
|
+
|
@@ -1,5 +1,3 @@
|
|
1
|
-
/* @flow */
|
2
|
-
|
3
1
|
import React from 'react'
|
4
2
|
import classnames from 'classnames'
|
5
3
|
|
@@ -17,14 +15,13 @@ type ContactItem = {
|
|
17
15
|
}
|
18
16
|
|
19
17
|
type PersonContactProps = {
|
20
|
-
aria?:
|
21
|
-
className?: string |
|
22
|
-
dark?: boolean,
|
18
|
+
aria?: { [key: string]: string },
|
19
|
+
className?: string | string[],
|
23
20
|
data?: object,
|
24
21
|
firstName: string,
|
25
22
|
id?: string,
|
26
23
|
lastName: string,
|
27
|
-
contacts?:
|
24
|
+
contacts?: ContactItem[],
|
28
25
|
}
|
29
26
|
|
30
27
|
const PersonContact = (props: PersonContactProps) => {
|
@@ -60,34 +57,34 @@ const PersonContact = (props: PersonContactProps) => {
|
|
60
57
|
|
61
58
|
return (
|
62
59
|
<div
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
60
|
+
{...ariaProps}
|
61
|
+
{...dataProps}
|
62
|
+
className={classes}
|
63
|
+
id={id}
|
67
64
|
>
|
68
65
|
<Person
|
69
|
-
|
70
|
-
|
66
|
+
firstName={firstName}
|
67
|
+
lastName={lastName}
|
71
68
|
/>
|
72
69
|
{validContacts().map((contactObject, index) => (
|
73
70
|
<Contact
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
71
|
+
contactDetail={contactObject.contactDetail}
|
72
|
+
contactType={contactObject.contactType}
|
73
|
+
contactValue={contactObject.contactValue}
|
74
|
+
key={`valid-contact-${index}`}
|
78
75
|
/>
|
79
76
|
))}
|
80
77
|
{wrongContacts().map((contactObject, index) => (
|
81
78
|
<div key={`wrong-contact-caption-wrapper-${index}`}>
|
82
79
|
<Caption
|
83
|
-
|
84
|
-
|
85
|
-
|
80
|
+
className="wrong_numbers"
|
81
|
+
key={`wrong-contact-caption-${index}`}
|
82
|
+
text="wrong number"
|
86
83
|
/>
|
87
84
|
<Contact
|
88
|
-
|
89
|
-
|
90
|
-
|
85
|
+
contactType={contactObject.contactType}
|
86
|
+
contactValue={contactObject.contactValue}
|
87
|
+
key={`wrong-contact-${index}`}
|
91
88
|
/>
|
92
89
|
</div>
|
93
90
|
))}
|