playbook_ui 13.23.0 β†’ 13.24.0.pre.alpha.PBNTR261NewKitDropdown2681

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/index.js +1 -0
  4. data/app/pb_kits/playbook/pb_avatar/avatar.html.erb +1 -6
  5. data/app/pb_kits/playbook/pb_bar_graph/_bar_graph.tsx +41 -6
  6. data/app/pb_kits/playbook/pb_bar_graph/bar_graph.rb +4 -0
  7. data/app/pb_kits/playbook/pb_bar_graph/docs/_bar_graph_negative_numbers.html.erb +23 -0
  8. data/app/pb_kits/playbook/pb_bar_graph/docs/_bar_graph_negative_numbers.jsx +35 -0
  9. data/app/pb_kits/playbook/pb_bar_graph/docs/_bar_graph_secondary_y_axis.html.erb +26 -0
  10. data/app/pb_kits/playbook/pb_bar_graph/docs/_bar_graph_secondary_y_axis.jsx +36 -0
  11. data/app/pb_kits/playbook/pb_bar_graph/docs/_bar_graph_secondary_y_axis.md +3 -0
  12. data/app/pb_kits/playbook/pb_bar_graph/docs/_bar_graph_stacked.html.erb +22 -0
  13. data/app/pb_kits/playbook/pb_bar_graph/docs/_bar_graph_stacked.jsx +34 -0
  14. data/app/pb_kits/playbook/pb_bar_graph/docs/_bar_graph_stacked.md +1 -0
  15. data/app/pb_kits/playbook/pb_bar_graph/docs/example.yml +6 -0
  16. data/app/pb_kits/playbook/pb_bar_graph/docs/index.js +3 -0
  17. data/app/pb_kits/playbook/pb_body/_body.scss +3 -3
  18. data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +92 -0
  19. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +152 -0
  20. data/app/pb_kits/playbook/pb_dropdown/context/index.tsx +5 -0
  21. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_default.jsx +53 -0
  22. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_display.jsx +104 -0
  23. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_options.jsx +69 -0
  24. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_trigger.jsx +78 -0
  25. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +9 -0
  26. data/app/pb_kits/playbook/pb_dropdown/docs/index.js +4 -0
  27. data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +17 -0
  28. data/app/pb_kits/playbook/pb_dropdown/hooks/useDropdown.tsx +17 -0
  29. data/app/pb_kits/playbook/pb_dropdown/hooks/useHandleOnKeydown.tsx +53 -0
  30. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownContainer.tsx +95 -0
  31. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownOption.tsx +91 -0
  32. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +118 -0
  33. data/app/pb_kits/playbook/pb_list/_list_item.tsx +2 -2
  34. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +27 -19
  35. data/app/pb_kits/playbook/pb_typeahead/components/MenuList.tsx +4 -2
  36. data/app/pb_kits/playbook/pb_typeahead/components/index.tsx +19 -0
  37. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_custom_menu_list.jsx +51 -0
  38. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_with_highlight.jsx +1 -1
  39. data/app/pb_kits/playbook/pb_typeahead/docs/example.yml +1 -0
  40. data/app/pb_kits/playbook/pb_typeahead/docs/index.js +1 -0
  41. data/app/pb_kits/playbook/playbook-doc.js +2 -0
  42. data/app/pb_kits/playbook/tokens/_colors.scss +1 -1
  43. data/dist/menu.yml +5 -1
  44. data/dist/playbook-rails.js +6 -6
  45. data/lib/playbook/kit_base.rb +21 -1
  46. data/lib/playbook/version.rb +2 -2
  47. metadata +30 -29
@@ -0,0 +1,53 @@
1
+ import React, { useState } from 'react'
2
+ import { Dropdown } from '../../'
3
+
4
+ const DropdownDefault = (props) => {
5
+ // eslint-disable-next-line no-unused-vars
6
+ const [selectedOption, setSelectedOption] = useState();
7
+
8
+ const options = [
9
+ {
10
+ label: "United States",
11
+ value: "United States",
12
+ areaCode: "+1",
13
+ icon: "πŸ‡ΊπŸ‡Έ",
14
+ id: "United-states"
15
+ },
16
+ {
17
+ label: "Ukraine",
18
+ value: "Ukraine",
19
+ areaCode: "+380",
20
+ icon: "πŸ‡ΊπŸ‡¦",
21
+ id: "ukraine"
22
+ },
23
+ {
24
+ label: "Pakistan",
25
+ value: "Pakistan",
26
+ areaCode: "+92",
27
+ icon: "πŸ‡΅πŸ‡°",
28
+ id: "pakistan"
29
+ }
30
+ ];
31
+
32
+
33
+ return (
34
+ <div>
35
+ <Dropdown
36
+ onSelect={(selectedItem) => setSelectedOption(selectedItem)}
37
+ options={options}
38
+ {...props}
39
+ >
40
+ <Dropdown.Trigger/>
41
+ <Dropdown.Container>
42
+ {options.map((option) => (
43
+ <Dropdown.Option key={option.id}
44
+ option={option}
45
+ />
46
+ ))}
47
+ </Dropdown.Container>
48
+ </Dropdown>
49
+ </div>
50
+ )
51
+ }
52
+
53
+ export default DropdownDefault
@@ -0,0 +1,104 @@
1
+ import React, { useState } from 'react'
2
+ import { Dropdown, User, FlexItem, Badge, Avatar } from '../../'
3
+
4
+ const DropdownWithCustomDisplay = (props) => {
5
+ const [selectedOption, setSelectedOption] = useState();
6
+
7
+ const options = [
8
+ {
9
+ label: "Jasper Furniss",
10
+ value: "Jasper Furniss",
11
+ territory: "PHL",
12
+ title: "Senior UX Engineer",
13
+ id: "jasper-furniss",
14
+ status: "Offline"
15
+ },
16
+ {
17
+ label: "Ramon Ruiz",
18
+ value: "Ramon Ruiz",
19
+ territory: "PHL",
20
+ title: "Senior UX Desinger",
21
+ id: "ramon-ruiz",
22
+ status: "Away"
23
+ },
24
+ {
25
+ label: "Jason Cypret",
26
+ value: "Jason Cypret",
27
+ territory: "PHL",
28
+ title: "VP of User Experience",
29
+ id: "jason-cypret",
30
+ status: "Online"
31
+ },
32
+ {
33
+ label: "Courtney Long",
34
+ value: "Courtney Long",
35
+ territory: "PHL",
36
+ title: "UX Design Mentor",
37
+ id: "courtney-long",
38
+ status: "Online"
39
+ }
40
+ ];
41
+
42
+ const customDisplay = () => {
43
+ return (
44
+ <>
45
+ {
46
+ selectedOption && (
47
+ <Avatar
48
+ name={selectedOption.label}
49
+ size="sm"
50
+ />
51
+ )
52
+ }
53
+ </>
54
+ )
55
+ };
56
+
57
+
58
+ return (
59
+ <div>
60
+ <Dropdown
61
+ onSelect={(selectedItem) => setSelectedOption(selectedItem)}
62
+ options={options}
63
+ {...props}
64
+ >
65
+ <Dropdown.Trigger customDisplay={customDisplay()}/>
66
+ <Dropdown.Container>
67
+ {options.map((option) => (
68
+ <Dropdown.Option key={option.id}
69
+ option={option}
70
+ >
71
+ <>
72
+ <FlexItem>
73
+ <User
74
+ align="left"
75
+ avatar
76
+ name={option.label}
77
+ orientation="horizontal"
78
+ territory={option.territory}
79
+ title={option.title}
80
+ />
81
+ </FlexItem>
82
+ <FlexItem>
83
+ <Badge
84
+ rounded
85
+ text={option.status}
86
+ variant={`${
87
+ option.status === "Offline"
88
+ ? "neutral"
89
+ : option.status === "Online"
90
+ ? "success"
91
+ : "warning"
92
+ }`}
93
+ />
94
+ </FlexItem>
95
+ </>
96
+ </Dropdown.Option>
97
+ ))}
98
+ </Dropdown.Container>
99
+ </Dropdown>
100
+ </div>
101
+ )
102
+ }
103
+
104
+ export default DropdownWithCustomDisplay
@@ -0,0 +1,69 @@
1
+ import React, { useState } from 'react'
2
+ import { Dropdown, Icon, Body, FlexItem, Flex } from '../..'
3
+
4
+ const DropdownWithCustomOptions = (props) => {
5
+ // eslint-disable-next-line no-unused-vars
6
+ const [selectedOption, setSelectedOption] = useState();
7
+
8
+ const options = [
9
+ {
10
+ label: "United States",
11
+ value: "United States",
12
+ areaCode: "+1",
13
+ icon: "πŸ‡ΊπŸ‡Έ",
14
+ id: "United-states"
15
+ },
16
+ {
17
+ label: "Ukraine",
18
+ value: "Ukraine",
19
+ areaCode: "+380",
20
+ icon: "πŸ‡ΊπŸ‡¦",
21
+ id: "ukraine"
22
+ },
23
+ {
24
+ label: "Pakistan",
25
+ value: "Pakistan",
26
+ areaCode: "+92",
27
+ icon: "πŸ‡΅πŸ‡°",
28
+ id: "pakistan"
29
+ }
30
+ ];
31
+
32
+
33
+ return (
34
+ <div>
35
+ <Dropdown
36
+ onSelect={(selectedItem) => setSelectedOption(selectedItem)}
37
+ options={options}
38
+ {...props}
39
+ >
40
+ <Dropdown.Trigger/>
41
+ <Dropdown.Container>
42
+ {options.map((option) => (
43
+ <Dropdown.Option key={option.id}
44
+ option={option}
45
+ >
46
+ <>
47
+ <FlexItem>
48
+ <Flex>
49
+ <Icon icon={option.icon}
50
+ paddingRight="xs"
51
+ />
52
+ <Body text={option.label} />
53
+ </Flex>
54
+ </FlexItem>
55
+ <FlexItem>
56
+ <Body color="light"
57
+ text={option.areaCode}
58
+ />
59
+ </FlexItem>
60
+ </>
61
+ </Dropdown.Option>
62
+ ))}
63
+ </Dropdown.Container>
64
+ </Dropdown>
65
+ </div>
66
+ )
67
+ }
68
+
69
+ export default DropdownWithCustomOptions
@@ -0,0 +1,78 @@
1
+ import React, { useState } from 'react'
2
+ import { Dropdown, Icon, Body, FlexItem, Flex, IconCircle } from '../..'
3
+
4
+ const DropdownWithCustomTrigger = (props) => {
5
+
6
+ const [selectedOption, setSelectedOption] = useState();
7
+
8
+ const options = [
9
+ {
10
+ label: "United States",
11
+ value: "United States",
12
+ areaCode: "+1",
13
+ icon: "πŸ‡ΊπŸ‡Έ",
14
+ id: "United-states"
15
+ },
16
+ {
17
+ label: "Ukraine",
18
+ value: "Ukraine",
19
+ areaCode: "+380",
20
+ icon: "πŸ‡ΊπŸ‡¦",
21
+ id: "ukraine"
22
+ },
23
+ {
24
+ label: "Pakistan",
25
+ value: "Pakistan",
26
+ areaCode: "+92",
27
+ icon: "πŸ‡΅πŸ‡°",
28
+ id: "pakistan"
29
+ }
30
+ ];
31
+
32
+
33
+ return (
34
+ <div>
35
+ <Dropdown
36
+ onSelect={(selectedItem) => setSelectedOption(selectedItem)}
37
+ options={options}
38
+ {...props}
39
+ >
40
+ <Dropdown.Trigger>
41
+ <div key={selectedOption ? selectedOption.icon : "flag"}>
42
+ <IconCircle
43
+ cursor="pointer"
44
+ icon={selectedOption ? selectedOption.icon : "flag"}
45
+ variant="royal"
46
+ />
47
+ </div>
48
+ </Dropdown.Trigger>
49
+
50
+ <Dropdown.Container>
51
+ {options.map((option) => (
52
+ <Dropdown.Option key={option.id}
53
+ option={option}
54
+ >
55
+ <>
56
+ <FlexItem>
57
+ <Flex>
58
+ <Icon icon={option.icon}
59
+ paddingRight="xs"
60
+ />
61
+ <Body text={option.label} />
62
+ </Flex>
63
+ </FlexItem>
64
+ <FlexItem>
65
+ <Body color="light"
66
+ text={option.areaCode}
67
+ />
68
+ </FlexItem>
69
+ </>
70
+ </Dropdown.Option>
71
+ ))}
72
+ </Dropdown.Container>
73
+ </Dropdown>
74
+ </div>
75
+ )
76
+ }
77
+
78
+ export default DropdownWithCustomTrigger
@@ -0,0 +1,9 @@
1
+ examples:
2
+
3
+
4
+ react:
5
+ - dropdown_default: Default
6
+ - dropdown_with_custom_options: Custom Options
7
+ - dropdown_with_custom_display: Custom Display
8
+ - dropdown_with_custom_trigger: Custom Trigger
9
+
@@ -0,0 +1,4 @@
1
+ export { default as DropdownDefault } from './_dropdown_default.jsx'
2
+ export { default as DropdownWithCustomDisplay } from './_dropdown_with_custom_display.jsx'
3
+ export { default as DropdownWithCustomOptions } from './_dropdown_with_custom_options.jsx'
4
+ export { default as DropdownWithCustomTrigger } from './_dropdown_with_custom_trigger.jsx'
@@ -0,0 +1,17 @@
1
+ import { renderKit } from '../utilities/test-utils'
2
+
3
+ import { Dropdown } from '../'
4
+
5
+ /* See these resources for more testing info:
6
+ - https://github.com/testing-library/jest-dom#usage for useage and examples
7
+ - https://jestjs.io/docs/en/using-matchers
8
+ */
9
+
10
+ test('generated scaffold test - update me', () => {
11
+ const props = {
12
+ data: { testid: 'default' }
13
+ }
14
+
15
+ const kit = renderKit(Dropdown , props)
16
+ expect(kit).toBeInTheDocument()
17
+ })
@@ -0,0 +1,17 @@
1
+ import {useState} from 'react';
2
+
3
+
4
+ const useDropdown = (initial=true) => {
5
+
6
+ const [isDropDownClosed, setIsDropDownClosed] = useState(initial);
7
+
8
+ const toggleDropdown = () => setIsDropDownClosed(!isDropDownClosed);
9
+
10
+ return [
11
+ isDropDownClosed,
12
+ setIsDropDownClosed as any,
13
+ toggleDropdown
14
+ ]
15
+ }
16
+
17
+ export default useDropdown
@@ -0,0 +1,53 @@
1
+ import React, { useContext } from "react";
2
+ import DropdownContext from "../context";
3
+
4
+
5
+ export const useHandleOnKeyDown = () => {
6
+
7
+ const {
8
+ focusedOptionIndex,
9
+ filteredOptions,
10
+ setFocusedOptionIndex,
11
+ handleOptionClick,
12
+ setIsDropDownClosed,
13
+ handleBackspace,
14
+ selected
15
+ }= useContext(DropdownContext)
16
+
17
+ return (e: React.KeyboardEvent) => {
18
+ switch (e.key) {
19
+ case "Backspace":
20
+ case "Delete":
21
+ handleBackspace();
22
+ break;
23
+ case "ArrowDown": {
24
+ e.preventDefault();
25
+ setIsDropDownClosed(false);
26
+ const nextIndex = (focusedOptionIndex + 1) % filteredOptions.length;
27
+ setFocusedOptionIndex(nextIndex);
28
+ break;
29
+ }
30
+ case "ArrowUp": {
31
+ e.preventDefault();
32
+ const nextIndexUp =
33
+ (focusedOptionIndex - 1 + filteredOptions.length) %
34
+ filteredOptions.length;
35
+ setFocusedOptionIndex(nextIndexUp);
36
+ break;
37
+ }
38
+ case "Enter":
39
+ if (focusedOptionIndex !== -1) {
40
+ e.preventDefault();
41
+ handleOptionClick(filteredOptions[focusedOptionIndex]);
42
+ setFocusedOptionIndex(-1)
43
+ }
44
+ break;
45
+ default:
46
+ if (selected && selected.label) {
47
+ e.preventDefault();
48
+ handleBackspace();
49
+ }
50
+ break;
51
+ }
52
+ }
53
+ };
@@ -0,0 +1,95 @@
1
+ import React, { useContext } from "react";
2
+ import classnames from "classnames";
3
+ import {
4
+ buildAriaProps,
5
+ buildCss,
6
+ buildDataProps,
7
+ } from "../../utilities/props";
8
+ import { globalProps } from "../../utilities/globalProps";
9
+
10
+ import DropdownContext from "../context";
11
+
12
+ import List from "../../pb_list/_list";
13
+ import ListItem from "../../pb_list/_list_item";
14
+ import TextInput from "../../pb_text_input/_text_input";
15
+ import Body from "../../pb_body/_body";
16
+
17
+ type DropdownContainerProps = {
18
+ aria?: { [key: string]: string };
19
+ className?: string;
20
+ children?: React.ReactChild[] | React.ReactChild;
21
+ data?: { [key: string]: string };
22
+ id?: string;
23
+ searchbar?: boolean;
24
+ };
25
+
26
+ const DropdownContainer = (props: DropdownContainerProps) => {
27
+ const {
28
+ aria = {},
29
+ className,
30
+ children,
31
+ data = {},
32
+ id,
33
+ searchbar = false,
34
+ } = props;
35
+
36
+ const {
37
+ isDropDownClosed,
38
+ handleChange,
39
+ filterItem,
40
+ filteredOptions,
41
+ inputRef,
42
+ setFocusedOptionIndex,
43
+ } = useContext(DropdownContext);
44
+
45
+ const ariaProps = buildAriaProps(aria);
46
+ const dataProps = buildDataProps(data);
47
+ const classes = classnames(
48
+ buildCss("pb_dropdown_container"),
49
+ `${isDropDownClosed ? "close" : "open"}`,
50
+ globalProps(props),
51
+ className
52
+ );
53
+
54
+ return (
55
+ <div {...ariaProps}
56
+ {...dataProps}
57
+ className={classes}
58
+ id={id}
59
+ onMouseEnter={() => setFocusedOptionIndex(-1)}
60
+ >
61
+ {searchbar && (
62
+ <TextInput paddingTop="xs"
63
+ paddingX="xs"
64
+ >
65
+ <input
66
+ onChange={handleChange}
67
+ placeholder="Select..."
68
+ ref={inputRef}
69
+ value={filterItem}
70
+ />
71
+ </TextInput>
72
+ )}
73
+ <List>{
74
+ filteredOptions?.length === 0 ? (
75
+ <ListItem
76
+ display="flex"
77
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
78
+ // @ts-ignore
79
+ justifyContent="center"
80
+ padding="xs"
81
+ >
82
+ <Body color="light"
83
+ text="no option"
84
+ />
85
+ </ListItem>
86
+ ): (
87
+ children
88
+ )
89
+ }
90
+ </List>
91
+ </div>
92
+ );
93
+ };
94
+
95
+ export default DropdownContainer;
@@ -0,0 +1,91 @@
1
+ import React, { useContext } from "react";
2
+ import classnames from "classnames";
3
+ import {
4
+ buildAriaProps,
5
+ buildCss,
6
+ buildDataProps,
7
+ } from "../../utilities/props";
8
+ import { globalProps } from "../../utilities/globalProps";
9
+
10
+ import DropdownContext from "../context";
11
+
12
+ import Flex from "../../pb_flex/_flex";
13
+ import Body from "../../pb_body/_body";
14
+ import ListItem from "../../pb_list/_list_item";
15
+ import { GenericObject } from "../../types";
16
+
17
+ type DropdownOptionProps = {
18
+ aria?: { [key: string]: string };
19
+ className?: string;
20
+ children?: React.ReactChild[] | React.ReactChild;
21
+ data?: { [key: string]: string };
22
+ id?: string;
23
+ option?: GenericObject;
24
+ key?: string;
25
+ };
26
+
27
+ const DropdownOption = (props: DropdownOptionProps) => {
28
+ const { aria = {}, className, children, data = {}, id, option, key } = props;
29
+
30
+ const { handleOptionClick, selected, filterItem, filteredOptions, focusedOptionIndex } =
31
+ useContext(DropdownContext);
32
+
33
+ const isItemMatchingFilter = (option: GenericObject) =>
34
+ option?.label.toLowerCase().includes(filterItem.toLowerCase());
35
+
36
+ if (!isItemMatchingFilter(option)) {
37
+ return null;
38
+ }
39
+ const isFocused = focusedOptionIndex >= 0 && filteredOptions[focusedOptionIndex].label === option.label
40
+ const focusedClass = isFocused && "dropdown_option_focused"
41
+
42
+ const selectedClass = `${
43
+ selected.label === option.label
44
+ ? "dropdown_option_selected"
45
+ : "dropdown_option_list"
46
+ }`
47
+ const ariaProps = buildAriaProps(aria);
48
+ const dataProps = buildDataProps(data);
49
+ const classes = classnames(
50
+ buildCss("pb_dropdown_option"),
51
+ selectedClass,
52
+ focusedClass,
53
+ globalProps(props),
54
+ className
55
+ );
56
+
57
+ return (
58
+ <div {...ariaProps}
59
+ {...dataProps}
60
+ className={classes}
61
+ id={id}
62
+ key={key}
63
+ >
64
+ <ListItem
65
+ cursor="pointer"
66
+ data-name={option.value}
67
+ htmlOptions={{ onClick: () => handleOptionClick(option) }}
68
+ key={option.label}
69
+ padding="xs"
70
+ >
71
+ <Flex
72
+ align="center"
73
+ className="dropdown_option"
74
+ justify="between"
75
+ paddingX="sm"
76
+ paddingY="xxs"
77
+ >
78
+ {
79
+ children ? (
80
+ children
81
+ ) : (
82
+ <Body text={option.label}/>
83
+ )
84
+ }
85
+ </Flex>
86
+ </ListItem>
87
+ </div>
88
+ );
89
+ };
90
+
91
+ export default DropdownOption;