@canonical/react-components 0.46.0 → 0.47.1

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.
@@ -13,7 +13,7 @@ export declare enum Label {
13
13
  */
14
14
  export type Props<L> = PropsWithSpread<{
15
15
  /**
16
- * Whether the menu should adjust to fit in the screen.
16
+ * Whether the menu should adjust its horizontal position to fit on the screen.
17
17
  */
18
18
  autoAdjust?: boolean;
19
19
  /**
@@ -57,13 +57,17 @@ export type Props<L> = PropsWithSpread<{
57
57
  */
58
58
  onToggleMenu?: (isOpen: boolean) => void | null;
59
59
  /**
60
- * The position of the menu.
60
+ * The horizontal position of the menu.
61
61
  */
62
62
  position?: Position | null;
63
63
  /**
64
64
  * An element to make the menu relative to.
65
65
  */
66
66
  positionNode?: HTMLElement | null;
67
+ /**
68
+ * Whether the dropdown should scroll if it is too long to fit on the screen.
69
+ */
70
+ scrollOverflow?: boolean;
67
71
  /**
68
72
  * The appearance of the toggle button.
69
73
  */
@@ -115,5 +119,5 @@ export type Props<L> = PropsWithSpread<{
115
119
  * @param [toggleLabelFirst=true] - Whether the toggle lable or icon should appear first.
116
120
  * @param [visible=false] - Whether the menu should be visible.
117
121
  */
118
- declare const ContextualMenu: <L>({ autoAdjust, children, className, closeOnEsc, closeOnOutsideClick, constrainPanelWidth, dropdownClassName, dropdownProps, hasToggleIcon, links, onToggleMenu, position, positionNode, toggleAppearance, toggleClassName, toggleDisabled, toggleLabel, toggleLabelFirst, toggleProps, visible, ...wrapperProps }: Props<L>) => JSX.Element;
122
+ declare const ContextualMenu: <L>({ autoAdjust, children, className, closeOnEsc, closeOnOutsideClick, constrainPanelWidth, dropdownClassName, dropdownProps, hasToggleIcon, links, onToggleMenu, position, positionNode, scrollOverflow, toggleAppearance, toggleClassName, toggleDisabled, toggleLabel, toggleLabelFirst, toggleProps, visible, ...wrapperProps }: Props<L>) => JSX.Element;
119
123
  export default ContextualMenu;
@@ -12,7 +12,7 @@ var _hooks = require("../../hooks");
12
12
  var _Button = _interopRequireDefault(require("../Button"));
13
13
  var _ContextualMenuDropdown = _interopRequireDefault(require("./ContextualMenuDropdown"));
14
14
  var _useId = require("../../hooks/useId");
15
- var _excluded = ["autoAdjust", "children", "className", "closeOnEsc", "closeOnOutsideClick", "constrainPanelWidth", "dropdownClassName", "dropdownProps", "hasToggleIcon", "links", "onToggleMenu", "position", "positionNode", "toggleAppearance", "toggleClassName", "toggleDisabled", "toggleLabel", "toggleLabelFirst", "toggleProps", "visible"];
15
+ var _excluded = ["autoAdjust", "children", "className", "closeOnEsc", "closeOnOutsideClick", "constrainPanelWidth", "dropdownClassName", "dropdownProps", "hasToggleIcon", "links", "onToggleMenu", "position", "positionNode", "scrollOverflow", "toggleAppearance", "toggleClassName", "toggleDisabled", "toggleLabel", "toggleLabelFirst", "toggleProps", "visible"];
16
16
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
17
17
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
18
18
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -103,6 +103,7 @@ var ContextualMenu = function ContextualMenu(_ref) {
103
103
  _ref$position = _ref.position,
104
104
  position = _ref$position === void 0 ? "right" : _ref$position,
105
105
  positionNode = _ref.positionNode,
106
+ scrollOverflow = _ref.scrollOverflow,
106
107
  toggleAppearance = _ref.toggleAppearance,
107
108
  toggleClassName = _ref.toggleClassName,
108
109
  toggleDisabled = _ref.toggleDisabled,
@@ -238,6 +239,7 @@ var ContextualMenu = function ContextualMenu(_ref) {
238
239
  position: position,
239
240
  positionCoords: positionCoords,
240
241
  positionNode: getPositionNode(wrapper.current, positionNode),
242
+ scrollOverflow: scrollOverflow,
241
243
  setAdjustedPosition: setAdjustedPosition,
242
244
  wrapperClass: wrapperClass
243
245
  }, dropdownProps))));
@@ -28,6 +28,7 @@ export type Props<L = null> = {
28
28
  position?: Position;
29
29
  positionCoords?: ClientRect;
30
30
  positionNode?: HTMLElement;
31
+ scrollOverflow?: boolean;
31
32
  setAdjustedPosition?: (position: Position) => void;
32
33
  wrapperClass?: string;
33
34
  } & HTMLProps<HTMLSpanElement>;
@@ -38,5 +39,5 @@ export type Props<L = null> = {
38
39
  * @return The new position.
39
40
  */
40
41
  export declare const adjustForWindow: (position: Position, fitsWindow: WindowFitment) => Position;
41
- declare const ContextualMenuDropdown: <L>({ adjustedPosition, autoAdjust, closePortal, constrainPanelWidth, dropdownClassName, dropdownContent, id, isOpen, links, position, positionCoords, positionNode, setAdjustedPosition, wrapperClass, ...props }: Props<L>) => JSX.Element;
42
+ declare const ContextualMenuDropdown: <L>({ adjustedPosition, autoAdjust, closePortal, constrainPanelWidth, dropdownClassName, dropdownContent, id, isOpen, links, position, positionCoords, positionNode, scrollOverflow, setAdjustedPosition, wrapperClass, ...props }: Props<L>) => JSX.Element;
42
43
  export default ContextualMenuDropdown;
@@ -9,7 +9,7 @@ var _react = _interopRequireWildcard(require("react"));
9
9
  var _hooks = require("../../../hooks");
10
10
  var _Button = _interopRequireDefault(require("../../Button"));
11
11
  var _excluded = ["children", "className", "onClick"],
12
- _excluded2 = ["adjustedPosition", "autoAdjust", "closePortal", "constrainPanelWidth", "dropdownClassName", "dropdownContent", "id", "isOpen", "links", "position", "positionCoords", "positionNode", "setAdjustedPosition", "wrapperClass"];
12
+ _excluded2 = ["adjustedPosition", "autoAdjust", "closePortal", "constrainPanelWidth", "dropdownClassName", "dropdownContent", "id", "isOpen", "links", "position", "positionCoords", "positionNode", "scrollOverflow", "setAdjustedPosition", "wrapperClass"];
13
13
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
14
14
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
15
15
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -144,6 +144,7 @@ var ContextualMenuDropdown = function ContextualMenuDropdown(_ref) {
144
144
  position = _ref.position,
145
145
  positionCoords = _ref.positionCoords,
146
146
  positionNode = _ref.positionNode,
147
+ scrollOverflow = _ref.scrollOverflow,
147
148
  setAdjustedPosition = _ref.setAdjustedPosition,
148
149
  wrapperClass = _ref.wrapperClass,
149
150
  props = _objectWithoutProperties(_ref, _excluded2);
@@ -152,6 +153,10 @@ var ContextualMenuDropdown = function ContextualMenuDropdown(_ref) {
152
153
  _useState2 = _slicedToArray(_useState, 2),
153
154
  positionStyle = _useState2[0],
154
155
  setPositionStyle = _useState2[1];
156
+ var _useState3 = (0, _react.useState)(),
157
+ _useState4 = _slicedToArray(_useState3, 2),
158
+ maxHeight = _useState4[0],
159
+ setMaxHeight = _useState4[1];
155
160
 
156
161
  // Update the styles to position the menu.
157
162
  var updatePositionStyle = (0, _react.useCallback)(function () {
@@ -160,11 +165,16 @@ var ContextualMenuDropdown = function ContextualMenuDropdown(_ref) {
160
165
 
161
166
  // Update the position when the window fitment info changes.
162
167
  var onUpdateWindowFitment = (0, _react.useCallback)(function (fitsWindow) {
163
- setAdjustedPosition(adjustForWindow(position, fitsWindow));
164
- }, [position, setAdjustedPosition]);
168
+ if (autoAdjust) {
169
+ setAdjustedPosition(adjustForWindow(position, fitsWindow));
170
+ }
171
+ if (scrollOverflow) {
172
+ setMaxHeight(fitsWindow.fromBottom.spaceBelow - 16);
173
+ }
174
+ }, [autoAdjust, position, scrollOverflow, setAdjustedPosition]);
165
175
 
166
- // Handle adjusting the position of the dropdown so that it remains on screen.
167
- (0, _hooks.useWindowFitment)(dropdown.current, positionNode, onUpdateWindowFitment, 0, isOpen && autoAdjust);
176
+ // Handle adjusting the horizontal position and scrolling of the dropdown so that it remains on screen.
177
+ (0, _hooks.useWindowFitment)(dropdown.current, positionNode, onUpdateWindowFitment, 0, isOpen && (autoAdjust || scrollOverflow));
168
178
 
169
179
  // Update the styles when the position changes.
170
180
  (0, _react.useEffect)(function () {
@@ -179,11 +189,15 @@ var ContextualMenuDropdown = function ContextualMenuDropdown(_ref) {
179
189
  "aria-hidden": isOpen ? "false" : "true",
180
190
  "aria-label": Label.Dropdown,
181
191
  ref: dropdown,
182
- style: constrainPanelWidth && positionStyle !== null && positionStyle !== void 0 && positionStyle.width ? {
192
+ style: _objectSpread(_objectSpread({}, constrainPanelWidth && positionStyle !== null && positionStyle !== void 0 && positionStyle.width ? {
183
193
  width: positionStyle.width,
184
194
  minWidth: 0,
185
195
  maxWidth: "none"
186
- } : null
196
+ } : {}), scrollOverflow ? {
197
+ maxHeight: maxHeight,
198
+ minHeight: "2rem",
199
+ overflowX: "auto"
200
+ } : {})
187
201
  }, dropdownContent ? typeof dropdownContent === "function" ? dropdownContent(closePortal) : dropdownContent : links.map(function (item, i) {
188
202
  if (Array.isArray(item)) {
189
203
  return /*#__PURE__*/_react.default.createElement("span", {
@@ -45,7 +45,13 @@ var Modal = function Modal(_ref) {
45
45
  }
46
46
  };
47
47
  var handleEscKey = function handleEscKey(e) {
48
- e.nativeEvent.stopImmediatePropagation();
48
+ if ("nativeEvent" in e && e.nativeEvent.stopImmediatePropagation) {
49
+ e.nativeEvent.stopImmediatePropagation();
50
+ } else if ("stopImmediatePropagation" in e) {
51
+ e.stopImmediatePropagation();
52
+ } else if (e.stopPropagation) {
53
+ e.stopPropagation();
54
+ }
49
55
  close();
50
56
  };
51
57
  var keyListenersMap = new Map([["Escape", handleEscKey], ["Tab", handleTabKey]]);
@@ -61,7 +67,7 @@ var Modal = function Modal(_ref) {
61
67
  focusIndex = 1;
62
68
  }
63
69
  (_focusableModalElemen = focusableModalElements.current[focusIndex]) === null || _focusableModalElemen === void 0 ? void 0 : _focusableModalElemen.focus();
64
- }, []);
70
+ }, [close]);
65
71
  (0, _react.useEffect)(function () {
66
72
  var keyDown = function keyDown(e) {
67
73
  var listener = keyListenersMap.get(e.code);
@@ -27,16 +27,16 @@ var generatePaginationItems = function generatePaginationItems(pageNumbers, curr
27
27
  // on page 1, also show pages 2, 3 and 4
28
28
  if (currentPage === 1) {
29
29
  start = 1;
30
- end = currentPage + 3;
30
+ end = Math.min(lastPage - 1, currentPage + 3);
31
31
  }
32
32
  // on page 2, show page 1, and also pages 3, and 4
33
33
  if (currentPage === 2) {
34
34
  start = 1;
35
- end = currentPage + 2;
35
+ end = Math.min(lastPage - 1, currentPage + 3);
36
36
  }
37
37
  // on the last page and page before last, also show the 3 previous pages
38
38
  if (currentPage === lastPage || currentPage === lastPage - 1) {
39
- start = lastPage - 4;
39
+ start = Math.max(1, lastPage - 4);
40
40
  end = lastPage - 1;
41
41
  }
42
42
  visiblePages = pageNumbers.slice(start, end);
@@ -54,7 +54,7 @@ var generatePaginationItems = function generatePaginationItems(pageNumbers, curr
54
54
  return changePage(1);
55
55
  }
56
56
  }));
57
- if (![1, 2, 3].includes(currentPage)) {
57
+ if (!visiblePages.includes(2)) {
58
58
  items.push( /*#__PURE__*/_react.default.createElement(PaginationItemSeparator, {
59
59
  key: "sep1"
60
60
  }));
@@ -72,7 +72,7 @@ var generatePaginationItems = function generatePaginationItems(pageNumbers, curr
72
72
  }));
73
73
  if (truncated) {
74
74
  // render last in sequence
75
- if (![lastPage, lastPage - 1, lastPage - 2].includes(currentPage)) {
75
+ if (!visiblePages.includes(lastPage - 1)) {
76
76
  items.push( /*#__PURE__*/_react.default.createElement(PaginationItemSeparator, {
77
77
  key: "sep2"
78
78
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonical/react-components",
3
- "version": "0.46.0",
3
+ "version": "0.47.1",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.js",
6
6
  "author": "Huw Wilkins <huw.wilkins@canonical.com>",
@@ -22,11 +22,11 @@
22
22
  },
23
23
  "homepage": "https://canonical.github.io/react-components",
24
24
  "devDependencies": {
25
- "@babel/cli": "7.22.5",
26
- "@babel/eslint-parser": "7.22.5",
27
- "@babel/preset-typescript": "7.22.5",
28
- "@percy/cli": "1.26.1",
29
- "@percy/storybook": "4.3.6",
25
+ "@babel/cli": "7.23.0",
26
+ "@babel/eslint-parser": "7.22.15",
27
+ "@babel/preset-typescript": "7.23.2",
28
+ "@percy/cli": "1.27.3",
29
+ "@percy/storybook": "4.3.7",
30
30
  "@storybook/addon-a11y": "6.5.16",
31
31
  "@storybook/addon-controls": "6.5.16",
32
32
  "@storybook/addon-docs": "6.5.16",
@@ -36,69 +36,69 @@
36
36
  "@storybook/react": "6.5.16",
37
37
  "@storybook/theming": "6.5.16",
38
38
  "@testing-library/cypress": "9.0.0",
39
- "@testing-library/dom": "9.3.1",
40
- "@testing-library/jest-dom": "5.16.5",
39
+ "@testing-library/dom": "9.3.3",
40
+ "@testing-library/jest-dom": "5.17.0",
41
41
  "@testing-library/react": "14.0.0",
42
- "@testing-library/user-event": "14.4.3",
42
+ "@testing-library/user-event": "14.5.1",
43
43
  "@types/react-router-dom": "5.3.3",
44
- "@typescript-eslint/eslint-plugin": "5.60.1",
45
- "@typescript-eslint/parser": "5.60.1",
44
+ "@typescript-eslint/eslint-plugin": "5.62.0",
45
+ "@typescript-eslint/parser": "5.62.0",
46
46
  "babel-jest": "27.5.1",
47
- "babel-loader": "9.1.2",
47
+ "babel-loader": "9.1.3",
48
48
  "babel-plugin-module-resolver": "5.0.0",
49
49
  "babel-plugin-typescript-to-proptypes": "2.1.0",
50
- "concurrently": "8.2.0",
50
+ "concurrently": "8.2.1",
51
51
  "css-loader": "6.8.1",
52
- "cypress": "12.16.0",
52
+ "cypress": "12.17.4",
53
53
  "deepmerge": "4.3.1",
54
- "eslint": "8.43.0",
55
- "eslint-config-prettier": "8.8.0",
54
+ "eslint": "8.51.0",
55
+ "eslint-config-prettier": "8.10.0",
56
56
  "eslint-config-react-app": "7.0.1",
57
- "eslint-plugin-cypress": "2.13.3",
57
+ "eslint-plugin-cypress": "2.15.1",
58
58
  "eslint-plugin-flowtype": "8.0.3",
59
- "eslint-plugin-import": "2.27.5",
59
+ "eslint-plugin-import": "2.28.1",
60
60
  "eslint-plugin-jsx-a11y": "6.7.1",
61
61
  "eslint-plugin-prettier": "4.2.1",
62
- "eslint-plugin-react": "7.32.2",
62
+ "eslint-plugin-react": "7.33.2",
63
63
  "eslint-plugin-react-hooks": "4.6.0",
64
- "eslint-plugin-testing-library": "5.11.0",
64
+ "eslint-plugin-testing-library": "5.11.1",
65
65
  "jest": "27.5.1",
66
66
  "npm-package-json-lint": "5.4.2",
67
67
  "prettier": "2.8.8",
68
68
  "react": "18.2.0",
69
69
  "react-docgen-typescript-loader": "3.7.2",
70
70
  "react-dom": "18.2.0",
71
- "sass": "1.63.6",
71
+ "sass": "1.69.3",
72
72
  "sass-loader": "10.4.1",
73
73
  "style-loader": "3.3.3",
74
- "stylelint": "15.10.1",
74
+ "stylelint": "15.11.0",
75
75
  "stylelint-config-prettier": "9.0.5",
76
76
  "stylelint-config-recommended-scss": "5.0.2",
77
77
  "stylelint-order": "5.0.0",
78
78
  "stylelint-prettier": "2.0.0",
79
79
  "ts-jest": "27.1.5",
80
- "tsc-alias": "1.8.6",
80
+ "tsc-alias": "1.8.8",
81
81
  "typescript": "4.9.5",
82
- "vanilla-framework": "4.2.0",
82
+ "vanilla-framework": "4.5.0",
83
83
  "wait-on": "5.3.0",
84
- "webpack": "5.88.0"
84
+ "webpack": "5.89.0"
85
85
  },
86
86
  "dependencies": {
87
87
  "@types/jest": "27.5.2",
88
- "@types/node": "18.16.18",
89
- "@types/react": "18.2.14",
90
- "@types/react-dom": "18.2.6",
91
- "@types/react-table": "7.7.14",
88
+ "@types/node": "18.18.5",
89
+ "@types/react": "18.2.28",
90
+ "@types/react-dom": "18.2.13",
91
+ "@types/react-table": "7.7.16",
92
92
  "classnames": "2.3.2",
93
93
  "nanoid": "3.3.6",
94
94
  "prop-types": "15.8.1",
95
- "react-router-dom": "6.6.1",
95
+ "react-router-dom": "6.17.0",
96
96
  "react-table": "7.8.0",
97
97
  "react-useportal": "1.0.18"
98
98
  },
99
99
  "resolutions": {
100
- "@types/react": "18.2.14",
101
- "@types/react-dom": "18.2.6",
100
+ "@types/react": "18.2.28",
101
+ "@types/react-dom": "18.2.13",
102
102
  "postcss": "^8.3.11"
103
103
  },
104
104
  "peerDependencies": {
@@ -130,11 +130,7 @@
130
130
  "unlink-packages": "yarn unlink && cd node_modules/react && yarn unlink && cd ../react-dom && yarn unlink",
131
131
  "cypress:test": "wait-on http://localhost:${PORT:-9009} && cypress run --env port=${PORT:-9009}",
132
132
  "cypress:run": "cypress run --env port=${PORT:-9009}",
133
- "cypress:open": "cypress open --env port=${PORT:-9009}",
134
- "prepare-release:major": "./prepare-release major",
135
- "prepare-release:minor": "./prepare-release minor",
136
- "prepare-release:patch": "./prepare-release patch",
137
- "prepare-release": "yarn run prepare-release:minor"
133
+ "cypress:open": "cypress open --env port=${PORT:-9009}"
138
134
  },
139
135
  "eslintConfig": {
140
136
  "extends": "react-app"