@instructure/ui-top-nav-bar 10.18.1 → 10.18.2-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.18.2-snapshot-0](https://github.com/instructure/instructure-ui/compare/v10.18.1...v10.18.2-snapshot-0) (2025-06-02)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **ui-top-nav-bar,ui-buttons:** display a focus ring in TopNavBar if a button has a Popover open ([1a03763](https://github.com/instructure/instructure-ui/commit/1a03763f99db390ec6cea58a71ef118930be64d8))
12
+
13
+
14
+
15
+
16
+
6
17
  ## [10.18.1](https://github.com/instructure/instructure-ui/compare/v10.18.0...v10.18.1) (2025-05-29)
7
18
 
8
19
 
@@ -72,6 +72,16 @@ let TopNavBarItem = (_dec = withDeterministicId(), _dec2 = withStyle(generateSty
72
72
  itemRef(el);
73
73
  }
74
74
  };
75
+ this.onFocus = () => {
76
+ this.setState({
77
+ isFocused: true
78
+ });
79
+ };
80
+ this.onBlur = () => {
81
+ this.setState({
82
+ isFocused: false
83
+ });
84
+ };
75
85
  this.handleKeyDown = e => {
76
86
  if (e.key === 'ArrowDown') {
77
87
  if (this.shouldRenderSubmenu && !this.state.isSubmenuOpen || this.shouldRenderPopover && !this.state.isPopoverOpen) {
@@ -99,7 +109,8 @@ let TopNavBarItem = (_dec = withDeterministicId(), _dec2 = withStyle(generateSty
99
109
  this._tooltipContentId = props.deterministicId('TopNavBarItem-tooltip');
100
110
  this.state = {
101
111
  isSubmenuOpen: false,
102
- isPopoverOpen: false
112
+ isPopoverOpen: false,
113
+ isFocused: false
103
114
  };
104
115
  }
105
116
  componentDidMount() {
@@ -265,7 +276,8 @@ let TopNavBarItem = (_dec = withDeterministicId(), _dec2 = withStyle(generateSty
265
276
  renderSubmenu = _this$props5.renderSubmenu,
266
277
  statusOriginal = _this$props5.status,
267
278
  renderAvatar = _this$props5.renderAvatar,
268
- renderIcon = _this$props5.renderIcon;
279
+ renderIcon = _this$props5.renderIcon,
280
+ withFocusOutline = _this$props5.withFocusOutline;
269
281
  let href = hrefOriginal;
270
282
  let onClick = onClickOriginal;
271
283
  let status = statusOriginal;
@@ -314,15 +326,16 @@ let TopNavBarItem = (_dec = withDeterministicId(), _dec2 = withStyle(generateSty
314
326
  onClick,
315
327
  onMouseOver,
316
328
  onMouseOut,
317
- onFocus,
318
- onBlur,
329
+ onFocus: createChainedFunction(onFocus, this.onFocus),
330
+ onBlur: createChainedFunction(onBlur, this.onBlur),
319
331
  onKeyDown: createChainedFunction(onKeyDown, this.handleKeyDown),
320
332
  onKeyUp,
321
333
  renderIcon,
322
334
  themeOverride: this.buttonThemeOverride,
323
335
  elementRef: e => {
324
336
  this.handleItemRef(e);
325
- }
337
+ },
338
+ withFocusOutline: withFocusOutline || this.hasOpenPopover || this.state.isFocused
326
339
  };
327
340
  }
328
341
  renderContent() {
@@ -62,7 +62,8 @@ const propTypes = {
62
62
  onKeyUp: PropTypes.func,
63
63
  elementRef: PropTypes.func,
64
64
  itemRef: PropTypes.func,
65
- shouldCloseOnClick: PropTypes.oneOf(['auto', 'always', 'never'])
65
+ shouldCloseOnClick: PropTypes.oneOf(['auto', 'always', 'never']),
66
+ withFocusOutline: PropTypes.bool
66
67
  };
67
- const allowedProps = ['id', 'as', 'children', 'variant', 'status', 'renderSubmenu', 'showSubmenuChevron', 'customPopoverConfig', 'tooltip', 'renderAvatar', 'renderIcon', 'href', 'onClick', 'onHiddenClick', 'onSubmenuToggle', 'onMouseOver', 'onMouseOut', 'onFocus', 'onBlur', 'onKeyDown', 'onKeyUp', 'elementRef', 'itemRef', 'shouldCloseOnClick'];
68
+ const allowedProps = ['id', 'as', 'children', 'variant', 'status', 'renderSubmenu', 'showSubmenuChevron', 'customPopoverConfig', 'tooltip', 'renderAvatar', 'renderIcon', 'href', 'onClick', 'onHiddenClick', 'onSubmenuToggle', 'onMouseOver', 'onMouseOut', 'onFocus', 'onBlur', 'onKeyDown', 'onKeyUp', 'elementRef', 'itemRef', 'shouldCloseOnClick', 'withFocusOutline'];
68
69
  export { propTypes, allowedProps, topNavBarItemTooltipPropType };
@@ -255,7 +255,8 @@ let TopNavBarSmallViewportLayout = (_dec = withDeterministicId(), _dec2 = withSt
255
255
  itemSpacing: '0.375rem'
256
256
  },
257
257
  'aria-haspopup': 'menu',
258
- 'aria-expanded': isDropdownMenuOpen
258
+ 'aria-expanded': isDropdownMenuOpen,
259
+ withFocusOutline: isDropdownMenuOpen ? true : void 0
259
260
  };
260
261
  const alternativeTitleIconProps = {
261
262
  size: 'x-small',
@@ -84,6 +84,16 @@ let TopNavBarItem = exports.TopNavBarItem = (_dec = (0, _withDeterministicId.wit
84
84
  itemRef(el);
85
85
  }
86
86
  };
87
+ this.onFocus = () => {
88
+ this.setState({
89
+ isFocused: true
90
+ });
91
+ };
92
+ this.onBlur = () => {
93
+ this.setState({
94
+ isFocused: false
95
+ });
96
+ };
87
97
  this.handleKeyDown = e => {
88
98
  if (e.key === 'ArrowDown') {
89
99
  if (this.shouldRenderSubmenu && !this.state.isSubmenuOpen || this.shouldRenderPopover && !this.state.isPopoverOpen) {
@@ -111,7 +121,8 @@ let TopNavBarItem = exports.TopNavBarItem = (_dec = (0, _withDeterministicId.wit
111
121
  this._tooltipContentId = props.deterministicId('TopNavBarItem-tooltip');
112
122
  this.state = {
113
123
  isSubmenuOpen: false,
114
- isPopoverOpen: false
124
+ isPopoverOpen: false,
125
+ isFocused: false
115
126
  };
116
127
  }
117
128
  componentDidMount() {
@@ -277,7 +288,8 @@ let TopNavBarItem = exports.TopNavBarItem = (_dec = (0, _withDeterministicId.wit
277
288
  renderSubmenu = _this$props5.renderSubmenu,
278
289
  statusOriginal = _this$props5.status,
279
290
  renderAvatar = _this$props5.renderAvatar,
280
- renderIcon = _this$props5.renderIcon;
291
+ renderIcon = _this$props5.renderIcon,
292
+ withFocusOutline = _this$props5.withFocusOutline;
281
293
  let href = hrefOriginal;
282
294
  let onClick = onClickOriginal;
283
295
  let status = statusOriginal;
@@ -326,15 +338,16 @@ let TopNavBarItem = exports.TopNavBarItem = (_dec = (0, _withDeterministicId.wit
326
338
  onClick,
327
339
  onMouseOver,
328
340
  onMouseOut,
329
- onFocus,
330
- onBlur,
341
+ onFocus: (0, _createChainedFunction.createChainedFunction)(onFocus, this.onFocus),
342
+ onBlur: (0, _createChainedFunction.createChainedFunction)(onBlur, this.onBlur),
331
343
  onKeyDown: (0, _createChainedFunction.createChainedFunction)(onKeyDown, this.handleKeyDown),
332
344
  onKeyUp,
333
345
  renderIcon,
334
346
  themeOverride: this.buttonThemeOverride,
335
347
  elementRef: e => {
336
348
  this.handleItemRef(e);
337
- }
349
+ },
350
+ withFocusOutline: withFocusOutline || this.hasOpenPopover || this.state.isFocused
338
351
  };
339
352
  }
340
353
  renderContent() {
@@ -69,6 +69,7 @@ const propTypes = exports.propTypes = {
69
69
  onKeyUp: _propTypes.default.func,
70
70
  elementRef: _propTypes.default.func,
71
71
  itemRef: _propTypes.default.func,
72
- shouldCloseOnClick: _propTypes.default.oneOf(['auto', 'always', 'never'])
72
+ shouldCloseOnClick: _propTypes.default.oneOf(['auto', 'always', 'never']),
73
+ withFocusOutline: _propTypes.default.bool
73
74
  };
74
- const allowedProps = exports.allowedProps = ['id', 'as', 'children', 'variant', 'status', 'renderSubmenu', 'showSubmenuChevron', 'customPopoverConfig', 'tooltip', 'renderAvatar', 'renderIcon', 'href', 'onClick', 'onHiddenClick', 'onSubmenuToggle', 'onMouseOver', 'onMouseOut', 'onFocus', 'onBlur', 'onKeyDown', 'onKeyUp', 'elementRef', 'itemRef', 'shouldCloseOnClick'];
75
+ const allowedProps = exports.allowedProps = ['id', 'as', 'children', 'variant', 'status', 'renderSubmenu', 'showSubmenuChevron', 'customPopoverConfig', 'tooltip', 'renderAvatar', 'renderIcon', 'href', 'onClick', 'onHiddenClick', 'onSubmenuToggle', 'onMouseOver', 'onMouseOut', 'onFocus', 'onBlur', 'onKeyDown', 'onKeyUp', 'elementRef', 'itemRef', 'shouldCloseOnClick', 'withFocusOutline'];
@@ -267,7 +267,8 @@ let TopNavBarSmallViewportLayout = exports.TopNavBarSmallViewportLayout = (_dec
267
267
  itemSpacing: '0.375rem'
268
268
  },
269
269
  'aria-haspopup': 'menu',
270
- 'aria-expanded': isDropdownMenuOpen
270
+ 'aria-expanded': isDropdownMenuOpen,
271
+ withFocusOutline: isDropdownMenuOpen ? true : void 0
271
272
  };
272
273
  const alternativeTitleIconProps = {
273
274
  size: 'x-small',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instructure/ui-top-nav-bar",
3
- "version": "10.18.1",
3
+ "version": "10.18.2-snapshot-0",
4
4
  "description": "A UI component library made by Instructure Inc.",
5
5
  "author": "Instructure, Inc. Engineering and Product Design",
6
6
  "module": "./es/index.js",
@@ -24,38 +24,38 @@
24
24
  "license": "MIT",
25
25
  "dependencies": {
26
26
  "@babel/runtime": "^7.26.0",
27
- "@instructure/console": "10.18.1",
28
- "@instructure/emotion": "10.18.1",
29
- "@instructure/shared-types": "10.18.1",
30
- "@instructure/ui-a11y-content": "10.18.1",
31
- "@instructure/ui-avatar": "10.18.1",
32
- "@instructure/ui-breadcrumb": "10.18.1",
33
- "@instructure/ui-buttons": "10.18.1",
34
- "@instructure/ui-dialog": "10.18.1",
35
- "@instructure/ui-dom-utils": "10.18.1",
36
- "@instructure/ui-drilldown": "10.18.1",
37
- "@instructure/ui-icons": "10.18.1",
38
- "@instructure/ui-link": "10.18.1",
39
- "@instructure/ui-popover": "10.18.1",
40
- "@instructure/ui-prop-types": "10.18.1",
41
- "@instructure/ui-react-utils": "10.18.1",
42
- "@instructure/ui-responsive": "10.18.1",
43
- "@instructure/ui-testable": "10.18.1",
44
- "@instructure/ui-tooltip": "10.18.1",
45
- "@instructure/ui-tray": "10.18.1",
46
- "@instructure/ui-truncate-list": "10.18.1",
47
- "@instructure/ui-utils": "10.18.1",
48
- "@instructure/ui-view": "10.18.1",
27
+ "@instructure/console": "10.18.2-snapshot-0",
28
+ "@instructure/emotion": "10.18.2-snapshot-0",
29
+ "@instructure/shared-types": "10.18.2-snapshot-0",
30
+ "@instructure/ui-a11y-content": "10.18.2-snapshot-0",
31
+ "@instructure/ui-avatar": "10.18.2-snapshot-0",
32
+ "@instructure/ui-breadcrumb": "10.18.2-snapshot-0",
33
+ "@instructure/ui-buttons": "10.18.2-snapshot-0",
34
+ "@instructure/ui-dialog": "10.18.2-snapshot-0",
35
+ "@instructure/ui-dom-utils": "10.18.2-snapshot-0",
36
+ "@instructure/ui-drilldown": "10.18.2-snapshot-0",
37
+ "@instructure/ui-icons": "10.18.2-snapshot-0",
38
+ "@instructure/ui-link": "10.18.2-snapshot-0",
39
+ "@instructure/ui-popover": "10.18.2-snapshot-0",
40
+ "@instructure/ui-prop-types": "10.18.2-snapshot-0",
41
+ "@instructure/ui-react-utils": "10.18.2-snapshot-0",
42
+ "@instructure/ui-responsive": "10.18.2-snapshot-0",
43
+ "@instructure/ui-testable": "10.18.2-snapshot-0",
44
+ "@instructure/ui-tooltip": "10.18.2-snapshot-0",
45
+ "@instructure/ui-tray": "10.18.2-snapshot-0",
46
+ "@instructure/ui-truncate-list": "10.18.2-snapshot-0",
47
+ "@instructure/ui-utils": "10.18.2-snapshot-0",
48
+ "@instructure/ui-view": "10.18.2-snapshot-0",
49
49
  "prop-types": "^15.8.1"
50
50
  },
51
51
  "devDependencies": {
52
- "@instructure/ui-axe-check": "10.18.1",
53
- "@instructure/ui-babel-preset": "10.18.1",
54
- "@instructure/ui-color-utils": "10.18.1",
55
- "@instructure/ui-scripts": "10.18.1",
56
- "@instructure/ui-test-locator": "10.18.1",
57
- "@instructure/ui-test-utils": "10.18.1",
58
- "@instructure/ui-themes": "10.18.1",
52
+ "@instructure/ui-axe-check": "10.18.2-snapshot-0",
53
+ "@instructure/ui-babel-preset": "10.18.2-snapshot-0",
54
+ "@instructure/ui-color-utils": "10.18.2-snapshot-0",
55
+ "@instructure/ui-scripts": "10.18.2-snapshot-0",
56
+ "@instructure/ui-test-locator": "10.18.2-snapshot-0",
57
+ "@instructure/ui-test-utils": "10.18.2-snapshot-0",
58
+ "@instructure/ui-themes": "10.18.2-snapshot-0",
59
59
  "@testing-library/jest-dom": "^6.6.3",
60
60
  "@testing-library/react": "^16.0.1",
61
61
  "@testing-library/user-event": "^14.5.2",
@@ -128,7 +128,8 @@ class TopNavBarItem extends Component<TopNavBarItemProps, TopNavBarItemState> {
128
128
 
129
129
  this.state = {
130
130
  isSubmenuOpen: false,
131
- isPopoverOpen: false
131
+ isPopoverOpen: false,
132
+ isFocused: false
132
133
  }
133
134
  }
134
135
 
@@ -347,7 +348,8 @@ class TopNavBarItem extends Component<TopNavBarItemProps, TopNavBarItemState> {
347
348
  renderSubmenu,
348
349
  status: statusOriginal,
349
350
  renderAvatar,
350
- renderIcon
351
+ renderIcon,
352
+ withFocusOutline
351
353
  } = this.props
352
354
 
353
355
  let href = hrefOriginal
@@ -423,18 +425,28 @@ class TopNavBarItem extends Component<TopNavBarItemProps, TopNavBarItemState> {
423
425
  onClick,
424
426
  onMouseOver,
425
427
  onMouseOut,
426
- onFocus,
427
- onBlur,
428
+ onFocus: createChainedFunction(onFocus, this.onFocus),
429
+ onBlur: createChainedFunction(onBlur, this.onBlur),
428
430
  onKeyDown: createChainedFunction(onKeyDown, this.handleKeyDown),
429
431
  onKeyUp,
430
432
  renderIcon,
431
433
  themeOverride: this.buttonThemeOverride,
432
434
  elementRef: (e) => {
433
435
  this.handleItemRef(e as HTMLButtonElement | HTMLLinkElement)
434
- }
436
+ },
437
+ withFocusOutline:
438
+ withFocusOutline || this.hasOpenPopover || this.state.isFocused
435
439
  }
436
440
  }
437
441
 
442
+ onFocus = () => {
443
+ this.setState({ isFocused: true })
444
+ }
445
+
446
+ onBlur = () => {
447
+ this.setState({ isFocused: false })
448
+ }
449
+
438
450
  handleKeyDown: TopNavBarItemProps['onKeyDown'] = (e) => {
439
451
  if (e.key === 'ArrowDown') {
440
452
  if (
@@ -254,6 +254,14 @@ type TopNavBarItemOwnProps = {
254
254
  * Should close the container menu component, if clicked on the option marked with this prop
255
255
  */
256
256
  shouldCloseOnClick?: ShouldCloseOnClick
257
+
258
+ /**
259
+ * Manually control if this component should display a focus outline.
260
+ *
261
+ * When left `undefined` (which is the default) the focus outline will display
262
+ * if this component is focusable and receives focus or has an open popover.
263
+ */
264
+ withFocusOutline?: boolean
257
265
  }
258
266
 
259
267
  type PropKeys = keyof TopNavBarItemOwnProps
@@ -281,6 +289,7 @@ type TopNavBarItemStyle = ComponentStyle<
281
289
  type TopNavBarItemState = {
282
290
  isSubmenuOpen: boolean
283
291
  isPopoverOpen: boolean
292
+ isFocused: boolean
284
293
  }
285
294
 
286
295
  type TopNavBarItemStyleProps = {
@@ -316,7 +325,8 @@ const propTypes: PropValidators<PropKeys> = {
316
325
  onKeyUp: PropTypes.func,
317
326
  elementRef: PropTypes.func,
318
327
  itemRef: PropTypes.func,
319
- shouldCloseOnClick: PropTypes.oneOf(['auto', 'always', 'never'])
328
+ shouldCloseOnClick: PropTypes.oneOf(['auto', 'always', 'never']),
329
+ withFocusOutline: PropTypes.bool
320
330
  }
321
331
 
322
332
  const allowedProps: AllowedPropKeys = [
@@ -343,7 +353,8 @@ const allowedProps: AllowedPropKeys = [
343
353
  'onKeyUp',
344
354
  'elementRef',
345
355
  'itemRef',
346
- 'shouldCloseOnClick'
356
+ 'shouldCloseOnClick',
357
+ 'withFocusOutline'
347
358
  ]
348
359
 
349
360
  export type {
@@ -400,7 +400,8 @@ class TopNavBarSmallViewportLayout extends Component<
400
400
  tooltip: dropdownMenuToggleButtonTooltip,
401
401
  themeOverride: { itemSpacing: '0.375rem' },
402
402
  'aria-haspopup': 'menu',
403
- 'aria-expanded': isDropdownMenuOpen
403
+ 'aria-expanded': isDropdownMenuOpen,
404
+ withFocusOutline: isDropdownMenuOpen ? true : undefined
404
405
  }
405
406
 
406
407
  const alternativeTitleIconProps = {