@saleor/macaw-ui 0.3.2 → 0.5.0

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.
Files changed (118) hide show
  1. package/README.md +20 -0
  2. package/dist/cjs/index.js +2 -2
  3. package/dist/cjs/index.js.map +3 -3
  4. package/dist/esm/index.js +2 -2
  5. package/dist/esm/index.js.map +3 -3
  6. package/dist/types/ActionBar/ActionBar.d.ts +1 -0
  7. package/dist/types/ActionBar/context.d.ts +6 -3
  8. package/dist/types/Autocomplete/Autocomplete.d.ts +23 -0
  9. package/dist/types/Autocomplete/Autocomplete.stories.d.ts +5 -0
  10. package/dist/types/Autocomplete/fixtures.d.ts +4 -0
  11. package/dist/types/Autocomplete/index.d.ts +1 -0
  12. package/dist/types/Autocomplete/styles.d.ts +2 -0
  13. package/dist/types/Chip/Chip.d.ts +8 -0
  14. package/dist/types/Chip/ChipAdornment.d.ts +6 -0
  15. package/dist/types/Chip/ChipMovable.d.ts +5 -0
  16. package/dist/types/Chip/ChipRemovable.d.ts +6 -0
  17. package/dist/types/Chip/ChipSwatch.d.ts +7 -0
  18. package/dist/types/Chip/index.d.ts +5 -0
  19. package/dist/types/Chip/private/ColorSwatch.d.ts +6 -0
  20. package/dist/types/Chip/styles.d.ts +2 -0
  21. package/dist/types/CircleIndicator/index.d.ts +1 -1
  22. package/dist/types/Filter/FilterField/AutocompleteFilterField.d.ts +6 -0
  23. package/dist/types/Filter/FilterField/MultipleValueAutocompleteFilterField.d.ts +6 -0
  24. package/dist/types/Filter/stories/Filter.stories.d.ts +5 -0
  25. package/dist/types/Filter/stories/FilterInteractive.stories.d.ts +4 -0
  26. package/dist/types/Filter/stories/labels.d.ts +8 -0
  27. package/dist/types/Filter/styles.d.ts +1 -1
  28. package/dist/types/Filter/types.d.ts +8 -2
  29. package/dist/types/Filter/utils.d.ts +4 -1
  30. package/dist/types/IconButton/IconButton.d.ts +2 -0
  31. package/dist/types/IconButton/partials.d.ts +1 -1
  32. package/dist/types/IconButton/styles.d.ts +1 -1
  33. package/dist/types/MultipleValueAutocomplete/MultipleValueAutocomplete.d.ts +26 -0
  34. package/dist/types/MultipleValueAutocomplete/MultipleValueAutocomplete.stories.d.ts +6 -0
  35. package/dist/types/MultipleValueAutocomplete/fixtures.d.ts +4 -0
  36. package/dist/types/MultipleValueAutocomplete/index.d.ts +1 -0
  37. package/dist/types/MultipleValueAutocomplete/styles.d.ts +2 -0
  38. package/dist/types/MultipleValueAutocomplete/useMultipleValueAutocomplete.d.ts +32 -0
  39. package/dist/types/Savebar/Savebar.d.ts +1 -0
  40. package/dist/types/Sidebar/MenuItem.d.ts +9 -3
  41. package/dist/types/Sidebar/types.d.ts +8 -0
  42. package/dist/types/Sidebar/utils.d.ts +12 -0
  43. package/dist/types/SidebarDrawer/MenuItemBtn.d.ts +2 -1
  44. package/dist/types/SwitchSelector/SwitchSelector.d.ts +2 -0
  45. package/dist/types/SwitchSelector/SwitchSelector.stories.d.ts +4 -0
  46. package/dist/types/SwitchSelector/SwitchSelectorButton.d.ts +8 -0
  47. package/dist/types/SwitchSelector/index.d.ts +2 -0
  48. package/dist/types/index.d.ts +3 -0
  49. package/dist/types/theme/ThemeProvider.d.ts +4 -0
  50. package/dist/types/theme/types.d.ts +1 -0
  51. package/dist/types/tools/useTextWidth.d.ts +2 -0
  52. package/dist/types/utils/guideStyles.d.ts +1 -1
  53. package/dist/types/utils/mergeRefs.d.ts +2 -0
  54. package/dist/types/utils/useMockAutocomplete.d.ts +10 -0
  55. package/package.json +11 -13
  56. package/src/ActionBar/ActionBar.tsx +8 -11
  57. package/src/ActionBar/context.tsx +15 -6
  58. package/src/ActionBar/styles.ts +1 -1
  59. package/src/Autocomplete/Autocomplete.stories.tsx +43 -0
  60. package/src/Autocomplete/Autocomplete.tsx +187 -0
  61. package/src/Autocomplete/fixtures.ts +122 -0
  62. package/src/Autocomplete/index.ts +1 -0
  63. package/src/Autocomplete/styles.ts +19 -0
  64. package/src/Backlink/Backlink.tsx +3 -1
  65. package/src/Chip/Chip.tsx +52 -0
  66. package/src/Chip/ChipAdornment.tsx +53 -0
  67. package/src/Chip/ChipMovable.tsx +40 -0
  68. package/src/Chip/ChipRemovable.tsx +29 -0
  69. package/src/Chip/ChipSwatch.tsx +42 -0
  70. package/src/Chip/index.ts +5 -0
  71. package/src/Chip/private/ColorSwatch.tsx +21 -0
  72. package/src/Chip/styles.ts +46 -0
  73. package/src/CircleIndicator/CircleIndicator.stories.tsx +6 -6
  74. package/src/CircleIndicator/index.ts +1 -1
  75. package/src/Filter/Filter.tsx +88 -44
  76. package/src/Filter/FilterBar.tsx +15 -9
  77. package/src/Filter/FilterContent.tsx +8 -1
  78. package/src/Filter/FilterField/AutocompleteFilterField.tsx +61 -0
  79. package/src/Filter/FilterField/MultipleSelectFilterField.tsx +9 -3
  80. package/src/Filter/FilterField/MultipleValueAutocompleteFilterField.tsx +60 -0
  81. package/src/Filter/context.tsx +1 -1
  82. package/src/Filter/{Filter.stories.tsx → stories/Filter.stories.tsx} +47 -13
  83. package/src/Filter/stories/FilterInteractive.stories.tsx +97 -0
  84. package/src/Filter/stories/labels.ts +8 -0
  85. package/src/Filter/styles.ts +37 -6
  86. package/src/Filter/types.ts +8 -1
  87. package/src/Filter/utils.ts +71 -5
  88. package/src/IconButton/IconButton.tsx +17 -2
  89. package/src/IconButton/partials.ts +1 -1
  90. package/src/IconButton/styles.ts +38 -16
  91. package/src/MultipleValueAutocomplete/MultipleValueAutocomplete.stories.tsx +76 -0
  92. package/src/MultipleValueAutocomplete/MultipleValueAutocomplete.tsx +185 -0
  93. package/src/MultipleValueAutocomplete/fixtures.ts +122 -0
  94. package/src/MultipleValueAutocomplete/index.ts +1 -0
  95. package/src/MultipleValueAutocomplete/styles.ts +39 -0
  96. package/src/MultipleValueAutocomplete/useMultipleValueAutocomplete.ts +172 -0
  97. package/src/Savebar/Savebar.tsx +3 -4
  98. package/src/Sidebar/MenuItem.tsx +35 -14
  99. package/src/Sidebar/Sidebar.tsx +27 -11
  100. package/src/Sidebar/types.ts +9 -0
  101. package/src/Sidebar/utils.ts +23 -0
  102. package/src/SidebarDrawer/MenuItemBtn.tsx +12 -6
  103. package/src/SidebarDrawer/SidebarDrawer.tsx +8 -2
  104. package/src/SwitchSelector/SwitchSelector.stories.tsx +63 -0
  105. package/src/SwitchSelector/SwitchSelector.tsx +19 -0
  106. package/src/SwitchSelector/SwitchSelectorButton.tsx +59 -0
  107. package/src/SwitchSelector/index.ts +2 -0
  108. package/src/index.tsx +4 -1
  109. package/src/theme/ThemeProvider.tsx +6 -0
  110. package/src/theme/createSaleorTheme/createSaleorTheme.tsx +2 -1
  111. package/src/theme/createSaleorTheme/overrides/controls.ts +4 -1
  112. package/src/theme/createSaleorTheme/overrides/inputs.ts +1 -1
  113. package/src/theme/themes.ts +1 -1
  114. package/src/theme/types.ts +1 -0
  115. package/src/tools/useTextWidth.ts +20 -0
  116. package/src/utils/guideStyles.ts +5 -0
  117. package/src/utils/mergeRefs.ts +14 -0
  118. package/src/utils/useMockAutocomplete.ts +37 -0
@@ -0,0 +1,185 @@
1
+ import CircularProgress from "@material-ui/core/CircularProgress";
2
+ import Grow from "@material-ui/core/Grow";
3
+ import Paper from "@material-ui/core/Paper";
4
+ import Popper, { PopperPlacementType } from "@material-ui/core/Popper";
5
+ import TextField, { StandardTextFieldProps } from "@material-ui/core/TextField";
6
+ import clsx from "clsx";
7
+ import { UseComboboxGetItemPropsOptions } from "downshift";
8
+ import React from "react";
9
+
10
+ import { SyntheticChangeEvent } from "../../types/utils";
11
+ import { ChipRemovable } from "../Chip";
12
+ import { Choice } from "../Filter";
13
+ import { IconButton } from "../IconButton";
14
+ import { PlusIcon } from "../icons";
15
+ import {
16
+ isScrolledToBottom,
17
+ useElementScroll,
18
+ } from "../tools/useElementScroll";
19
+ import { mergeRefs } from "../utils/mergeRefs";
20
+ import useStyles from "./styles";
21
+ import useMultipleValueAutocomplete from "./useMultipleValueAutocomplete";
22
+
23
+ export interface MultipleValueAutocompleteProps
24
+ extends Omit<StandardTextFieldProps, "onChange"> {
25
+ children: (data: {
26
+ getItemProps: (opts: UseComboboxGetItemPropsOptions<Choice>) => any;
27
+ highlightedIndex: number;
28
+ inputValue: string;
29
+ choices: Choice[];
30
+ }) => React.ReactNode | React.ReactNodeArray;
31
+ className?: string;
32
+ enableReinitialize?: boolean;
33
+ styles?: React.CSSProperties;
34
+ choices: Choice[];
35
+ label?: string;
36
+ loading?: boolean;
37
+ popperPlacement?: PopperPlacementType;
38
+ initialValue?: Choice[];
39
+ onChange?: (event: SyntheticChangeEvent<string[]>) => void;
40
+ onInputChange?: (value: string) => void;
41
+ onScrollToBottom?: () => void;
42
+ }
43
+
44
+ export const MultipleValueAutocomplete: React.FC<MultipleValueAutocompleteProps> =
45
+ ({
46
+ choices,
47
+ children,
48
+ enableReinitialize,
49
+ name,
50
+ InputProps,
51
+ initialValue = [],
52
+ loading,
53
+ popperPlacement = "bottom-start",
54
+ onChange,
55
+ onInputChange,
56
+ onScrollToBottom,
57
+ ...rest
58
+ }) => {
59
+ const classes = useStyles();
60
+ const {
61
+ anchor,
62
+ comboboxProps,
63
+ filteredChoices,
64
+ getItemProps,
65
+ getSelectedItemProps,
66
+ getToggleButtonProps,
67
+ highlightedIndex,
68
+ inputProps,
69
+ inputRef,
70
+ inputValue,
71
+ inputWidth,
72
+ isOpen,
73
+ labelProps,
74
+ menuProps,
75
+ ref,
76
+ removeSelectedItem,
77
+ selectedItems,
78
+ } = useMultipleValueAutocomplete({
79
+ choices,
80
+ enableReinitialize,
81
+ initialValue,
82
+ name,
83
+ onChange,
84
+ onInputChange,
85
+ });
86
+ const { anchor: dropdownRef, position, setAnchor } = useElementScroll();
87
+
88
+ React.useEffect(() => {
89
+ if (
90
+ isOpen &&
91
+ onScrollToBottom &&
92
+ dropdownRef &&
93
+ isScrolledToBottom(dropdownRef, position!, 5)
94
+ ) {
95
+ onScrollToBottom();
96
+ }
97
+ }, [position?.y, dropdownRef]);
98
+
99
+ return (
100
+ <>
101
+ <TextField
102
+ {...rest}
103
+ {...comboboxProps}
104
+ name={name}
105
+ InputLabelProps={{
106
+ shrink: isOpen || selectedItems.length || inputValue.length,
107
+ ...labelProps,
108
+ }}
109
+ ref={ref}
110
+ InputProps={{
111
+ ...InputProps,
112
+ ...inputProps,
113
+ classes: {
114
+ ...(InputProps?.classes ?? {}),
115
+ root: clsx(classes.inputContainer, InputProps?.classes?.root, {
116
+ [classes.inputContainerWithChips]: selectedItems.length > 0,
117
+ }),
118
+ input: clsx(classes.input, InputProps?.classes?.input),
119
+ },
120
+ startAdornment: selectedItems.map((item, index) => (
121
+ <ChipRemovable
122
+ key={`selected-item-${index}`}
123
+ {...getSelectedItemProps({ selectedItem: item, index })}
124
+ onRemove={() => removeSelectedItem(item)}
125
+ >
126
+ {item.label}
127
+ </ChipRemovable>
128
+ )),
129
+ endAdornment: (
130
+ <>
131
+ {loading && (
132
+ <div className={classes.loader}>
133
+ <CircularProgress size={24} />
134
+ </div>
135
+ )}
136
+ <IconButton
137
+ {...getToggleButtonProps()}
138
+ aria-label="toggle menu"
139
+ className={classes.icon}
140
+ hoverOutline={false}
141
+ type="button"
142
+ variant="secondary"
143
+ >
144
+ <PlusIcon />
145
+ </IconButton>
146
+ </>
147
+ ),
148
+ }}
149
+ inputProps={{ ref: inputRef, style: { width: inputWidth } }}
150
+ />
151
+ <Popper
152
+ className={clsx(classes.popper, menuProps.className)}
153
+ open={isOpen}
154
+ anchorEl={anchor.current}
155
+ transition
156
+ placement={popperPlacement}
157
+ >
158
+ {({ TransitionProps, placement }) => (
159
+ <Grow
160
+ {...TransitionProps}
161
+ style={{
162
+ transformOrigin:
163
+ placement === "bottom" ? "left top" : "left bottom",
164
+ }}
165
+ >
166
+ <Paper
167
+ className={classes.dropdown}
168
+ elevation={8}
169
+ style={{ width: anchor.current?.clientWidth }}
170
+ {...menuProps}
171
+ ref={mergeRefs(setAnchor, menuProps.ref)}
172
+ >
173
+ {children({
174
+ choices: filteredChoices,
175
+ highlightedIndex,
176
+ getItemProps,
177
+ inputValue,
178
+ })}
179
+ </Paper>
180
+ </Grow>
181
+ )}
182
+ </Popper>
183
+ </>
184
+ );
185
+ };
@@ -0,0 +1,122 @@
1
+ export const choices = [
2
+ {
3
+ label: "Hazel, Direct Integration Producer",
4
+ value: "hazel, direct integration producer",
5
+ },
6
+ {
7
+ label: "Audra, District Functionality Facilitator",
8
+ value: "audra, district functionality facilitator",
9
+ },
10
+ {
11
+ label: "Emilie, Product Integration Assistant",
12
+ value: "emilie, product integration assistant",
13
+ },
14
+ {
15
+ label: "Kyla, Central Interactions Director",
16
+ value: "kyla, central interactions director",
17
+ },
18
+ {
19
+ label: "Vida, Senior Usability Producer",
20
+ value: "vida, senior usability producer",
21
+ },
22
+ {
23
+ label: "Lonie, Chief Infrastructure Director",
24
+ value: "lonie, chief infrastructure director",
25
+ },
26
+ {
27
+ label: "Zola, Lead Operations Producer",
28
+ value: "zola, lead operations producer",
29
+ },
30
+ {
31
+ label: "Angel, District Operations Orchestrator",
32
+ value: "angel, district operations orchestrator",
33
+ },
34
+ {
35
+ label: "Glenda, Lead Accountability Technician",
36
+ value: "glenda, lead accountability technician",
37
+ },
38
+ {
39
+ label: "Kayley, National Web Facilitator",
40
+ value: "kayley, national web facilitator",
41
+ },
42
+ {
43
+ label: "Kali, National Configuration Planner",
44
+ value: "kali, national configuration planner",
45
+ },
46
+ {
47
+ label: "Lilian, Internal Branding Planner",
48
+ value: "lilian, internal branding planner",
49
+ },
50
+ {
51
+ label: "Lamar, Dynamic Integration Executive",
52
+ value: "lamar, dynamic integration executive",
53
+ },
54
+ {
55
+ label: "Leonel, Central Brand Strategist",
56
+ value: "leonel, central brand strategist",
57
+ },
58
+ {
59
+ label: "Broderick, Chief Division Liaison",
60
+ value: "broderick, chief division liaison",
61
+ },
62
+ {
63
+ label: "Kavon, Future Marketing Representative",
64
+ value: "kavon, future marketing representative",
65
+ },
66
+ {
67
+ label: "Sydnee, Corporate Marketing Liaison",
68
+ value: "sydnee, corporate marketing liaison",
69
+ },
70
+ {
71
+ label: "Jett, Future Data Specialist",
72
+ value: "jett, future data specialist",
73
+ },
74
+ {
75
+ label: "Theresia, International Tactics Assistant",
76
+ value: "theresia, international tactics assistant",
77
+ },
78
+ {
79
+ label: "Cesar, Direct Mobility Director",
80
+ value: "cesar, direct mobility director",
81
+ },
82
+ {
83
+ label: "Madonna, Investor Assurance Executive",
84
+ value: "madonna, investor assurance executive",
85
+ },
86
+ {
87
+ label: "Ima, Internal Research Facilitator",
88
+ value: "ima, internal research facilitator",
89
+ },
90
+ {
91
+ label: "Joanne, Investor Identity Coordinator",
92
+ value: "joanne, investor identity coordinator",
93
+ },
94
+ {
95
+ label: "Gavin, Future Web Assistant",
96
+ value: "gavin, future web assistant",
97
+ },
98
+ {
99
+ label: "Maverick, Internal Optimization Assistant",
100
+ value: "maverick, internal optimization assistant",
101
+ },
102
+ {
103
+ label: "Hudson, Regional Branding Representative",
104
+ value: "hudson, regional branding representative",
105
+ },
106
+ {
107
+ label: "Brooklyn, Human Paradigm Producer",
108
+ value: "brooklyn, human paradigm producer",
109
+ },
110
+ {
111
+ label: "Gussie, Future Configuration Supervisor",
112
+ value: "gussie, future configuration supervisor",
113
+ },
114
+ {
115
+ label: "Michel, Corporate Tactics Orchestrator",
116
+ value: "michel, corporate tactics orchestrator",
117
+ },
118
+ {
119
+ label: "Maxime, Customer Mobility Analyst",
120
+ value: "maxime, customer mobility analyst",
121
+ },
122
+ ];
@@ -0,0 +1 @@
1
+ export * from "./MultipleValueAutocomplete";
@@ -0,0 +1,39 @@
1
+ import { makeStyles } from "../theme";
2
+
3
+ const useStyles = makeStyles(
4
+ (theme) => ({
5
+ dropdown: {
6
+ maxHeight: 220,
7
+ overflow: "scroll",
8
+ },
9
+ icon: {
10
+ position: "absolute",
11
+ bottom: 4,
12
+ right: 4,
13
+ },
14
+ inputContainer: {
15
+ padding: "23px 80px 10px 12px",
16
+ flexWrap: "wrap",
17
+ gap: theme.spacing(1),
18
+ },
19
+ inputContainerWithChips: {
20
+ paddingTop: 27,
21
+ },
22
+ input: {
23
+ height: "1.1875em",
24
+ minWidth: "3rem",
25
+ padding: 0,
26
+ },
27
+ loader: {
28
+ position: "absolute",
29
+ bottom: theme.spacing(1),
30
+ right: theme.spacing(6),
31
+ },
32
+ popper: {
33
+ marginTop: theme.spacing(1),
34
+ },
35
+ }),
36
+ { name: "MultipleValueAutocomplete" }
37
+ );
38
+
39
+ export default useStyles;
@@ -0,0 +1,172 @@
1
+ import {
2
+ useCombobox,
3
+ UseComboboxGetItemPropsOptions,
4
+ useMultipleSelection,
5
+ } from "downshift";
6
+ import { useCallback, useEffect, useMemo, useRef } from "react";
7
+
8
+ import { SyntheticChangeEvent } from "../../types/utils";
9
+ import { Choice } from "../Filter";
10
+ import { useTextWidth } from "../tools/useTextWidth";
11
+ import { mergeRefs } from "../utils/mergeRefs";
12
+
13
+ export interface UseMultipleValueAutocomplete {
14
+ choices: Choice[];
15
+ enableReinitialize?: boolean;
16
+ name?: string;
17
+ initialValue: Choice[];
18
+ onChange?: (event: SyntheticChangeEvent<string[]>) => void;
19
+ onInputChange?: (value: string) => void;
20
+ }
21
+
22
+ function useMultipleValueAutocomplete({
23
+ choices,
24
+ enableReinitialize,
25
+ initialValue,
26
+ name,
27
+ onChange,
28
+ onInputChange,
29
+ }: UseMultipleValueAutocomplete) {
30
+ const anchor = useRef<HTMLDivElement>();
31
+ const input = useRef<HTMLInputElement>();
32
+
33
+ const [inputWidth, setInputText] = useTextWidth(
34
+ window
35
+ .getComputedStyle(input.current ?? document.body, null)
36
+ .getPropertyValue("font")
37
+ );
38
+
39
+ const {
40
+ getSelectedItemProps,
41
+ getDropdownProps,
42
+ addSelectedItem,
43
+ removeSelectedItem,
44
+ selectedItems,
45
+ setSelectedItems,
46
+ } = useMultipleSelection({
47
+ initialSelectedItems: initialValue,
48
+ onSelectedItemsChange: ({ selectedItems }) => {
49
+ if (onChange) {
50
+ onChange({
51
+ target: {
52
+ name: name ?? "",
53
+ value: selectedItems!.map((choice) => choice.value),
54
+ },
55
+ });
56
+ }
57
+ },
58
+ });
59
+ const filteredChoices = useMemo(
60
+ () =>
61
+ choices.filter(
62
+ (choice) =>
63
+ !selectedItems.find(
64
+ (selectedChoice) => selectedChoice.value === choice.value
65
+ )
66
+ ),
67
+ [choices, selectedItems]
68
+ );
69
+
70
+ useEffect(() => {
71
+ if (enableReinitialize) setSelectedItems(initialValue);
72
+ }, [initialValue]);
73
+
74
+ const {
75
+ isOpen,
76
+ getToggleButtonProps,
77
+ getLabelProps,
78
+ getMenuProps,
79
+ getInputProps,
80
+ getComboboxProps,
81
+ highlightedIndex,
82
+ getItemProps: baseGetItemProps,
83
+ openMenu,
84
+ inputValue,
85
+ setInputValue,
86
+ selectItem,
87
+ } = useCombobox({
88
+ circularNavigation: false,
89
+ defaultHighlightedIndex: 0,
90
+ items: filteredChoices,
91
+ onInputValueChange: ({ inputValue }) => {
92
+ setInputText(inputValue || "");
93
+ if (onInputChange && inputValue) {
94
+ onInputChange(inputValue);
95
+ }
96
+ },
97
+ onSelectedItemChange: ({ selectedItem }) => {
98
+ if (selectedItem) {
99
+ addSelectedItem(selectedItem);
100
+ setInputValue("");
101
+ }
102
+ },
103
+ stateReducer: (_, actionAndChanges) => {
104
+ const { changes, type } = actionAndChanges;
105
+ switch (type) {
106
+ case useCombobox.stateChangeTypes.InputKeyDownEnter:
107
+ case useCombobox.stateChangeTypes.FunctionSelectItem:
108
+ const index = filteredChoices.findIndex(
109
+ (choice) => choice.value === changes.selectedItem?.value
110
+ );
111
+ return {
112
+ ...changes,
113
+ highlightedIndex: index > 0 ? index - 1 : 0,
114
+ isOpen: true,
115
+ };
116
+ }
117
+ return changes;
118
+ },
119
+ selectedItem: null,
120
+ itemToString: () => "",
121
+ });
122
+
123
+ // Downshift doesn't like portals like popper
124
+ // https://github.com/downshift-js/downshift/issues/287
125
+ const getItemProps = useCallback(
126
+ (options: UseComboboxGetItemPropsOptions<Choice>) => {
127
+ const baseProps = baseGetItemProps(options);
128
+
129
+ return {
130
+ ...baseProps,
131
+ onClick: () => selectItem(options.item),
132
+ selected: highlightedIndex === options.index,
133
+ };
134
+ },
135
+ [baseGetItemProps, highlightedIndex]
136
+ );
137
+
138
+ const labelProps = getLabelProps();
139
+ const { ref: comboboxDownshiftRef, ...comboboxProps } = getComboboxProps();
140
+ const { ref: downshiftRef, ...inputProps } = getInputProps({
141
+ ...getDropdownProps(),
142
+ onFocus: () => {
143
+ if (!isOpen) {
144
+ input.current?.select();
145
+ openMenu();
146
+ }
147
+ },
148
+ });
149
+ const menuProps = getMenuProps({}, { suppressRefError: true });
150
+
151
+ return {
152
+ anchor,
153
+ comboboxProps,
154
+ filteredChoices,
155
+ getItemProps,
156
+ getSelectedItemProps,
157
+ getToggleButtonProps,
158
+ highlightedIndex,
159
+ inputProps,
160
+ inputRef: mergeRefs(downshiftRef, input),
161
+ inputValue,
162
+ inputWidth,
163
+ isOpen,
164
+ labelProps,
165
+ menuProps,
166
+ ref: mergeRefs(comboboxDownshiftRef, anchor),
167
+ removeSelectedItem,
168
+ selectedItems,
169
+ };
170
+ }
171
+
172
+ export default useMultipleValueAutocomplete;
@@ -1,7 +1,6 @@
1
1
  import React from "react";
2
2
 
3
3
  import { Button } from "..";
4
- import { useActionBar } from "../ActionBar";
5
4
  import { ActionBar } from "../ActionBar/ActionBar";
6
5
  import {
7
6
  ConfirmButton,
@@ -21,6 +20,7 @@ export interface SavebarProps {
21
20
  state: ConfirmButtonTransitionState;
22
21
  labels: SavebarLabels;
23
22
  tooltips?: SavebarTooltips;
23
+ className?: string;
24
24
  onCancel: () => void;
25
25
  onDelete?: () => void;
26
26
  onSubmit: () => void;
@@ -31,15 +31,15 @@ export const Savebar: React.FC<SavebarProps> = ({
31
31
  labels,
32
32
  tooltips,
33
33
  state,
34
+ className,
34
35
  onCancel,
35
36
  onDelete,
36
37
  onSubmit,
37
38
  }) => {
38
39
  const classes = useStyles();
39
- const { setDocked } = useActionBar();
40
40
 
41
41
  return (
42
- <ActionBar state={state} disabled={disabled}>
42
+ <ActionBar state={state} disabled={disabled} className={className}>
43
43
  {!!onDelete && (
44
44
  <ButtonTooltipDecorator tooltip={tooltips?.delete}>
45
45
  <Button
@@ -70,7 +70,6 @@ export const Savebar: React.FC<SavebarProps> = ({
70
70
  onClick={onSubmit}
71
71
  transitionState={state}
72
72
  data-test="button-bar-confirm"
73
- onTransitionToDefault={() => setDocked(true)}
74
73
  />
75
74
  </ButtonTooltipDecorator>
76
75
  </ActionBar>
@@ -9,15 +9,27 @@ import React from "react";
9
9
  import SVG from "react-inlinesvg";
10
10
 
11
11
  import { makeStyles } from "../theme";
12
- import { SidebarMenuItem } from "./types";
12
+ import { CustomLinkComponent, SidebarMenuItem } from "./types";
13
+ import { getLinkComponent, getLinkProps } from "./utils";
13
14
 
14
- export interface MenuItemProps {
15
+ export interface MenuItemCommonProps {
15
16
  activeId: string;
16
17
  isMenuShrunk: boolean;
17
18
  menuItem: SidebarMenuItem;
18
- onClick: (menuItem: SidebarMenuItem) => void;
19
19
  }
20
20
 
21
+ export type MenuItemProps = MenuItemCommonProps &
22
+ (
23
+ | {
24
+ onClick: (menuItem: SidebarMenuItem) => void;
25
+ linkComponent?: never;
26
+ }
27
+ | {
28
+ onClick?: never;
29
+ linkComponent: CustomLinkComponent;
30
+ }
31
+ );
32
+
21
33
  export const menuWidth = 210;
22
34
  export const shrunkMenuWidth = 72;
23
35
 
@@ -139,23 +151,33 @@ export const MenuItem: React.FC<MenuItemProps> = ({
139
151
  menuItem,
140
152
  isMenuShrunk,
141
153
  onClick,
154
+ linkComponent,
142
155
  }) => {
143
156
  const classes = useStyles({});
144
157
  const [open, setOpen] = React.useState(false);
145
- const anchor = React.useRef<HTMLDivElement>(null);
158
+ const anchor = React.useRef<any>(null);
146
159
 
147
160
  const handleClick = (event: React.MouseEvent, menuItem: SidebarMenuItem) => {
148
161
  event.stopPropagation();
149
162
  if (menuItem.children) {
150
163
  setOpen(true);
151
164
  } else {
152
- onClick(menuItem);
165
+ if (onClick) {
166
+ onClick(menuItem);
167
+ }
168
+
169
+ if (menuItem.onClick) {
170
+ menuItem.onClick();
171
+ }
172
+
153
173
  setOpen(false);
154
174
  }
155
175
  };
156
176
 
177
+ const RootNavComponent = menuItem.children ? "div" : linkComponent ?? "div";
178
+
157
179
  return (
158
- <div
180
+ <RootNavComponent
159
181
  className={clsx(classes.root, {
160
182
  [classes.rootOpen]: open,
161
183
  [classes.rootActive]: [
@@ -165,9 +187,10 @@ export const MenuItem: React.FC<MenuItemProps> = ({
165
187
  [classes.rootExpanded]: !isMenuShrunk,
166
188
  })}
167
189
  ref={anchor}
168
- onClick={(event) => handleClick(event, menuItem)}
190
+ onClick={(event: React.MouseEvent) => handleClick(event, menuItem)}
191
+ {...getLinkProps(menuItem)}
169
192
  >
170
- <button
193
+ <span
171
194
  className={classes.menuItemBtn}
172
195
  data-test="menu-item-label"
173
196
  data-test-id={menuItem.id}
@@ -184,7 +207,7 @@ export const MenuItem: React.FC<MenuItemProps> = ({
184
207
  >
185
208
  {menuItem.label}
186
209
  </Typography>
187
- </button>
210
+ </span>
188
211
  {menuItem.children && (
189
212
  <Popper
190
213
  className={clsx(classes.popper, {
@@ -199,14 +222,12 @@ export const MenuItem: React.FC<MenuItemProps> = ({
199
222
  <Paper className={classes.paper}>
200
223
  {menuItem.children.map((subMenuItem) => {
201
224
  if (subMenuItem.url || subMenuItem.children) {
202
- const linkProps = subMenuItem.external
203
- ? { href: subMenuItem.url, target: "_blank" }
204
- : {};
225
+ const linkProps = getLinkProps(subMenuItem);
205
226
 
206
227
  return (
207
228
  <MuiMenuItem
208
229
  aria-label={subMenuItem.ariaLabel}
209
- component={subMenuItem.external ? "a" : "button"}
230
+ component={getLinkComponent(subMenuItem, linkComponent)}
210
231
  className={clsx(classes.label, classes.subMenuLabel)}
211
232
  key={subMenuItem.url}
212
233
  onClick={(event: React.MouseEvent) =>
@@ -236,7 +257,7 @@ export const MenuItem: React.FC<MenuItemProps> = ({
236
257
  </ClickAwayListener>
237
258
  </Popper>
238
259
  )}
239
- </div>
260
+ </RootNavComponent>
240
261
  );
241
262
  };
242
263