@instructure/ui-modal 10.11.1-snapshot-14 → 10.11.1-snapshot-17

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,9 +3,12 @@
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.11.1-snapshot-14](https://github.com/instructure/instructure-ui/compare/v10.11.0...v10.11.1-snapshot-14) (2025-02-21)
6
+ ## [10.11.1-snapshot-17](https://github.com/instructure/instructure-ui/compare/v10.11.0...v10.11.1-snapshot-17) (2025-02-21)
7
7
 
8
- **Note:** Version bump only for package @instructure/ui-modal
8
+
9
+ ### Bug Fixes
10
+
11
+ * **ui-modal:** make Modal's header non-sticky with small window height ([db5c708](https://github.com/instructure/instructure-ui/commit/db5c7082ec4834793d83cf7d6f469c4fbaa83eed))
9
12
 
10
13
 
11
14
 
@@ -26,7 +26,8 @@ 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'])
29
+ spacing: PropTypes.oneOf(['default', 'compact']),
30
+ smallViewPort: PropTypes.bool
30
31
  };
31
32
  const allowedProps = ['children', 'variant', 'spacing'];
32
33
  export { propTypes, allowedProps };
@@ -34,7 +34,8 @@
34
34
  */
35
35
  const generateStyle = (componentTheme, props, state) => {
36
36
  const variant = props.variant,
37
- spacing = props.spacing;
37
+ spacing = props.spacing,
38
+ smallViewPort = props.smallViewPort;
38
39
  const withCloseButton = state.withCloseButton;
39
40
  const sizeVariants = {
40
41
  default: {
@@ -63,6 +64,9 @@ const generateStyle = (componentTheme, props, state) => {
63
64
  borderBottomWidth: '0.0625rem',
64
65
  borderBottomStyle: 'solid',
65
66
  borderBottomColor: componentTheme.borderColor,
67
+ ...(smallViewPort ? {
68
+ position: 'relative'
69
+ } : {}),
66
70
  ...sizeVariants[spacing],
67
71
  ...inverseStyle
68
72
  }
package/es/Modal/index.js CHANGED
@@ -30,6 +30,7 @@ import { Children, Component, isValidElement } from 'react';
30
30
  import { passthroughProps, safeCloneElement } from '@instructure/ui-react-utils';
31
31
  import { createChainedFunction } from '@instructure/ui-utils';
32
32
  import { testable } from '@instructure/ui-testable';
33
+ import { canUseDOM } from '@instructure/ui-dom-utils';
33
34
  import { Transition } from '@instructure/ui-motion';
34
35
  import { Portal } from '@instructure/ui-portal';
35
36
  import { Dialog } from '@instructure/ui-dialog';
@@ -57,6 +58,11 @@ let Modal = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 = te
57
58
  this.handleRef = el => {
58
59
  this.ref = el;
59
60
  };
61
+ this.updateHeight = () => {
62
+ this.setState({
63
+ windowHeight: window.innerHeight
64
+ });
65
+ };
60
66
  this.handlePortalOpen = DOMNode => {
61
67
  this.DOMNode = DOMNode;
62
68
  };
@@ -71,14 +77,27 @@ let Modal = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 = te
71
77
  this.props.contentRef(el);
72
78
  }
73
79
  };
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
+ };
74
91
  this.state = {
75
92
  transitioning: false,
76
- open: (_props$open = props.open) !== null && _props$open !== void 0 ? _props$open : false
93
+ open: (_props$open = props.open) !== null && _props$open !== void 0 ? _props$open : false,
94
+ windowHeight: 99999
77
95
  };
78
96
  }
79
97
  componentDidMount() {
80
98
  var _this$props$makeStyle, _this$props;
81
99
  (_this$props$makeStyle = (_this$props = this.props).makeStyles) === null || _this$props$makeStyle === void 0 ? void 0 : _this$props$makeStyle.call(_this$props);
100
+ window.addEventListener('resize', this.updateHeight);
82
101
  }
83
102
  componentDidUpdate(prevProps) {
84
103
  var _this$props$makeStyle2, _this$props2;
@@ -90,6 +109,9 @@ let Modal = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 = te
90
109
  }
91
110
  (_this$props$makeStyle2 = (_this$props2 = this.props).makeStyles) === null || _this$props$makeStyle2 === void 0 ? void 0 : _this$props$makeStyle2.call(_this$props2);
92
111
  }
112
+ componentWillUnmount() {
113
+ window.removeEventListener('resize', this.updateHeight);
114
+ }
93
115
  get defaultFocusElement() {
94
116
  return this.props.defaultFocusElement;
95
117
  }
@@ -111,31 +133,76 @@ let Modal = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 = te
111
133
  children = _this$props3.children,
112
134
  variant = _this$props3.variant,
113
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
+ }
114
141
  return Children.map(children, child => {
115
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 = [];
153
+ const childrenArray = Children.toArray(children);
116
154
 
155
+ // Separate header and body elements
156
+ const filteredChildren = childrenArray.filter(child => {
117
157
  if (/*#__PURE__*/isValidElement(child)) {
118
- var _child$props;
119
- return safeCloneElement(child, {
120
- variant: variant,
121
- overflow: (child === null || child === void 0 ? void 0 : (_child$props = child.props) === null || _child$props === void 0 ? void 0 : _child$props.overflow) || overflow
122
- });
123
- } else {
124
- return child;
158
+ 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;
168
+ }
125
169
  }
170
+ return true;
126
171
  });
172
+
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
+ }, headerAndBody));
178
+ }
179
+ return Children.map(filteredChildren, child => {
180
+ if (!child) return; // ignore null, falsy children
181
+ return this.cloneChildWithProps(child, variant, overflow);
182
+ });
183
+ }
184
+ cloneChildWithProps(child, variant, overflow) {
185
+ if (/*#__PURE__*/isValidElement(child)) {
186
+ var _child$props;
187
+ return safeCloneElement(child, {
188
+ variant: variant,
189
+ overflow: (child === null || child === void 0 ? void 0 : (_child$props = child.props) === null || _child$props === void 0 ? void 0 : _child$props.overflow) || overflow
190
+ });
191
+ } else {
192
+ return child;
193
+ }
127
194
  }
128
195
  renderDialog(props) {
129
- const _this$props4 = this.props,
130
- onDismiss = _this$props4.onDismiss,
131
- label = _this$props4.label,
132
- shouldCloseOnDocumentClick = _this$props4.shouldCloseOnDocumentClick,
133
- shouldReturnFocus = _this$props4.shouldReturnFocus,
134
- liveRegion = _this$props4.liveRegion,
135
- size = _this$props4.size,
136
- constrain = _this$props4.constrain,
137
- as = _this$props4.as,
138
- styles = _this$props4.styles;
196
+ const _this$props5 = this.props,
197
+ onDismiss = _this$props5.onDismiss,
198
+ label = _this$props5.label,
199
+ shouldCloseOnDocumentClick = _this$props5.shouldCloseOnDocumentClick,
200
+ shouldReturnFocus = _this$props5.shouldReturnFocus,
201
+ liveRegion = _this$props5.liveRegion,
202
+ size = _this$props5.size,
203
+ constrain = _this$props5.constrain,
204
+ as = _this$props5.as,
205
+ styles = _this$props5.styles;
139
206
  const isFullScreen = size === 'fullscreen';
140
207
  const dialog = jsx(Dialog, Object.assign({}, passthroughProps(props), {
141
208
  as: as,
@@ -163,22 +230,22 @@ let Modal = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 = te
163
230
  }
164
231
  render() {
165
232
  var _this$props$styles;
166
- const _this$props5 = this.props,
167
- open = _this$props5.open,
168
- onOpen = _this$props5.onOpen,
169
- onClose = _this$props5.onClose,
170
- mountNode = _this$props5.mountNode,
171
- insertAt = _this$props5.insertAt,
172
- transition = _this$props5.transition,
173
- onEnter = _this$props5.onEnter,
174
- onEntering = _this$props5.onEntering,
175
- onEntered = _this$props5.onEntered,
176
- onExit = _this$props5.onExit,
177
- onExiting = _this$props5.onExiting,
178
- onExited = _this$props5.onExited,
179
- constrain = _this$props5.constrain,
180
- overflow = _this$props5.overflow,
181
- passthroughProps = _objectWithoutProperties(_this$props5, _excluded);
233
+ const _this$props6 = this.props,
234
+ open = _this$props6.open,
235
+ onOpen = _this$props6.onOpen,
236
+ onClose = _this$props6.onClose,
237
+ mountNode = _this$props6.mountNode,
238
+ insertAt = _this$props6.insertAt,
239
+ transition = _this$props6.transition,
240
+ onEnter = _this$props6.onEnter,
241
+ onEntering = _this$props6.onEntering,
242
+ onEntered = _this$props6.onEntered,
243
+ onExit = _this$props6.onExit,
244
+ onExiting = _this$props6.onExiting,
245
+ onExited = _this$props6.onExited,
246
+ constrain = _this$props6.constrain,
247
+ overflow = _this$props6.overflow,
248
+ passthroughProps = _objectWithoutProperties(_this$props6, _excluded);
182
249
  const portalIsOpen = this.state.open || this.state.transitioning;
183
250
  return jsx(Portal, {
184
251
  mountNode: mountNode,
@@ -98,6 +98,10 @@ const generateStyle = (componentTheme, props) => {
98
98
  position: 'relative',
99
99
  width: '100%',
100
100
  height: '100%'
101
+ },
102
+ joinedHeaderAndBody: {
103
+ borderRadius: componentTheme.borderRadius,
104
+ overflowY: 'scroll'
101
105
  }
102
106
  };
103
107
  };
@@ -33,6 +33,7 @@ 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'])
36
+ spacing: _propTypes.default.oneOf(['default', 'compact']),
37
+ smallViewPort: _propTypes.default.bool
37
38
  };
38
39
  const allowedProps = exports.allowedProps = ['children', 'variant', 'spacing'];
@@ -40,7 +40,8 @@ exports.default = void 0;
40
40
  */
41
41
  const generateStyle = (componentTheme, props, state) => {
42
42
  const variant = props.variant,
43
- spacing = props.spacing;
43
+ spacing = props.spacing,
44
+ smallViewPort = props.smallViewPort;
44
45
  const withCloseButton = state.withCloseButton;
45
46
  const sizeVariants = {
46
47
  default: {
@@ -69,6 +70,9 @@ const generateStyle = (componentTheme, props, state) => {
69
70
  borderBottomWidth: '0.0625rem',
70
71
  borderBottomStyle: 'solid',
71
72
  borderBottomColor: componentTheme.borderColor,
73
+ ...(smallViewPort ? {
74
+ position: 'relative'
75
+ } : {}),
72
76
  ...sizeVariants[spacing],
73
77
  ...inverseStyle
74
78
  }
@@ -30,6 +30,7 @@ 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");
33
34
  var _Transition = require("@instructure/ui-motion/lib/Transition");
34
35
  var _Portal = require("@instructure/ui-portal/lib/Portal");
35
36
  var _Dialog = require("@instructure/ui-dialog/lib/Dialog");
@@ -83,6 +84,11 @@ let Modal = exports.Modal = (_dec = (0, _emotion.withStyle)(_styles.default, _th
83
84
  this.handleRef = el => {
84
85
  this.ref = el;
85
86
  };
87
+ this.updateHeight = () => {
88
+ this.setState({
89
+ windowHeight: window.innerHeight
90
+ });
91
+ };
86
92
  this.handlePortalOpen = DOMNode => {
87
93
  this.DOMNode = DOMNode;
88
94
  };
@@ -97,14 +103,27 @@ let Modal = exports.Modal = (_dec = (0, _emotion.withStyle)(_styles.default, _th
97
103
  this.props.contentRef(el);
98
104
  }
99
105
  };
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
+ };
100
117
  this.state = {
101
118
  transitioning: false,
102
- open: (_props$open = props.open) !== null && _props$open !== void 0 ? _props$open : false
119
+ open: (_props$open = props.open) !== null && _props$open !== void 0 ? _props$open : false,
120
+ windowHeight: 99999
103
121
  };
104
122
  }
105
123
  componentDidMount() {
106
124
  var _this$props$makeStyle, _this$props;
107
125
  (_this$props$makeStyle = (_this$props = this.props).makeStyles) === null || _this$props$makeStyle === void 0 ? void 0 : _this$props$makeStyle.call(_this$props);
126
+ window.addEventListener('resize', this.updateHeight);
108
127
  }
109
128
  componentDidUpdate(prevProps) {
110
129
  var _this$props$makeStyle2, _this$props2;
@@ -116,6 +135,9 @@ let Modal = exports.Modal = (_dec = (0, _emotion.withStyle)(_styles.default, _th
116
135
  }
117
136
  (_this$props$makeStyle2 = (_this$props2 = this.props).makeStyles) === null || _this$props$makeStyle2 === void 0 ? void 0 : _this$props$makeStyle2.call(_this$props2);
118
137
  }
138
+ componentWillUnmount() {
139
+ window.removeEventListener('resize', this.updateHeight);
140
+ }
119
141
  get defaultFocusElement() {
120
142
  return this.props.defaultFocusElement;
121
143
  }
@@ -137,31 +159,76 @@ let Modal = exports.Modal = (_dec = (0, _emotion.withStyle)(_styles.default, _th
137
159
  children = _this$props3.children,
138
160
  variant = _this$props3.variant,
139
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
+ }
140
167
  return _react.Children.map(children, child => {
141
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 = [];
179
+ const childrenArray = _react.Children.toArray(children);
142
180
 
181
+ // Separate header and body elements
182
+ const filteredChildren = childrenArray.filter(child => {
143
183
  if (/*#__PURE__*/(0, _react.isValidElement)(child)) {
144
- var _child$props;
145
- return (0, _safeCloneElement.safeCloneElement)(child, {
146
- variant: variant,
147
- overflow: (child === null || child === void 0 ? void 0 : (_child$props = child.props) === null || _child$props === void 0 ? void 0 : _child$props.overflow) || overflow
148
- });
149
- } else {
150
- return child;
184
+ 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;
194
+ }
151
195
  }
196
+ return true;
152
197
  });
198
+
199
+ // Adds the <div> to the beginning of the filteredChildren array
200
+ if (headerAndBody.length > 0) {
201
+ filteredChildren.unshift((0, _emotion.jsx)("div", {
202
+ css: styles === null || styles === void 0 ? void 0 : styles.joinedHeaderAndBody
203
+ }, headerAndBody));
204
+ }
205
+ return _react.Children.map(filteredChildren, child => {
206
+ if (!child) return; // ignore null, falsy children
207
+ return this.cloneChildWithProps(child, variant, overflow);
208
+ });
209
+ }
210
+ cloneChildWithProps(child, variant, overflow) {
211
+ if (/*#__PURE__*/(0, _react.isValidElement)(child)) {
212
+ var _child$props;
213
+ return (0, _safeCloneElement.safeCloneElement)(child, {
214
+ variant: variant,
215
+ overflow: (child === null || child === void 0 ? void 0 : (_child$props = child.props) === null || _child$props === void 0 ? void 0 : _child$props.overflow) || overflow
216
+ });
217
+ } else {
218
+ return child;
219
+ }
153
220
  }
154
221
  renderDialog(props) {
155
- const _this$props4 = this.props,
156
- onDismiss = _this$props4.onDismiss,
157
- label = _this$props4.label,
158
- shouldCloseOnDocumentClick = _this$props4.shouldCloseOnDocumentClick,
159
- shouldReturnFocus = _this$props4.shouldReturnFocus,
160
- liveRegion = _this$props4.liveRegion,
161
- size = _this$props4.size,
162
- constrain = _this$props4.constrain,
163
- as = _this$props4.as,
164
- styles = _this$props4.styles;
222
+ const _this$props5 = this.props,
223
+ onDismiss = _this$props5.onDismiss,
224
+ label = _this$props5.label,
225
+ shouldCloseOnDocumentClick = _this$props5.shouldCloseOnDocumentClick,
226
+ shouldReturnFocus = _this$props5.shouldReturnFocus,
227
+ liveRegion = _this$props5.liveRegion,
228
+ size = _this$props5.size,
229
+ constrain = _this$props5.constrain,
230
+ as = _this$props5.as,
231
+ styles = _this$props5.styles;
165
232
  const isFullScreen = size === 'fullscreen';
166
233
  const dialog = (0, _emotion.jsx)(_Dialog.Dialog, Object.assign({}, (0, _passthroughProps.passthroughProps)(props), {
167
234
  as: as,
@@ -189,22 +256,22 @@ let Modal = exports.Modal = (_dec = (0, _emotion.withStyle)(_styles.default, _th
189
256
  }
190
257
  render() {
191
258
  var _this$props$styles;
192
- const _this$props5 = this.props,
193
- open = _this$props5.open,
194
- onOpen = _this$props5.onOpen,
195
- onClose = _this$props5.onClose,
196
- mountNode = _this$props5.mountNode,
197
- insertAt = _this$props5.insertAt,
198
- transition = _this$props5.transition,
199
- onEnter = _this$props5.onEnter,
200
- onEntering = _this$props5.onEntering,
201
- onEntered = _this$props5.onEntered,
202
- onExit = _this$props5.onExit,
203
- onExiting = _this$props5.onExiting,
204
- onExited = _this$props5.onExited,
205
- constrain = _this$props5.constrain,
206
- overflow = _this$props5.overflow,
207
- passthroughProps = (0, _objectWithoutProperties2.default)(_this$props5, _excluded);
259
+ const _this$props6 = this.props,
260
+ open = _this$props6.open,
261
+ onOpen = _this$props6.onOpen,
262
+ onClose = _this$props6.onClose,
263
+ mountNode = _this$props6.mountNode,
264
+ insertAt = _this$props6.insertAt,
265
+ transition = _this$props6.transition,
266
+ onEnter = _this$props6.onEnter,
267
+ onEntering = _this$props6.onEntering,
268
+ onEntered = _this$props6.onEntered,
269
+ onExit = _this$props6.onExit,
270
+ onExiting = _this$props6.onExiting,
271
+ onExited = _this$props6.onExited,
272
+ constrain = _this$props6.constrain,
273
+ overflow = _this$props6.overflow,
274
+ passthroughProps = (0, _objectWithoutProperties2.default)(_this$props6, _excluded);
208
275
  const portalIsOpen = this.state.open || this.state.transitioning;
209
276
  return (0, _emotion.jsx)(_Portal.Portal, {
210
277
  mountNode: mountNode,
@@ -104,6 +104,10 @@ const generateStyle = (componentTheme, props) => {
104
104
  position: 'relative',
105
105
  width: '100%',
106
106
  height: '100%'
107
+ },
108
+ joinedHeaderAndBody: {
109
+ borderRadius: componentTheme.borderRadius,
110
+ overflowY: 'scroll'
107
111
  }
108
112
  };
109
113
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instructure/ui-modal",
3
- "version": "10.11.1-snapshot-14",
3
+ "version": "10.11.1-snapshot-17",
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.11.1-snapshot-14",
28
- "@instructure/emotion": "10.11.1-snapshot-14",
29
- "@instructure/shared-types": "10.11.1-snapshot-14",
30
- "@instructure/ui-buttons": "10.11.1-snapshot-14",
31
- "@instructure/ui-dialog": "10.11.1-snapshot-14",
32
- "@instructure/ui-dom-utils": "10.11.1-snapshot-14",
33
- "@instructure/ui-motion": "10.11.1-snapshot-14",
34
- "@instructure/ui-overlays": "10.11.1-snapshot-14",
35
- "@instructure/ui-portal": "10.11.1-snapshot-14",
36
- "@instructure/ui-prop-types": "10.11.1-snapshot-14",
37
- "@instructure/ui-react-utils": "10.11.1-snapshot-14",
38
- "@instructure/ui-testable": "10.11.1-snapshot-14",
39
- "@instructure/ui-utils": "10.11.1-snapshot-14",
40
- "@instructure/ui-view": "10.11.1-snapshot-14",
27
+ "@instructure/console": "10.11.1-snapshot-17",
28
+ "@instructure/emotion": "10.11.1-snapshot-17",
29
+ "@instructure/shared-types": "10.11.1-snapshot-17",
30
+ "@instructure/ui-buttons": "10.11.1-snapshot-17",
31
+ "@instructure/ui-dialog": "10.11.1-snapshot-17",
32
+ "@instructure/ui-dom-utils": "10.11.1-snapshot-17",
33
+ "@instructure/ui-motion": "10.11.1-snapshot-17",
34
+ "@instructure/ui-overlays": "10.11.1-snapshot-17",
35
+ "@instructure/ui-portal": "10.11.1-snapshot-17",
36
+ "@instructure/ui-prop-types": "10.11.1-snapshot-17",
37
+ "@instructure/ui-react-utils": "10.11.1-snapshot-17",
38
+ "@instructure/ui-testable": "10.11.1-snapshot-17",
39
+ "@instructure/ui-utils": "10.11.1-snapshot-17",
40
+ "@instructure/ui-view": "10.11.1-snapshot-17",
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.11.1-snapshot-14",
48
- "@instructure/ui-color-utils": "10.11.1-snapshot-14",
49
- "@instructure/ui-position": "10.11.1-snapshot-14",
50
- "@instructure/ui-themes": "10.11.1-snapshot-14",
47
+ "@instructure/ui-babel-preset": "10.11.1-snapshot-17",
48
+ "@instructure/ui-color-utils": "10.11.1-snapshot-17",
49
+ "@instructure/ui-position": "10.11.1-snapshot-17",
50
+ "@instructure/ui-themes": "10.11.1-snapshot-17",
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",
@@ -36,6 +36,7 @@ type ModalHeaderOwnProps = {
36
36
  children?: React.ReactNode
37
37
  variant?: 'default' | 'inverse'
38
38
  spacing?: 'default' | 'compact'
39
+ smallViewPort?: boolean
39
40
  }
40
41
 
41
42
  type ModalHeaderStyleProps = {
@@ -55,7 +56,8 @@ type ModalHeaderStyle = ComponentStyle<'modalHeader'>
55
56
  const propTypes: PropValidators<PropKeys> = {
56
57
  children: PropTypes.node,
57
58
  variant: PropTypes.oneOf(['default', 'inverse']),
58
- spacing: PropTypes.oneOf(['default', 'compact'])
59
+ spacing: PropTypes.oneOf(['default', 'compact']),
60
+ smallViewPort: PropTypes.bool
59
61
  }
60
62
 
61
63
  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 } = props
47
+ const { variant, spacing, smallViewPort } = props
48
48
  const { withCloseButton } = state
49
49
 
50
50
  const sizeVariants = {
@@ -83,6 +83,7 @@ const generateStyle = (
83
83
  borderBottomWidth: '0.0625rem',
84
84
  borderBottomStyle: 'solid',
85
85
  borderBottomColor: componentTheme.borderColor,
86
+ ...(smallViewPort ? { position: 'relative' } : {}),
86
87
  ...sizeVariants[spacing!],
87
88
  ...inverseStyle
88
89
  }
@@ -1180,6 +1180,7 @@ type: embed
1180
1180
  <Figure.Item>When a user closes a modal, focus must return to a logical place within the page. This is usually the element that triggered opening the modal</Figure.Item>
1181
1181
  <Figure.Item>Modals should be able to be closed by clicking away, esc key and/or a close button</Figure.Item>
1182
1182
  <Figure.Item>We recommend that modals begin with a heading (typically H2)</Figure.Item>
1183
+ <Figure.Item>The Modal's header currently becomes non-sticky when the window height is too small, improving navigation of the Modal.Body, e.g., at higher zoom levels</Figure.Item>
1183
1184
  </Figure>
1184
1185
  </Guidelines>
1185
1186
  ```