@canonical/react-components 0.47.3 → 0.48.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.
package/README.md CHANGED
@@ -1,13 +1,10 @@
1
1
  # React components for Vanilla Framework
2
+ ![CI](https://github.com/canonical/react-components/workflows/CI/badge.svg?branch=main)
2
3
 
3
4
  This is a collection of components designed to be the way to consume [Vanilla Framework](http://vanillaframework.io) when using React.
4
5
 
5
- ## How to use the components
6
-
7
- See the [component docs](https://canonical.github.io/react-components/) for usage instructions.
6
+ **[Storybook](https://canonical.github.io/react-components/)** contains component docs with usage instructions.
8
7
 
9
- ![CI](https://github.com/canonical/react-components/workflows/CI/badge.svg?branch=main)
10
- ![Cypress chrome headless](https://github.com/canonical/react-components/workflows/Cypress%20chrome%20headless/badge.svg)
11
8
 
12
9
  ## Requirements
13
10
 
@@ -30,7 +30,7 @@ let Label = exports.Label = /*#__PURE__*/function (Label) {
30
30
  * @param positionCoords - The coordinates of the position node.
31
31
  * @param constrainPanelWidth - Whether the menu width should be constrained to the position width.
32
32
  */
33
- const getPositionStyle = (position, positionCoords, constrainPanelWidth) => {
33
+ const getPositionStyle = (position, verticalPosition, positionCoords, constrainPanelWidth) => {
34
34
  if (!positionCoords) {
35
35
  return null;
36
36
  }
@@ -40,7 +40,7 @@ const getPositionStyle = (position, positionCoords, constrainPanelWidth) => {
40
40
  top,
41
41
  width
42
42
  } = positionCoords;
43
- const topPos = top + height + (window.scrollY || 0);
43
+ const topPos = verticalPosition === "bottom" ? top + height + (window.scrollY || 0) : top + (window.scrollY || 0);
44
44
  let leftPos = left;
45
45
  switch (position) {
46
46
  case "left":
@@ -120,6 +120,20 @@ const generateLink = (link, key, handleClose) => {
120
120
  } : null
121
121
  }, props), children);
122
122
  };
123
+ const getClosestScrollableParent = node => {
124
+ let currentNode = node;
125
+ while (currentNode && currentNode !== document.body) {
126
+ const {
127
+ overflowY,
128
+ overflowX
129
+ } = window.getComputedStyle(currentNode);
130
+ if (["auto", "scroll", "overlay"].includes(overflowY) && ["auto", "scroll", "overlay"].includes(overflowX)) {
131
+ return currentNode;
132
+ }
133
+ currentNode = currentNode.parentElement;
134
+ }
135
+ return document.body;
136
+ };
123
137
  const ContextualMenuDropdown = _ref => {
124
138
  let {
125
139
  adjustedPosition,
@@ -140,23 +154,46 @@ const ContextualMenuDropdown = _ref => {
140
154
  ...props
141
155
  } = _ref;
142
156
  const dropdown = (0, _react.useRef)();
143
- const [positionStyle, setPositionStyle] = (0, _react.useState)(getPositionStyle(adjustedPosition, positionCoords, constrainPanelWidth));
157
+ const [verticalPosition, setVerticalPosition] = (0, _react.useState)("bottom");
158
+ const [positionStyle, setPositionStyle] = (0, _react.useState)(getPositionStyle(adjustedPosition, verticalPosition, positionCoords, constrainPanelWidth));
144
159
  const [maxHeight, setMaxHeight] = (0, _react.useState)();
145
-
146
160
  // Update the styles to position the menu.
147
161
  const updatePositionStyle = (0, _react.useCallback)(() => {
148
- setPositionStyle(getPositionStyle(adjustedPosition, positionCoords, constrainPanelWidth));
149
- }, [adjustedPosition, positionCoords, constrainPanelWidth]);
162
+ setPositionStyle(getPositionStyle(adjustedPosition, verticalPosition, positionCoords, constrainPanelWidth));
163
+ }, [adjustedPosition, positionCoords, verticalPosition, constrainPanelWidth]);
164
+ const updateVerticalPosition = (0, _react.useCallback)(() => {
165
+ if (!positionNode) {
166
+ return null;
167
+ }
168
+ const scrollableParent = getClosestScrollableParent(positionNode);
169
+ if (!scrollableParent) {
170
+ return null;
171
+ }
172
+ const scrollableParentRect = scrollableParent.getBoundingClientRect();
173
+ const rect = positionNode.getBoundingClientRect();
174
+
175
+ // Calculate the rect in relation to the scrollableParent
176
+ const relativeRect = {
177
+ top: rect.top - scrollableParentRect.top,
178
+ bottom: rect.bottom - scrollableParentRect.top,
179
+ height: rect.height
180
+ };
181
+ const spaceBelow = scrollableParentRect.height - relativeRect.bottom;
182
+ const spaceAbove = relativeRect.top;
183
+ const dropdownHeight = relativeRect.height;
184
+ setVerticalPosition(spaceBelow >= dropdownHeight || spaceBelow > spaceAbove ? "bottom" : "top");
185
+ }, [positionNode]);
150
186
 
151
187
  // Update the position when the window fitment info changes.
152
188
  const onUpdateWindowFitment = (0, _react.useCallback)(fitsWindow => {
153
189
  if (autoAdjust) {
154
190
  setAdjustedPosition(adjustForWindow(position, fitsWindow));
191
+ updateVerticalPosition();
155
192
  }
156
193
  if (scrollOverflow) {
157
194
  setMaxHeight(fitsWindow.fromBottom.spaceBelow - 16);
158
195
  }
159
- }, [autoAdjust, position, scrollOverflow, setAdjustedPosition]);
196
+ }, [autoAdjust, position, scrollOverflow, setAdjustedPosition, updateVerticalPosition]);
160
197
 
161
198
  // Handle adjusting the horizontal position and scrolling of the dropdown so that it remains on screen.
162
199
  (0, _hooks.useWindowFitment)(dropdown.current, positionNode, onUpdateWindowFitment, 0, isOpen && (autoAdjust || scrollOverflow));
@@ -165,6 +202,9 @@ const ContextualMenuDropdown = _ref => {
165
202
  (0, _react.useEffect)(() => {
166
203
  updatePositionStyle();
167
204
  }, [adjustedPosition, updatePositionStyle]);
205
+ (0, _react.useEffect)(() => {
206
+ updateVerticalPosition();
207
+ }, [updateVerticalPosition]);
168
208
  return (
169
209
  /*#__PURE__*/
170
210
  // Vanilla Framework uses .p-contextual-menu parent modifier classnames to determine the correct position of the .p-contextual-menu__dropdown dropdown (left, center, right).
@@ -188,6 +228,9 @@ const ContextualMenuDropdown = _ref => {
188
228
  maxHeight,
189
229
  minHeight: "2rem",
190
230
  overflowX: "auto"
231
+ } : {}),
232
+ ...(verticalPosition === "top" ? {
233
+ bottom: "0"
191
234
  } : {})
192
235
  }
193
236
  }, props), dropdownContent ? typeof dropdownContent === "function" ? dropdownContent(handleClose) : dropdownContent : links.map((item, i) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonical/react-components",
3
- "version": "0.47.3",
3
+ "version": "0.48.0",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.js",
6
6
  "author": "Huw Wilkins <huw.wilkins@canonical.com>",
@@ -26,7 +26,9 @@
26
26
  "@babel/eslint-parser": "7.23.3",
27
27
  "@babel/preset-typescript": "7.23.3",
28
28
  "@percy/cli": "1.27.6",
29
- "@percy/storybook": "4.3.7",
29
+ "@percy/storybook": "5.0.1",
30
+ "@semantic-release/changelog": "6.0.3",
31
+ "@semantic-release/git": "10.0.1",
30
32
  "@storybook/addon-a11y": "7.6.7",
31
33
  "@storybook/addon-essentials": "7.6.7",
32
34
  "@storybook/addon-interactions": "7.6.7",
@@ -36,63 +38,65 @@
36
38
  "@storybook/blocks": "7.6.7",
37
39
  "@storybook/react": "7.6.7",
38
40
  "@storybook/react-webpack5": "7.6.7",
39
- "@testing-library/cypress": "9.0.0",
41
+ "@testing-library/cypress": "10.0.1",
40
42
  "@testing-library/dom": "9.3.3",
41
- "@testing-library/jest-dom": "5.17.0",
43
+ "@testing-library/jest-dom": "6.2.1",
42
44
  "@testing-library/react": "14.1.2",
43
45
  "@testing-library/user-event": "14.5.2",
44
- "@typescript-eslint/eslint-plugin": "5.62.0",
45
- "@typescript-eslint/parser": "5.62.0",
46
- "babel-jest": "27.5.1",
46
+ "@typescript-eslint/eslint-plugin": "6.19.1",
47
+ "@typescript-eslint/parser": "6.19.1",
48
+ "babel-jest": "29.7.0",
47
49
  "babel-loader": "9.1.3",
48
50
  "babel-plugin-module-resolver": "5.0.0",
49
51
  "babel-plugin-typescript-to-proptypes": "2.1.0",
50
52
  "concurrently": "8.2.2",
51
53
  "css-loader": "6.8.1",
52
- "cypress": "12.17.4",
54
+ "cypress": "13.6.3",
53
55
  "deepmerge": "4.3.1",
54
56
  "eslint": "8.56.0",
55
- "eslint-config-prettier": "8.10.0",
57
+ "eslint-config-prettier": "9.1.0",
56
58
  "eslint-config-react-app": "7.0.1",
57
59
  "eslint-plugin-cypress": "2.15.1",
58
60
  "eslint-plugin-flowtype": "8.0.3",
59
61
  "eslint-plugin-import": "2.29.1",
60
62
  "eslint-plugin-jsx-a11y": "6.8.0",
61
- "eslint-plugin-prettier": "4.2.1",
63
+ "eslint-plugin-prettier": "5.1.3",
62
64
  "eslint-plugin-react": "7.33.2",
63
65
  "eslint-plugin-react-hooks": "4.6.0",
64
66
  "eslint-plugin-storybook": "0.6.15",
65
- "eslint-plugin-testing-library": "5.11.1",
66
- "jest": "27.5.1",
67
- "npm-package-json-lint": "5.4.2",
68
- "prettier": "2.8.8",
67
+ "eslint-plugin-testing-library": "6.2.0",
68
+ "jest": "29.7.0",
69
+ "npm-package-json-lint": "7.1.0",
70
+ "prettier": "3.2.4",
69
71
  "react": "18.2.0",
70
72
  "react-docgen-typescript-loader": "3.7.2",
71
73
  "react-dom": "18.2.0",
72
74
  "sass": "1.69.7",
73
- "sass-loader": "10.5.1",
75
+ "sass-loader": "14.0.0",
76
+ "semantic-release": "23.0.0",
74
77
  "storybook": "7.6.7",
75
78
  "style-loader": "3.3.3",
76
- "stylelint": "15.11.0",
79
+ "stylelint": "16.2.0",
77
80
  "stylelint-config-prettier": "9.0.5",
78
- "stylelint-config-recommended-scss": "5.0.2",
79
- "stylelint-order": "5.0.0",
80
- "stylelint-prettier": "2.0.0",
81
- "ts-jest": "27.1.5",
81
+ "stylelint-config-recommended-scss": "14.0.0",
82
+ "stylelint-order": "6.0.4",
83
+ "stylelint-prettier": "5.0.0",
84
+ "ts-jest": "29.1.2",
82
85
  "tsc-alias": "1.8.8",
83
- "typescript": "4.9.5",
86
+ "typescript": "5.3.3",
84
87
  "vanilla-framework": "4.6.0",
85
- "wait-on": "5.3.0",
88
+ "wait-on": "7.2.0",
86
89
  "webpack": "5.89.0"
87
90
  },
88
91
  "dependencies": {
89
- "@types/jest": "27.5.2",
92
+ "@types/jest": "29.5.11",
90
93
  "@types/node": "18.19.4",
91
94
  "@types/react": "18.2.46",
92
95
  "@types/react-dom": "18.2.18",
93
96
  "@types/react-table": "7.7.19",
94
97
  "classnames": "2.5.1",
95
- "nanoid": "3.3.7",
98
+ "jest-environment-jsdom": "29.7.0",
99
+ "nanoid": "5.0.4",
96
100
  "prop-types": "15.8.1",
97
101
  "react-table": "7.8.0",
98
102
  "react-useportal": "1.0.19"
@@ -101,10 +105,7 @@
101
105
  "@types/react": "18.2.46",
102
106
  "@types/react-dom": "18.2.18",
103
107
  "postcss": "^8.3.11",
104
- "string-width": "^4",
105
- "jackspeak": "^2",
106
- "strip-ansi": "^6.0.0",
107
- "strip-ansi-cjs": "^8.0.0"
108
+ "jackspeak": "2.1.1"
108
109
  },
109
110
  "peerDependencies": {
110
111
  "@types/react": "^17.0.2 || ^18.0.0",
@@ -127,7 +128,6 @@
127
128
  "lint-package-json": "npmPkgJsonLint .",
128
129
  "lint": "yarn lint-package-json && yarn lint-js && yarn lint-style",
129
130
  "percy": "yarn build-docs && percy storybook ./docs",
130
- "prepublishOnly": "yarn clean && yarn install && yarn build",
131
131
  "serve": "yarn docs",
132
132
  "start": "yarn docs",
133
133
  "test": "jest",
@@ -135,7 +135,9 @@
135
135
  "unlink-packages": "yarn unlink && cd node_modules/react && yarn unlink && cd ../react-dom && yarn unlink",
136
136
  "cypress:test": "wait-on http://localhost:${PORT:-9009} && cypress run --env port=${PORT:-9009}",
137
137
  "cypress:run": "cypress run --env port=${PORT:-9009}",
138
- "cypress:open": "cypress open --env port=${PORT:-9009}"
138
+ "cypress:open": "cypress open --env port=${PORT:-9009}",
139
+ "semantic-release": "semantic-release",
140
+ "semantic-release-dry-run": "semantic-release --dry-run --no-ci"
139
141
  },
140
142
  "eslintConfig": {
141
143
  "extends": "react-app"