@instructure/ui-modal 10.19.0 → 10.19.1-snapshot-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/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [10.19.1-snapshot-0](https://github.com/instructure/instructure-ui/compare/v10.19.0...v10.19.1-snapshot-0) (2025-06-05)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **ui-modal:** save modal from unnecessary rerender ([a473127](https://github.com/instructure/instructure-ui/commit/a473127b2049b02e5463b71c84a0b803fd9d40d4))
12
+
13
+
14
+
15
+
16
+
6
17
  # [10.19.0](https://github.com/instructure/instructure-ui/compare/v10.18.1...v10.19.0) (2025-06-03)
7
18
 
8
19
  **Note:** Version bump only for package @instructure/ui-modal
@@ -42,10 +42,13 @@ const generateStyle = (componentTheme, props) => {
42
42
  label: 'modalBody',
43
43
  boxSizing: 'border-box',
44
44
  flex: '1 1 auto',
45
- overflowY: 'auto',
46
45
  '&:focus': {
47
46
  outline: 'none'
48
47
  },
48
+ // ModalBody is set to scrollable above 20rem height
49
+ '@media (min-height: 20rem)': {
50
+ overflowY: 'auto'
51
+ },
49
52
  ...backgroundStyle
50
53
  }
51
54
  };
@@ -26,8 +26,7 @@ import PropTypes from 'prop-types';
26
26
  const propTypes = {
27
27
  children: PropTypes.node,
28
28
  variant: PropTypes.oneOf(['default', 'inverse']),
29
- spacing: PropTypes.oneOf(['default', 'compact']),
30
- smallViewPort: PropTypes.bool
29
+ spacing: PropTypes.oneOf(['default', 'compact'])
31
30
  };
32
31
  const allowedProps = ['children', 'variant', 'spacing'];
33
32
  export { propTypes, allowedProps };
@@ -34,8 +34,7 @@
34
34
  */
35
35
  const generateStyle = (componentTheme, props, state) => {
36
36
  const variant = props.variant,
37
- spacing = props.spacing,
38
- smallViewPort = props.smallViewPort;
37
+ spacing = props.spacing;
39
38
  const withCloseButton = state.withCloseButton;
40
39
  const sizeVariants = {
41
40
  default: {
@@ -64,11 +63,12 @@ const generateStyle = (componentTheme, props, state) => {
64
63
  borderBottomWidth: '0.0625rem',
65
64
  borderBottomStyle: 'solid',
66
65
  borderBottomColor: componentTheme.borderColor,
67
- ...(smallViewPort ? {
68
- position: 'relative'
69
- } : {}),
70
66
  ...sizeVariants[spacing],
71
- ...inverseStyle
67
+ ...inverseStyle,
68
+ // Position is set to relative for small viewports to ensure proper close button positioning during scrolling
69
+ '@media (max-height: 20rem)': {
70
+ position: 'relative'
71
+ }
72
72
  }
73
73
  };
74
74
  };
package/es/Modal/index.js CHANGED
@@ -29,7 +29,6 @@ import { Children, Component, isValidElement } from 'react';
29
29
  import { passthroughProps, safeCloneElement } from '@instructure/ui-react-utils';
30
30
  import { createChainedFunction } from '@instructure/ui-utils';
31
31
  import { testable } from '@instructure/ui-testable';
32
- import { canUseDOM } from '@instructure/ui-dom-utils';
33
32
  import { Transition } from '@instructure/ui-motion';
34
33
  import { Portal } from '@instructure/ui-portal';
35
34
  import { Dialog } from '@instructure/ui-dialog';
@@ -77,17 +76,6 @@ let Modal = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 = te
77
76
  this.props.contentRef(el);
78
77
  }
79
78
  };
80
- this.getWindowHeightInRem = () => {
81
- var _getComputedStyle;
82
- if (!canUseDOM) {
83
- return Infinity;
84
- }
85
- const rootFontSize = parseFloat(((_getComputedStyle = getComputedStyle(document.documentElement)) === null || _getComputedStyle === void 0 ? void 0 : _getComputedStyle.fontSize) || '16');
86
- if (isNaN(rootFontSize) || rootFontSize <= 0) {
87
- return Infinity;
88
- }
89
- return window.innerHeight / rootFontSize;
90
- };
91
79
  this.state = {
92
80
  transitioning: false,
93
81
  open: (_props$open = props.open) !== null && _props$open !== void 0 ? _props$open : false,
@@ -132,55 +120,28 @@ let Modal = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 = te
132
120
  const _this$props3 = this.props,
133
121
  children = _this$props3.children,
134
122
  variant = _this$props3.variant,
135
- overflow = _this$props3.overflow;
136
-
137
- // header should be non-sticky for small viewport height (ca. 320px)
138
- if (this.getWindowHeightInRem() <= 20) {
139
- return this.renderForSmallViewportHeight();
140
- }
141
- return Children.map(children, child => {
142
- if (!child) return; // ignore null, falsy children
143
- return this.cloneChildWithProps(child, variant, overflow);
144
- });
145
- }
146
- renderForSmallViewportHeight() {
147
- const _this$props4 = this.props,
148
- children = _this$props4.children,
149
- variant = _this$props4.variant,
150
- overflow = _this$props4.overflow,
151
- styles = _this$props4.styles;
152
- const headerAndBody = [];
123
+ overflow = _this$props3.overflow,
124
+ styles = _this$props3.styles;
153
125
  const childrenArray = Children.toArray(children);
154
-
155
- // Separate header and body elements
156
- const filteredChildren = childrenArray.filter(child => {
126
+ const headerAndBody = [];
127
+ const others = [];
128
+ childrenArray.forEach(child => {
157
129
  if (/*#__PURE__*/isValidElement(child)) {
158
130
  if (child.type === Modal.Header || child.type === Modal.Body) {
159
- if (child.type === Modal.Header) {
160
- const headerWithProp = safeCloneElement(child, {
161
- smallViewPort: true
162
- });
163
- headerAndBody.push(headerWithProp);
164
- } else {
165
- headerAndBody.push(child);
166
- }
167
- return false;
131
+ headerAndBody.push(this.cloneChildWithProps(child, variant, overflow));
132
+ } else {
133
+ others.push(this.cloneChildWithProps(child, variant, overflow));
168
134
  }
169
135
  }
170
- return true;
171
136
  });
172
137
 
173
- // Adds the <div> to the beginning of the filteredChildren array
174
- if (headerAndBody.length > 0) {
175
- filteredChildren.unshift(_jsx("div", {
176
- css: styles === null || styles === void 0 ? void 0 : styles.joinedHeaderAndBody,
177
- children: headerAndBody
178
- }));
179
- }
180
- return Children.map(filteredChildren, child => {
181
- if (!child) return; // ignore null, falsy children
182
- return this.cloneChildWithProps(child, variant, overflow);
183
- });
138
+ // Putting ModalHeader and ModalBody into the same container, so they can be styled together;
139
+ // this is needed to apply a media query breakpoint
140
+ const wrappedHeaderAndBody = headerAndBody.length > 0 ? _jsx("div", {
141
+ css: styles === null || styles === void 0 ? void 0 : styles.joinedHeaderAndBody,
142
+ children: headerAndBody
143
+ }) : null;
144
+ return [...(wrappedHeaderAndBody ? [wrappedHeaderAndBody] : []), ...others];
184
145
  }
185
146
  cloneChildWithProps(child, variant, overflow) {
186
147
  if (/*#__PURE__*/isValidElement(child)) {
@@ -194,16 +155,16 @@ let Modal = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 = te
194
155
  }
195
156
  }
196
157
  renderDialog(props) {
197
- const _this$props5 = this.props,
198
- onDismiss = _this$props5.onDismiss,
199
- label = _this$props5.label,
200
- shouldCloseOnDocumentClick = _this$props5.shouldCloseOnDocumentClick,
201
- shouldReturnFocus = _this$props5.shouldReturnFocus,
202
- liveRegion = _this$props5.liveRegion,
203
- size = _this$props5.size,
204
- constrain = _this$props5.constrain,
205
- as = _this$props5.as,
206
- styles = _this$props5.styles;
158
+ const _this$props4 = this.props,
159
+ onDismiss = _this$props4.onDismiss,
160
+ label = _this$props4.label,
161
+ shouldCloseOnDocumentClick = _this$props4.shouldCloseOnDocumentClick,
162
+ shouldReturnFocus = _this$props4.shouldReturnFocus,
163
+ liveRegion = _this$props4.liveRegion,
164
+ size = _this$props4.size,
165
+ constrain = _this$props4.constrain,
166
+ as = _this$props4.as,
167
+ styles = _this$props4.styles;
207
168
  const isFullScreen = size === 'fullscreen';
208
169
  const dialog = _jsx(Dialog, {
209
170
  ...passthroughProps(props),
@@ -235,22 +196,22 @@ let Modal = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 = te
235
196
  }
236
197
  render() {
237
198
  var _this$props$styles;
238
- const _this$props6 = this.props,
239
- open = _this$props6.open,
240
- onOpen = _this$props6.onOpen,
241
- onClose = _this$props6.onClose,
242
- mountNode = _this$props6.mountNode,
243
- insertAt = _this$props6.insertAt,
244
- transition = _this$props6.transition,
245
- onEnter = _this$props6.onEnter,
246
- onEntering = _this$props6.onEntering,
247
- onEntered = _this$props6.onEntered,
248
- onExit = _this$props6.onExit,
249
- onExiting = _this$props6.onExiting,
250
- onExited = _this$props6.onExited,
251
- constrain = _this$props6.constrain,
252
- overflow = _this$props6.overflow,
253
- passthroughProps = _objectWithoutProperties(_this$props6, _excluded);
199
+ const _this$props5 = this.props,
200
+ open = _this$props5.open,
201
+ onOpen = _this$props5.onOpen,
202
+ onClose = _this$props5.onClose,
203
+ mountNode = _this$props5.mountNode,
204
+ insertAt = _this$props5.insertAt,
205
+ transition = _this$props5.transition,
206
+ onEnter = _this$props5.onEnter,
207
+ onEntering = _this$props5.onEntering,
208
+ onEntered = _this$props5.onEntered,
209
+ onExit = _this$props5.onExit,
210
+ onExiting = _this$props5.onExiting,
211
+ onExited = _this$props5.onExited,
212
+ constrain = _this$props5.constrain,
213
+ overflow = _this$props5.overflow,
214
+ passthroughProps = _objectWithoutProperties(_this$props5, _excluded);
254
215
  const portalIsOpen = this.state.open || this.state.transitioning;
255
216
  return _jsx(Portal, {
256
217
  mountNode: mountNode,
@@ -101,7 +101,14 @@ const generateStyle = (componentTheme, props) => {
101
101
  },
102
102
  joinedHeaderAndBody: {
103
103
  borderRadius: componentTheme.borderRadius,
104
- overflowY: 'scroll'
104
+ display: 'flex',
105
+ flexDirection: 'column',
106
+ overflow: 'hidden',
107
+ // ModalHeader and ModalBody is set to scrollable above 20rem height instead of just the ModalBody
108
+ '@media (max-height: 20rem)': {
109
+ overflowY: 'auto',
110
+ maxHeight: '20rem'
111
+ }
105
112
  }
106
113
  };
107
114
  };
@@ -48,10 +48,13 @@ const generateStyle = (componentTheme, props) => {
48
48
  label: 'modalBody',
49
49
  boxSizing: 'border-box',
50
50
  flex: '1 1 auto',
51
- overflowY: 'auto',
52
51
  '&:focus': {
53
52
  outline: 'none'
54
53
  },
54
+ // ModalBody is set to scrollable above 20rem height
55
+ '@media (min-height: 20rem)': {
56
+ overflowY: 'auto'
57
+ },
55
58
  ...backgroundStyle
56
59
  }
57
60
  };
@@ -33,7 +33,6 @@ var _propTypes = _interopRequireDefault(require("prop-types"));
33
33
  const propTypes = exports.propTypes = {
34
34
  children: _propTypes.default.node,
35
35
  variant: _propTypes.default.oneOf(['default', 'inverse']),
36
- spacing: _propTypes.default.oneOf(['default', 'compact']),
37
- smallViewPort: _propTypes.default.bool
36
+ spacing: _propTypes.default.oneOf(['default', 'compact'])
38
37
  };
39
38
  const allowedProps = exports.allowedProps = ['children', 'variant', 'spacing'];
@@ -40,8 +40,7 @@ exports.default = void 0;
40
40
  */
41
41
  const generateStyle = (componentTheme, props, state) => {
42
42
  const variant = props.variant,
43
- spacing = props.spacing,
44
- smallViewPort = props.smallViewPort;
43
+ spacing = props.spacing;
45
44
  const withCloseButton = state.withCloseButton;
46
45
  const sizeVariants = {
47
46
  default: {
@@ -70,11 +69,12 @@ const generateStyle = (componentTheme, props, state) => {
70
69
  borderBottomWidth: '0.0625rem',
71
70
  borderBottomStyle: 'solid',
72
71
  borderBottomColor: componentTheme.borderColor,
73
- ...(smallViewPort ? {
74
- position: 'relative'
75
- } : {}),
76
72
  ...sizeVariants[spacing],
77
- ...inverseStyle
73
+ ...inverseStyle,
74
+ // Position is set to relative for small viewports to ensure proper close button positioning during scrolling
75
+ '@media (max-height: 20rem)': {
76
+ position: 'relative'
77
+ }
78
78
  }
79
79
  };
80
80
  };
@@ -30,7 +30,6 @@ var _passthroughProps = require("@instructure/ui-react-utils/lib/passthroughProp
30
30
  var _safeCloneElement = require("@instructure/ui-react-utils/lib/safeCloneElement.js");
31
31
  var _createChainedFunction = require("@instructure/ui-utils/lib/createChainedFunction.js");
32
32
  var _testable = require("@instructure/ui-testable/lib/testable.js");
33
- var _canUseDOM = require("@instructure/ui-dom-utils/lib/canUseDOM.js");
34
33
  var _Transition = require("@instructure/ui-motion/lib/Transition");
35
34
  var _Portal = require("@instructure/ui-portal/lib/Portal");
36
35
  var _Dialog = require("@instructure/ui-dialog/lib/Dialog");
@@ -103,17 +102,6 @@ let Modal = exports.Modal = (_dec = (0, _emotion.withStyle)(_styles.default, _th
103
102
  this.props.contentRef(el);
104
103
  }
105
104
  };
106
- this.getWindowHeightInRem = () => {
107
- var _getComputedStyle;
108
- if (!_canUseDOM.canUseDOM) {
109
- return Infinity;
110
- }
111
- const rootFontSize = parseFloat(((_getComputedStyle = getComputedStyle(document.documentElement)) === null || _getComputedStyle === void 0 ? void 0 : _getComputedStyle.fontSize) || '16');
112
- if (isNaN(rootFontSize) || rootFontSize <= 0) {
113
- return Infinity;
114
- }
115
- return window.innerHeight / rootFontSize;
116
- };
117
105
  this.state = {
118
106
  transitioning: false,
119
107
  open: (_props$open = props.open) !== null && _props$open !== void 0 ? _props$open : false,
@@ -158,55 +146,28 @@ let Modal = exports.Modal = (_dec = (0, _emotion.withStyle)(_styles.default, _th
158
146
  const _this$props3 = this.props,
159
147
  children = _this$props3.children,
160
148
  variant = _this$props3.variant,
161
- overflow = _this$props3.overflow;
162
-
163
- // header should be non-sticky for small viewport height (ca. 320px)
164
- if (this.getWindowHeightInRem() <= 20) {
165
- return this.renderForSmallViewportHeight();
166
- }
167
- return _react.Children.map(children, child => {
168
- if (!child) return; // ignore null, falsy children
169
- return this.cloneChildWithProps(child, variant, overflow);
170
- });
171
- }
172
- renderForSmallViewportHeight() {
173
- const _this$props4 = this.props,
174
- children = _this$props4.children,
175
- variant = _this$props4.variant,
176
- overflow = _this$props4.overflow,
177
- styles = _this$props4.styles;
178
- const headerAndBody = [];
149
+ overflow = _this$props3.overflow,
150
+ styles = _this$props3.styles;
179
151
  const childrenArray = _react.Children.toArray(children);
180
-
181
- // Separate header and body elements
182
- const filteredChildren = childrenArray.filter(child => {
152
+ const headerAndBody = [];
153
+ const others = [];
154
+ childrenArray.forEach(child => {
183
155
  if (/*#__PURE__*/(0, _react.isValidElement)(child)) {
184
156
  if (child.type === Modal.Header || child.type === Modal.Body) {
185
- if (child.type === Modal.Header) {
186
- const headerWithProp = (0, _safeCloneElement.safeCloneElement)(child, {
187
- smallViewPort: true
188
- });
189
- headerAndBody.push(headerWithProp);
190
- } else {
191
- headerAndBody.push(child);
192
- }
193
- return false;
157
+ headerAndBody.push(this.cloneChildWithProps(child, variant, overflow));
158
+ } else {
159
+ others.push(this.cloneChildWithProps(child, variant, overflow));
194
160
  }
195
161
  }
196
- return true;
197
162
  });
198
163
 
199
- // Adds the <div> to the beginning of the filteredChildren array
200
- if (headerAndBody.length > 0) {
201
- filteredChildren.unshift((0, _jsxRuntime.jsx)("div", {
202
- css: styles === null || styles === void 0 ? void 0 : styles.joinedHeaderAndBody,
203
- children: headerAndBody
204
- }));
205
- }
206
- return _react.Children.map(filteredChildren, child => {
207
- if (!child) return; // ignore null, falsy children
208
- return this.cloneChildWithProps(child, variant, overflow);
209
- });
164
+ // Putting ModalHeader and ModalBody into the same container, so they can be styled together;
165
+ // this is needed to apply a media query breakpoint
166
+ const wrappedHeaderAndBody = headerAndBody.length > 0 ? (0, _jsxRuntime.jsx)("div", {
167
+ css: styles === null || styles === void 0 ? void 0 : styles.joinedHeaderAndBody,
168
+ children: headerAndBody
169
+ }) : null;
170
+ return [...(wrappedHeaderAndBody ? [wrappedHeaderAndBody] : []), ...others];
210
171
  }
211
172
  cloneChildWithProps(child, variant, overflow) {
212
173
  if (/*#__PURE__*/(0, _react.isValidElement)(child)) {
@@ -220,16 +181,16 @@ let Modal = exports.Modal = (_dec = (0, _emotion.withStyle)(_styles.default, _th
220
181
  }
221
182
  }
222
183
  renderDialog(props) {
223
- const _this$props5 = this.props,
224
- onDismiss = _this$props5.onDismiss,
225
- label = _this$props5.label,
226
- shouldCloseOnDocumentClick = _this$props5.shouldCloseOnDocumentClick,
227
- shouldReturnFocus = _this$props5.shouldReturnFocus,
228
- liveRegion = _this$props5.liveRegion,
229
- size = _this$props5.size,
230
- constrain = _this$props5.constrain,
231
- as = _this$props5.as,
232
- styles = _this$props5.styles;
184
+ const _this$props4 = this.props,
185
+ onDismiss = _this$props4.onDismiss,
186
+ label = _this$props4.label,
187
+ shouldCloseOnDocumentClick = _this$props4.shouldCloseOnDocumentClick,
188
+ shouldReturnFocus = _this$props4.shouldReturnFocus,
189
+ liveRegion = _this$props4.liveRegion,
190
+ size = _this$props4.size,
191
+ constrain = _this$props4.constrain,
192
+ as = _this$props4.as,
193
+ styles = _this$props4.styles;
233
194
  const isFullScreen = size === 'fullscreen';
234
195
  const dialog = (0, _jsxRuntime.jsx)(_Dialog.Dialog, {
235
196
  ...(0, _passthroughProps.passthroughProps)(props),
@@ -261,22 +222,22 @@ let Modal = exports.Modal = (_dec = (0, _emotion.withStyle)(_styles.default, _th
261
222
  }
262
223
  render() {
263
224
  var _this$props$styles;
264
- const _this$props6 = this.props,
265
- open = _this$props6.open,
266
- onOpen = _this$props6.onOpen,
267
- onClose = _this$props6.onClose,
268
- mountNode = _this$props6.mountNode,
269
- insertAt = _this$props6.insertAt,
270
- transition = _this$props6.transition,
271
- onEnter = _this$props6.onEnter,
272
- onEntering = _this$props6.onEntering,
273
- onEntered = _this$props6.onEntered,
274
- onExit = _this$props6.onExit,
275
- onExiting = _this$props6.onExiting,
276
- onExited = _this$props6.onExited,
277
- constrain = _this$props6.constrain,
278
- overflow = _this$props6.overflow,
279
- passthroughProps = (0, _objectWithoutProperties2.default)(_this$props6, _excluded);
225
+ const _this$props5 = this.props,
226
+ open = _this$props5.open,
227
+ onOpen = _this$props5.onOpen,
228
+ onClose = _this$props5.onClose,
229
+ mountNode = _this$props5.mountNode,
230
+ insertAt = _this$props5.insertAt,
231
+ transition = _this$props5.transition,
232
+ onEnter = _this$props5.onEnter,
233
+ onEntering = _this$props5.onEntering,
234
+ onEntered = _this$props5.onEntered,
235
+ onExit = _this$props5.onExit,
236
+ onExiting = _this$props5.onExiting,
237
+ onExited = _this$props5.onExited,
238
+ constrain = _this$props5.constrain,
239
+ overflow = _this$props5.overflow,
240
+ passthroughProps = (0, _objectWithoutProperties2.default)(_this$props5, _excluded);
280
241
  const portalIsOpen = this.state.open || this.state.transitioning;
281
242
  return (0, _jsxRuntime.jsx)(_Portal.Portal, {
282
243
  mountNode: mountNode,
@@ -107,7 +107,14 @@ const generateStyle = (componentTheme, props) => {
107
107
  },
108
108
  joinedHeaderAndBody: {
109
109
  borderRadius: componentTheme.borderRadius,
110
- overflowY: 'scroll'
110
+ display: 'flex',
111
+ flexDirection: 'column',
112
+ overflow: 'hidden',
113
+ // ModalHeader and ModalBody is set to scrollable above 20rem height instead of just the ModalBody
114
+ '@media (max-height: 20rem)': {
115
+ overflowY: 'auto',
116
+ maxHeight: '20rem'
117
+ }
111
118
  }
112
119
  };
113
120
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instructure/ui-modal",
3
- "version": "10.19.0",
3
+ "version": "10.19.1-snapshot-0",
4
4
  "description": "A component for displaying content in a dialog overlay",
5
5
  "author": "Instructure, Inc. Engineering and Product Design",
6
6
  "module": "./es/index.js",
@@ -24,30 +24,30 @@
24
24
  "license": "MIT",
25
25
  "dependencies": {
26
26
  "@babel/runtime": "^7.26.0",
27
- "@instructure/console": "10.19.0",
28
- "@instructure/emotion": "10.19.0",
29
- "@instructure/shared-types": "10.19.0",
30
- "@instructure/ui-buttons": "10.19.0",
31
- "@instructure/ui-dialog": "10.19.0",
32
- "@instructure/ui-dom-utils": "10.19.0",
33
- "@instructure/ui-motion": "10.19.0",
34
- "@instructure/ui-overlays": "10.19.0",
35
- "@instructure/ui-portal": "10.19.0",
36
- "@instructure/ui-prop-types": "10.19.0",
37
- "@instructure/ui-react-utils": "10.19.0",
38
- "@instructure/ui-testable": "10.19.0",
39
- "@instructure/ui-utils": "10.19.0",
40
- "@instructure/ui-view": "10.19.0",
27
+ "@instructure/console": "10.19.1-snapshot-0",
28
+ "@instructure/emotion": "10.19.1-snapshot-0",
29
+ "@instructure/shared-types": "10.19.1-snapshot-0",
30
+ "@instructure/ui-buttons": "10.19.1-snapshot-0",
31
+ "@instructure/ui-dialog": "10.19.1-snapshot-0",
32
+ "@instructure/ui-dom-utils": "10.19.1-snapshot-0",
33
+ "@instructure/ui-motion": "10.19.1-snapshot-0",
34
+ "@instructure/ui-overlays": "10.19.1-snapshot-0",
35
+ "@instructure/ui-portal": "10.19.1-snapshot-0",
36
+ "@instructure/ui-prop-types": "10.19.1-snapshot-0",
37
+ "@instructure/ui-react-utils": "10.19.1-snapshot-0",
38
+ "@instructure/ui-testable": "10.19.1-snapshot-0",
39
+ "@instructure/ui-utils": "10.19.1-snapshot-0",
40
+ "@instructure/ui-view": "10.19.1-snapshot-0",
41
41
  "prop-types": "^15.8.1"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "react": ">=16.14 <=18"
45
45
  },
46
46
  "devDependencies": {
47
- "@instructure/ui-babel-preset": "10.19.0",
48
- "@instructure/ui-color-utils": "10.19.0",
49
- "@instructure/ui-position": "10.19.0",
50
- "@instructure/ui-themes": "10.19.0",
47
+ "@instructure/ui-babel-preset": "10.19.1-snapshot-0",
48
+ "@instructure/ui-color-utils": "10.19.1-snapshot-0",
49
+ "@instructure/ui-position": "10.19.1-snapshot-0",
50
+ "@instructure/ui-themes": "10.19.1-snapshot-0",
51
51
  "@testing-library/jest-dom": "^6.6.3",
52
52
  "@testing-library/react": "^16.0.1",
53
53
  "@testing-library/user-event": "^14.5.2",
@@ -53,10 +53,13 @@ const generateStyle = (
53
53
  label: 'modalBody',
54
54
  boxSizing: 'border-box',
55
55
  flex: '1 1 auto',
56
- overflowY: 'auto',
57
56
  '&:focus': {
58
57
  outline: 'none'
59
58
  },
59
+ // ModalBody is set to scrollable above 20rem height
60
+ '@media (min-height: 20rem)': {
61
+ overflowY: 'auto'
62
+ },
60
63
  ...backgroundStyle
61
64
  }
62
65
  }
@@ -36,7 +36,6 @@ type ModalHeaderOwnProps = {
36
36
  children?: React.ReactNode
37
37
  variant?: 'default' | 'inverse'
38
38
  spacing?: 'default' | 'compact'
39
- smallViewPort?: boolean
40
39
  }
41
40
 
42
41
  type ModalHeaderStyleProps = {
@@ -56,8 +55,7 @@ type ModalHeaderStyle = ComponentStyle<'modalHeader'>
56
55
  const propTypes: PropValidators<PropKeys> = {
57
56
  children: PropTypes.node,
58
57
  variant: PropTypes.oneOf(['default', 'inverse']),
59
- spacing: PropTypes.oneOf(['default', 'compact']),
60
- smallViewPort: PropTypes.bool
58
+ spacing: PropTypes.oneOf(['default', 'compact'])
61
59
  }
62
60
 
63
61
  const allowedProps: AllowedPropKeys = ['children', 'variant', 'spacing']
@@ -44,7 +44,7 @@ const generateStyle = (
44
44
  props: ModalHeaderProps,
45
45
  state: ModalHeaderStyleProps
46
46
  ): ModalHeaderStyle => {
47
- const { variant, spacing, smallViewPort } = props
47
+ const { variant, spacing } = props
48
48
  const { withCloseButton } = state
49
49
 
50
50
  const sizeVariants = {
@@ -83,9 +83,13 @@ const generateStyle = (
83
83
  borderBottomWidth: '0.0625rem',
84
84
  borderBottomStyle: 'solid',
85
85
  borderBottomColor: componentTheme.borderColor,
86
- ...(smallViewPort ? { position: 'relative' } : {}),
87
86
  ...sizeVariants[spacing!],
88
- ...inverseStyle
87
+ ...inverseStyle,
88
+
89
+ // Position is set to relative for small viewports to ensure proper close button positioning during scrolling
90
+ '@media (max-height: 20rem)': {
91
+ position: 'relative'
92
+ }
89
93
  }
90
94
  }
91
95
  }