playbook_ui 12.5.0.pre.alpha.phonerails1 → 12.6.0.pre.alpha.cssphone1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/_playbook.scss +2 -0
  3. data/app/pb_kits/playbook/data/menu.yml +2 -1
  4. data/app/pb_kits/playbook/index.js +1 -0
  5. data/app/pb_kits/playbook/pb_button/_button_mixins.scss +2 -2
  6. data/app/pb_kits/playbook/pb_circle_icon_button/_circle_icon_button.scss +1 -1
  7. data/app/pb_kits/playbook/pb_filter/Filter/CurrentFilters.tsx +72 -0
  8. data/app/pb_kits/playbook/pb_filter/Filter/{FilterBackground.jsx → FilterBackground.tsx} +12 -14
  9. data/app/pb_kits/playbook/pb_filter/Filter/{FilterDouble.jsx → FilterDouble.tsx} +7 -8
  10. data/app/pb_kits/playbook/pb_filter/Filter/{FilterSingle.jsx → FilterSingle.tsx} +25 -25
  11. data/app/pb_kits/playbook/pb_filter/Filter/{FiltersPopover.jsx → FiltersPopover.tsx} +13 -11
  12. data/app/pb_kits/playbook/pb_filter/Filter/{ResultsCount.jsx → ResultsCount.tsx} +39 -14
  13. data/app/pb_kits/playbook/pb_filter/Filter/{SortMenu.jsx → SortMenu.tsx} +6 -6
  14. data/app/pb_kits/playbook/pb_filter/Filter/{index.jsx → index.tsx} +17 -10
  15. data/app/pb_kits/playbook/pb_filter/{_filter.jsx → _filter.tsx} +0 -2
  16. data/app/pb_kits/playbook/pb_list/_list.tsx +2 -2
  17. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.scss +86 -0
  18. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +81 -0
  19. data/app/pb_kits/playbook/pb_multi_level_select/_multi_select_helper.tsx +30 -0
  20. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_default.jsx +86 -0
  21. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_default.md +3 -0
  22. data/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +6 -0
  23. data/app/pb_kits/playbook/pb_multi_level_select/docs/index.js +1 -0
  24. data/app/pb_kits/playbook/pb_multi_level_select/helper_functions.ts +60 -0
  25. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.test.jsx +40 -0
  26. data/app/pb_kits/playbook/pb_person_contact/{_person_contact.jsx → _person_contact.tsx} +19 -22
  27. data/app/pb_kits/playbook/pb_person_contact/person_contact.test.js +112 -0
  28. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.scss +52 -16
  29. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +21 -20
  30. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_default.jsx +1 -0
  31. data/app/pb_kits/playbook/pb_popover/_popover.tsx +33 -32
  32. data/app/pb_kits/playbook/playbook-doc.js +2 -0
  33. data/app/pb_kits/playbook/tokens/_animation-curves.scss +30 -30
  34. data/app/pb_kits/playbook/tokens/_border_radius.scss +15 -16
  35. data/app/pb_kits/playbook/tokens/_colors.scss +3 -1
  36. data/app/pb_kits/playbook/tokens/_display.scss +6 -6
  37. data/app/pb_kits/playbook/tokens/_line_height.scss +7 -7
  38. data/app/pb_kits/playbook/tokens/_opacity.scss +10 -10
  39. data/app/pb_kits/playbook/tokens/_positioning.scss +11 -11
  40. data/app/pb_kits/playbook/tokens/_screen_sizes.scss +10 -10
  41. data/app/pb_kits/playbook/tokens/_shadows.scss +4 -4
  42. data/app/pb_kits/playbook/tokens/_spacing.scss +6 -6
  43. data/app/pb_kits/playbook/tokens/_transition.scss +3 -3
  44. data/app/pb_kits/playbook/tokens/_typography.scss +35 -46
  45. data/lib/playbook/version.rb +2 -2
  46. metadata +22 -12
  47. data/app/pb_kits/playbook/pb_filter/Filter/CurrentFilters.jsx +0 -76
@@ -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,6 @@
1
+ examples:
2
+
3
+
4
+ react:
5
+ - multi_level_select_default: Default
6
+
@@ -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?: object,
21
- className?: string | array<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?: array<ContactItem>,
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
- {...ariaProps}
64
- {...dataProps}
65
- className={classes}
66
- id={id}
60
+ {...ariaProps}
61
+ {...dataProps}
62
+ className={classes}
63
+ id={id}
67
64
  >
68
65
  <Person
69
- firstName={firstName}
70
- lastName={lastName}
66
+ firstName={firstName}
67
+ lastName={lastName}
71
68
  />
72
69
  {validContacts().map((contactObject, index) => (
73
70
  <Contact
74
- contactDetail={contactObject.contactDetail}
75
- contactType={contactObject.contactType}
76
- contactValue={contactObject.contactValue}
77
- key={`valid-contact-${index}`}
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
- className="wrong_numbers"
84
- key={`wrong-contact-caption-${index}`}
85
- text="wrong number"
80
+ className="wrong_numbers"
81
+ key={`wrong-contact-caption-${index}`}
82
+ text="wrong number"
86
83
  />
87
84
  <Contact
88
- contactType={contactObject.contactType}
89
- contactValue={contactObject.contactValue}
90
- key={`wrong-contact-${index}`}
85
+ contactType={contactObject.contactType}
86
+ contactValue={contactObject.contactValue}
87
+ key={`wrong-contact-${index}`}
91
88
  />
92
89
  </div>
93
90
  ))}
@@ -0,0 +1,112 @@
1
+ import React from 'react'
2
+ import { render, screen } from '../utilities/test-utils'
3
+ import PersonContact from './_person_contact'
4
+
5
+ const testId = 'personContact'
6
+ const multipleTestId = 'personContactMultiple'
7
+
8
+ const PersonContactTest = (props) => {
9
+ return (
10
+ <>
11
+ <PersonContact
12
+ aria={{ label: testId }}
13
+ className={'custom-class'}
14
+ contacts={[
15
+ {
16
+ contactType: 'email',
17
+ contactValue: 'email@example.com',
18
+ },
19
+ {
20
+ contactValue: '5555555555',
21
+ contactDetail: 'Home',
22
+ },
23
+ {
24
+ contactType: 'work',
25
+ contactValue: '3245627482',
26
+ contactDetail: 'Work',
27
+ },
28
+ ]}
29
+ data={{ testid: testId }}
30
+ firstName="Jose"
31
+ id={testId}
32
+ lastName="da Silva"
33
+ {...props}
34
+ />
35
+ <PersonContact
36
+ contacts={[
37
+ {
38
+ contactValue: '5555555555',
39
+ contactType: 'wrong-phone',
40
+ },
41
+ ]}
42
+ data={{ testid: multipleTestId }}
43
+ firstName="Brenda"
44
+ lastName="Walters"
45
+ {...props}
46
+ />
47
+ </>
48
+ )
49
+ }
50
+
51
+ test('should render custom class and data', () => {
52
+ render(<PersonContactTest />)
53
+
54
+ const kit = screen.getByTestId(testId)
55
+ expect(kit).toHaveClass('custom-class')
56
+ })
57
+
58
+ test('should render id', () => {
59
+ render(<PersonContactTest />)
60
+
61
+ const kit = screen.getByTestId(testId)
62
+ expect(kit).toHaveProperty('id', testId)
63
+ })
64
+
65
+ test('should render aria-label', () => {
66
+ render(<PersonContactTest />)
67
+
68
+ const kit = screen.getByTestId(testId)
69
+ expect(kit).toHaveAttribute('aria-label', testId)
70
+ })
71
+
72
+ test('should render firstName', () => {
73
+ render(<PersonContactTest />)
74
+
75
+ const kit = screen.getByTestId(testId)
76
+ expect(kit).toHaveTextContent('Jose')
77
+ })
78
+
79
+ test('should render lastName', () => {
80
+ render(<PersonContactTest />)
81
+
82
+ const kit = screen.getByTestId(testId)
83
+ expect(kit).toHaveTextContent('da Silva')
84
+ })
85
+
86
+ test('should render contact value', () => {
87
+ render(<PersonContactTest />)
88
+
89
+ const kit = screen.getByTestId(testId)
90
+ expect(kit).toHaveTextContent('(555) 555-5555')
91
+ })
92
+
93
+ test('should render contact detail', () => {
94
+ render(<PersonContactTest />)
95
+
96
+ const kit = screen.getByTestId(testId)
97
+ expect(kit).toHaveTextContent('Home')
98
+ })
99
+
100
+ test('should render multiple contacts', () => {
101
+ render(<PersonContactTest />)
102
+
103
+ const kit = screen.getByTestId(multipleTestId)
104
+ expect(kit).toHaveTextContent('Brenda Walters')
105
+ })
106
+
107
+ test('should render wrong number', () => {
108
+ render(<PersonContactTest />)
109
+
110
+ const kit = screen.getByTestId(multipleTestId)
111
+ expect(kit).toHaveTextContent('wrong number')
112
+ })
@@ -1,16 +1,46 @@
1
1
  @import "../tokens/colors";
2
2
 
3
+ $transform-rotate-deg: 135deg;
4
+ $input-max-width: 284px;
5
+ $dropdown-min-width: 340px;
6
+ $flag-min-resolution: 192dpi;
7
+
3
8
  .pb_phone_number_input {
4
9
  input::placeholder {
5
10
  color: $focus_input_light;
6
11
  }
7
12
 
13
+ .text_input {
14
+ max-width: $input-max-width;
15
+ }
16
+
17
+ .dropdown_open {
18
+ .text_input {
19
+ border-color: $primary !important;
20
+ }
21
+
22
+ .iti__selected-flag:focus-visible {
23
+ outline-style: none;
24
+ }
25
+ }
26
+
8
27
  .iti__country {
9
28
  padding: 5px 10px 5px 16px;
29
+ transition: $transition_default;
10
30
  }
11
31
 
12
32
  .iti__selected-flag {
13
- padding: 0 6px 0 16px;
33
+ padding: 0 $space_xxs 0 $space_sm;
34
+
35
+ &[aria-expanded="true"] {
36
+ color: $primary_action;
37
+ }
38
+
39
+ &:focus-visible {
40
+ outline-style: solid;
41
+ border-radius: $space_xxs 0px 0px $space_xxs;
42
+ outline-color: $primary;
43
+ }
14
44
  }
15
45
 
16
46
  .iti__country.iti__highlight {
@@ -31,6 +61,11 @@
31
61
 
32
62
  .iti__flag {
33
63
  background-image: url("https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.19/img/flags.png");
64
+ border-radius: 1px;
65
+ }
66
+
67
+ .iti--separate-dial-code {
68
+ width: 100%;
34
69
  }
35
70
 
36
71
  .iti--separate-dial-code .iti__selected-flag {
@@ -52,49 +87,50 @@
52
87
 
53
88
  .iti__arrow::before {
54
89
  border-style: solid;
55
- border-width: 0.1em 0.1em 0 0;
90
+ border-width: 1px 1px 0 0;
56
91
  content: '';
57
92
  display: inline-block;
58
- height: 0.6em;
59
- left: 0.1em;
93
+ height: $space_xxs + 1px;
94
+ left: 1px;
60
95
  position: relative;
61
96
  vertical-align: top;
62
- width: 0.6em;
63
- top: 10px;
64
- transform: rotate(135deg);
65
- font-size: 0.5em;
97
+ width: $space_xxs + 1px;
98
+ top: $space_xs + 2px;
99
+ transform: rotate($transform-rotate-deg);
66
100
  color: $slate;
67
101
  }
68
102
 
69
103
  .iti__arrow.iti__arrow--up::before {
70
- transform: rotate(-45deg);
71
- top: 12px;
104
+ transform: rotate(-($transform-rotate-deg/3));
105
+ top: $space_xs + 4px;
106
+ color: $primary_action;
72
107
  }
73
108
 
74
109
  .iti__active::after {
75
110
  float: right;
76
111
  content: "";
77
- margin-top: 5px;
78
- transform: rotate(45deg);
79
- height: 12px;
80
- width: 6px;
112
+ margin-top: $space_xxs + 1px;
113
+ transform: rotate($transform-rotate-deg/3);
114
+ height: $space_xs + 4px;
115
+ width: $space_xxs + 2px;
81
116
  border-bottom: 2px solid;
82
117
  border-right: 2px solid;
83
118
  border-radius: 1px;
84
119
  }
85
120
 
86
121
  .iti__country-list {
87
- min-width: 340px;
122
+ min-width: $dropdown-min-width;
88
123
  border-radius: $border_radius_md;
89
124
  border: 1px solid $border_light;
90
125
  box-shadow: $shadow_deep;
126
+ margin-top: 1px;
91
127
  }
92
128
 
93
129
  .iti__divider {
94
130
  border-bottom: 1px solid $border_light;
95
131
  }
96
132
 
97
- @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
133
+ @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: $flag-min-resolution) {
98
134
  .iti__flag {
99
135
  background-image: url("https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.19/img/flags@2x.png");
100
136
  }