@carbon/react 1.55.0-rc.0 → 1.56.0-rc.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/.playwright/INTERNAL_AVT_REPORT_DO_NOT_USE.json +1112 -989
- package/es/components/ComposedModal/ComposedModal.d.ts +1 -1
- package/es/components/ComposedModal/ComposedModal.js +4 -4
- package/es/components/Modal/Modal.js +1 -1
- package/es/components/Tabs/Tabs.js +44 -13
- package/es/components/UIShell/HeaderMenu.d.ts +219 -0
- package/es/components/UIShell/HeaderMenu.js +22 -10
- package/lib/components/ComposedModal/ComposedModal.d.ts +1 -1
- package/lib/components/ComposedModal/ComposedModal.js +3 -3
- package/lib/components/Modal/Modal.js +1 -1
- package/lib/components/Tabs/Tabs.js +44 -13
- package/lib/components/UIShell/HeaderMenu.d.ts +219 -0
- package/lib/components/UIShell/HeaderMenu.js +22 -10
- package/package.json +5 -5
|
@@ -68,7 +68,7 @@ export interface ComposedModalProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
68
68
|
*/
|
|
69
69
|
selectorPrimaryFocus?: string;
|
|
70
70
|
/** Specify the CSS selectors that match the floating menus. */
|
|
71
|
-
selectorsFloatingMenus?:
|
|
71
|
+
selectorsFloatingMenus?: string[];
|
|
72
72
|
size?: 'xs' | 'sm' | 'md' | 'lg';
|
|
73
73
|
/**
|
|
74
74
|
* **Experimental**: Provide a `Slug` component to be rendered inside the `ComposedModal` component
|
|
@@ -17,7 +17,7 @@ import mergeRefs from '../../tools/mergeRefs.js';
|
|
|
17
17
|
import cx from 'classnames';
|
|
18
18
|
import toggleClass from '../../tools/toggleClass.js';
|
|
19
19
|
import requiredIfGivenPropIsTruthy from '../../prop-types/requiredIfGivenPropIsTruthy.js';
|
|
20
|
-
import wrapFocus, { wrapFocusWithoutSentinels } from '../../internal/wrapFocus.js';
|
|
20
|
+
import wrapFocus, { wrapFocusWithoutSentinels, elementOrParentIsFloatingMenu } from '../../internal/wrapFocus.js';
|
|
21
21
|
import { usePrefix } from '../../internal/usePrefix.js';
|
|
22
22
|
import { useFeatureFlag } from '../FeatureFlags/index.js';
|
|
23
23
|
import { match } from '../../internal/keyboard/match.js';
|
|
@@ -148,9 +148,9 @@ const ComposedModal = /*#__PURE__*/React__default.forwardRef(function ComposedMo
|
|
|
148
148
|
onKeyDown?.(event);
|
|
149
149
|
}
|
|
150
150
|
function handleMousedown(evt) {
|
|
151
|
+
const target = evt.target;
|
|
151
152
|
evt.stopPropagation();
|
|
152
|
-
|
|
153
|
-
if (!isInside && !preventCloseOnClickOutside) {
|
|
153
|
+
if (!preventCloseOnClickOutside && !elementOrParentIsFloatingMenu(target, selectorsFloatingMenus) && innerModal.current && !innerModal.current.contains(target)) {
|
|
154
154
|
closeModal(evt);
|
|
155
155
|
}
|
|
156
156
|
}
|
|
@@ -336,7 +336,7 @@ ComposedModal.propTypes = {
|
|
|
336
336
|
/**
|
|
337
337
|
* Specify the CSS selectors that match the floating menus
|
|
338
338
|
*/
|
|
339
|
-
selectorsFloatingMenus: PropTypes.arrayOf(PropTypes.string),
|
|
339
|
+
selectorsFloatingMenus: PropTypes.arrayOf(PropTypes.string.isRequired),
|
|
340
340
|
/**
|
|
341
341
|
* Specify the size variant.
|
|
342
342
|
*/
|
|
@@ -108,7 +108,7 @@ const Modal = /*#__PURE__*/React__default.forwardRef(function Modal(_ref, ref) {
|
|
|
108
108
|
function handleMousedown(evt) {
|
|
109
109
|
const target = evt.target;
|
|
110
110
|
evt.stopPropagation();
|
|
111
|
-
if (
|
|
111
|
+
if (!preventCloseOnClickOutside && !elementOrParentIsFloatingMenu(target, selectorsFloatingMenus) && innerModal.current && !innerModal.current.contains(target)) {
|
|
112
112
|
onRequestClose(evt);
|
|
113
113
|
}
|
|
114
114
|
}
|
|
@@ -199,7 +199,8 @@ function TabList(_ref2) {
|
|
|
199
199
|
// TODO: V12 - Remove this class
|
|
200
200
|
[`${prefix}--layout--size-lg`]: iconSize === 'lg',
|
|
201
201
|
[`${prefix}--tabs--tall`]: hasSecondaryLabelTabs,
|
|
202
|
-
[`${prefix}--tabs--full-width`]: distributeWidth
|
|
202
|
+
[`${prefix}--tabs--full-width`]: distributeWidth,
|
|
203
|
+
[`${prefix}--tabs--dismissable`]: dismissable
|
|
203
204
|
}, customClassName);
|
|
204
205
|
|
|
205
206
|
// Previous Button
|
|
@@ -541,6 +542,25 @@ const Tab = /*#__PURE__*/forwardRef(function Tab(_ref5, forwardRef) {
|
|
|
541
542
|
const handleClose = evt => {
|
|
542
543
|
evt.stopPropagation();
|
|
543
544
|
onTabCloseRequest?.(index);
|
|
545
|
+
|
|
546
|
+
// set focus after removing tab
|
|
547
|
+
if (tabRef.current && tabRef.current.parentElement) {
|
|
548
|
+
// determine number of tabs, excluding disabled
|
|
549
|
+
const tabCount = Array.from(tabRef.current.parentElement.childNodes).filter(node => {
|
|
550
|
+
const element = node;
|
|
551
|
+
return element.classList.contains('cds--tabs__nav-link') && !element.classList.contains('cds--tabs__nav-item--disabled');
|
|
552
|
+
}).length;
|
|
553
|
+
|
|
554
|
+
// if not removing last tab focus on next tab
|
|
555
|
+
if (tabRef.current && index + 1 !== tabCount) {
|
|
556
|
+
tabRef.current.focus();
|
|
557
|
+
}
|
|
558
|
+
// if removing last tab focus on previous tab
|
|
559
|
+
else {
|
|
560
|
+
const prevTabIndex = (tabCount - 2) * 2;
|
|
561
|
+
tabRef.current.parentElement.childNodes[prevTabIndex]?.focus();
|
|
562
|
+
}
|
|
563
|
+
}
|
|
544
564
|
};
|
|
545
565
|
const handleKeyDown = event => {
|
|
546
566
|
if (dismissable && match(event, Delete)) {
|
|
@@ -549,20 +569,31 @@ const Tab = /*#__PURE__*/forwardRef(function Tab(_ref5, forwardRef) {
|
|
|
549
569
|
onKeyDown?.(event);
|
|
550
570
|
};
|
|
551
571
|
const DismissIcon = /*#__PURE__*/React__default.createElement("div", {
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
572
|
+
className: cx({
|
|
573
|
+
[`${prefix}--tabs__nav-item--close`]: dismissable,
|
|
574
|
+
[`${prefix}--tabs__nav-item--close--hidden`]: !dismissable
|
|
575
|
+
})
|
|
576
|
+
}, /*#__PURE__*/React__default.createElement("button", {
|
|
577
|
+
type: "button",
|
|
578
|
+
tabIndex: selectedIndex === index && dismissable ? 0 : -1,
|
|
579
|
+
"aria-disabled": disabled,
|
|
580
|
+
"aria-hidden": selectedIndex === index && dismissable ? 'false' : 'true',
|
|
581
|
+
disabled: disabled,
|
|
582
|
+
className: cx({
|
|
583
|
+
[`${prefix}--tabs__nav-item--close-icon`]: dismissable,
|
|
584
|
+
[`${prefix}--visually-hidden`]: !dismissable,
|
|
585
|
+
[`${prefix}--tabs__nav-item--close-icon--selected`]: selectedIndex === index,
|
|
586
|
+
[`${prefix}--tabs__nav-item--close-icon--disabled`]: disabled
|
|
556
587
|
}),
|
|
557
588
|
onClick: handleClose,
|
|
558
|
-
title:
|
|
589
|
+
title: `Remove ${typeof children === 'string' ? children : ''} tab`,
|
|
559
590
|
ref: dismissIconRef
|
|
560
591
|
}, /*#__PURE__*/React__default.createElement(Close, {
|
|
561
|
-
"aria-hidden": dismissable ? 'false' : 'true',
|
|
562
|
-
"aria-label":
|
|
563
|
-
}));
|
|
592
|
+
"aria-hidden": selectedIndex === index && dismissable ? 'false' : 'true',
|
|
593
|
+
"aria-label": `Press delete to remove ${typeof children === 'string' ? children : ''} tab`
|
|
594
|
+
})));
|
|
564
595
|
const hasIcon = Icon ?? dismissable;
|
|
565
|
-
return /*#__PURE__*/React__default.createElement(BaseComponent, _extends({}, rest, {
|
|
596
|
+
return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement(BaseComponent, _extends({}, rest, {
|
|
566
597
|
"aria-controls": panelId,
|
|
567
598
|
"aria-disabled": disabled,
|
|
568
599
|
"aria-selected": selectedIndex === index,
|
|
@@ -589,17 +620,17 @@ const Tab = /*#__PURE__*/forwardRef(function Tab(_ref5, forwardRef) {
|
|
|
589
620
|
size: 16
|
|
590
621
|
})), /*#__PURE__*/React__default.createElement(Text, {
|
|
591
622
|
className: `${prefix}--tabs__nav-item-label`
|
|
592
|
-
}, children), /*#__PURE__*/React__default.createElement("div", {
|
|
623
|
+
}, children), !dismissable && Icon && /*#__PURE__*/React__default.createElement("div", {
|
|
593
624
|
className: cx(`${prefix}--tabs__nav-item--icon`, {
|
|
594
625
|
[`${prefix}--visually-hidden`]: !hasIcon
|
|
595
626
|
})
|
|
596
|
-
},
|
|
627
|
+
}, !dismissable && Icon && /*#__PURE__*/React__default.createElement(Icon, {
|
|
597
628
|
size: 16
|
|
598
629
|
}))), hasSecondaryLabel && secondaryLabel && /*#__PURE__*/React__default.createElement(Text, {
|
|
599
630
|
as: "div",
|
|
600
631
|
className: `${prefix}--tabs__nav-item-secondary-label`,
|
|
601
632
|
title: secondaryLabel
|
|
602
|
-
}, secondaryLabel));
|
|
633
|
+
}, secondaryLabel)), DismissIcon);
|
|
603
634
|
});
|
|
604
635
|
Tab.propTypes = {
|
|
605
636
|
/**
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2016, 2023
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import PropTypes from 'prop-types';
|
|
9
|
+
/**
|
|
10
|
+
* `HeaderMenu` is used to render submenu's in the `Header`. Most often children
|
|
11
|
+
* will be a `HeaderMenuItem`. It handles certain keyboard events to help
|
|
12
|
+
* with managing focus. It also passes along refs to each child so that it can
|
|
13
|
+
* help manage focus state of its children.
|
|
14
|
+
*/
|
|
15
|
+
interface HeaderMenuProps {
|
|
16
|
+
/**
|
|
17
|
+
* Required props for the accessibility label of the menu
|
|
18
|
+
*/
|
|
19
|
+
'aria-label'?: string;
|
|
20
|
+
'aria-labelledby'?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Optionally provide a custom class to apply to the underlying `<li>` node
|
|
23
|
+
*/
|
|
24
|
+
className?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Provide a custom ref handler for the menu button
|
|
27
|
+
*/
|
|
28
|
+
focusRef?: React.Ref<any>;
|
|
29
|
+
/**
|
|
30
|
+
* Applies selected styles to the item if a user sets this to true and `aria-current !== 'page'`.
|
|
31
|
+
*/
|
|
32
|
+
isActive?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Applies selected styles to the item if a user sets this to true and `aria-current !== 'page'`.
|
|
35
|
+
* @deprecated Please use `isActive` instead. This will be removed in the next major release.
|
|
36
|
+
*/
|
|
37
|
+
isCurrentPage?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Provide a label for the link text
|
|
40
|
+
*/
|
|
41
|
+
menuLinkName: string;
|
|
42
|
+
/**
|
|
43
|
+
* Optionally provide an onBlur handler that is called when the underlying
|
|
44
|
+
* button fires it's onblur event
|
|
45
|
+
*/
|
|
46
|
+
onBlur?: (event: React.FocusEvent<HTMLLIElement>) => void;
|
|
47
|
+
/**
|
|
48
|
+
* Optionally provide an onClick handler that is called when the underlying
|
|
49
|
+
* button fires it's onclick event
|
|
50
|
+
*/
|
|
51
|
+
onClick?: (event: React.MouseEvent<HTMLLIElement, MouseEvent>) => void;
|
|
52
|
+
/**
|
|
53
|
+
* Optionally provide an onKeyDown handler that is called when the underlying
|
|
54
|
+
* button fires it's onkeydown event
|
|
55
|
+
*/
|
|
56
|
+
onKeyDown?: (event: React.KeyboardEvent<HTMLLIElement>) => void;
|
|
57
|
+
/**
|
|
58
|
+
* Optional component to render instead of string
|
|
59
|
+
*/
|
|
60
|
+
renderMenuContent?: () => JSX.Element;
|
|
61
|
+
/**
|
|
62
|
+
* Optionally provide a tabIndex for the underlying menu button
|
|
63
|
+
*/
|
|
64
|
+
tabIndex?: number;
|
|
65
|
+
/**
|
|
66
|
+
* The children should be a series of `HeaderMenuItem` components.
|
|
67
|
+
*/
|
|
68
|
+
children?: React.ReactNode;
|
|
69
|
+
}
|
|
70
|
+
interface HeaderMenuState {
|
|
71
|
+
expanded: boolean;
|
|
72
|
+
selectedIndex: number | null;
|
|
73
|
+
}
|
|
74
|
+
declare class HeaderMenu extends React.Component<HeaderMenuProps, HeaderMenuState> {
|
|
75
|
+
static propTypes: {
|
|
76
|
+
/**
|
|
77
|
+
* Optionally provide a custom class to apply to the underlying `<li>` node
|
|
78
|
+
*/
|
|
79
|
+
className: PropTypes.Requireable<string>;
|
|
80
|
+
/**
|
|
81
|
+
* Provide a custom ref handler for the menu button
|
|
82
|
+
*/
|
|
83
|
+
focusRef: PropTypes.Requireable<(...args: any[]) => any>;
|
|
84
|
+
/**
|
|
85
|
+
* Applies selected styles to the item if a user sets this to true and `aria-current !== 'page'`.
|
|
86
|
+
*/
|
|
87
|
+
isActive: PropTypes.Requireable<boolean>;
|
|
88
|
+
/**
|
|
89
|
+
* Applies selected styles to the item if a user sets this to true and `aria-current !== 'page'`.
|
|
90
|
+
* @deprecated Please use `isActive` instead. This will be removed in the next major release.
|
|
91
|
+
*/
|
|
92
|
+
isCurrentPage: (props: any, propName: any, componentName: any, ...rest: any[]) => any;
|
|
93
|
+
/**
|
|
94
|
+
* Provide a label for the link text
|
|
95
|
+
*/
|
|
96
|
+
menuLinkName: PropTypes.Validator<string>;
|
|
97
|
+
/**
|
|
98
|
+
* Optionally provide an onBlur handler that is called when the underlying
|
|
99
|
+
* button fires it's onblur event
|
|
100
|
+
*/
|
|
101
|
+
onBlur: PropTypes.Requireable<(...args: any[]) => any>;
|
|
102
|
+
/**
|
|
103
|
+
* Optionally provide an onClick handler that is called when the underlying
|
|
104
|
+
* button fires it's onclick event
|
|
105
|
+
*/
|
|
106
|
+
onClick: PropTypes.Requireable<(...args: any[]) => any>;
|
|
107
|
+
/**
|
|
108
|
+
* Optionally provide an onKeyDown handler that is called when the underlying
|
|
109
|
+
* button fires it's onkeydown event
|
|
110
|
+
*/
|
|
111
|
+
onKeyDown: PropTypes.Requireable<(...args: any[]) => any>;
|
|
112
|
+
/**
|
|
113
|
+
* Optional component to render instead of string
|
|
114
|
+
*/
|
|
115
|
+
renderMenuContent: PropTypes.Requireable<(...args: any[]) => any>;
|
|
116
|
+
/**
|
|
117
|
+
* Optionally provide a tabIndex for the underlying menu button
|
|
118
|
+
*/
|
|
119
|
+
tabIndex: PropTypes.Requireable<number>;
|
|
120
|
+
0: string;
|
|
121
|
+
length: 1;
|
|
122
|
+
toString(): string;
|
|
123
|
+
toLocaleString(): string;
|
|
124
|
+
pop(): string | undefined;
|
|
125
|
+
push(...items: string[]): number;
|
|
126
|
+
concat(...items: ConcatArray<string>[]): string[];
|
|
127
|
+
concat(...items: (string | ConcatArray<string>)[]): string[];
|
|
128
|
+
join(separator?: string | undefined): string;
|
|
129
|
+
reverse(): string[];
|
|
130
|
+
shift(): string | undefined;
|
|
131
|
+
slice(start?: number | undefined, end?: number | undefined): string[];
|
|
132
|
+
sort(compareFn?: ((a: string, b: string) => number) | undefined): [key: string];
|
|
133
|
+
splice(start: number, deleteCount?: number | undefined): string[];
|
|
134
|
+
splice(start: number, deleteCount: number, ...items: string[]): string[];
|
|
135
|
+
unshift(...items: string[]): number;
|
|
136
|
+
indexOf(searchElement: string, fromIndex?: number | undefined): number;
|
|
137
|
+
lastIndexOf(searchElement: string, fromIndex?: number | undefined): number;
|
|
138
|
+
every<S extends string>(predicate: (value: string, index: number, array: string[]) => value is S, thisArg?: any): this is S[];
|
|
139
|
+
every(predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): boolean;
|
|
140
|
+
some(predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): boolean;
|
|
141
|
+
forEach(callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any): void;
|
|
142
|
+
map<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any): U[];
|
|
143
|
+
filter<S_1 extends string>(predicate: (value: string, index: number, array: string[]) => value is S_1, thisArg?: any): S_1[];
|
|
144
|
+
filter(predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): string[];
|
|
145
|
+
reduce(callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string;
|
|
146
|
+
reduce(callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string;
|
|
147
|
+
reduce<U_1>(callbackfn: (previousValue: U_1, currentValue: string, currentIndex: number, array: string[]) => U_1, initialValue: U_1): U_1;
|
|
148
|
+
reduceRight(callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string;
|
|
149
|
+
reduceRight(callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string;
|
|
150
|
+
reduceRight<U_2>(callbackfn: (previousValue: U_2, currentValue: string, currentIndex: number, array: string[]) => U_2, initialValue: U_2): U_2;
|
|
151
|
+
find<S_2 extends string>(predicate: (this: void, value: string, index: number, obj: string[]) => value is S_2, thisArg?: any): S_2 | undefined;
|
|
152
|
+
find(predicate: (value: string, index: number, obj: string[]) => unknown, thisArg?: any): string | undefined;
|
|
153
|
+
findIndex(predicate: (value: string, index: number, obj: string[]) => unknown, thisArg?: any): number;
|
|
154
|
+
fill(value: string, start?: number | undefined, end?: number | undefined): [key: string];
|
|
155
|
+
copyWithin(target: number, start: number, end?: number | undefined): [key: string];
|
|
156
|
+
entries(): IterableIterator<[number, string]>;
|
|
157
|
+
keys(): IterableIterator<number>;
|
|
158
|
+
values(): IterableIterator<string>;
|
|
159
|
+
includes(searchElement: string, fromIndex?: number | undefined): boolean;
|
|
160
|
+
flatMap<U_3, This = undefined>(callback: (this: This, value: string, index: number, array: string[]) => U_3 | readonly U_3[], thisArg?: This | undefined): U_3[];
|
|
161
|
+
flat<A, D extends number = 1>(this: A, depth?: D | undefined): FlatArray<A, D>[];
|
|
162
|
+
at(index: number): string | undefined;
|
|
163
|
+
[Symbol.iterator](): IterableIterator<string>;
|
|
164
|
+
[Symbol.unscopables](): {
|
|
165
|
+
copyWithin: boolean;
|
|
166
|
+
entries: boolean;
|
|
167
|
+
fill: boolean;
|
|
168
|
+
find: boolean;
|
|
169
|
+
findIndex: boolean;
|
|
170
|
+
keys: boolean;
|
|
171
|
+
values: boolean;
|
|
172
|
+
};
|
|
173
|
+
};
|
|
174
|
+
static contextType: React.Context<string>;
|
|
175
|
+
_subMenus: React.RefObject<HTMLUListElement>;
|
|
176
|
+
private items;
|
|
177
|
+
private menuButtonRef;
|
|
178
|
+
constructor(props: any);
|
|
179
|
+
/**
|
|
180
|
+
* Toggle the expanded state of the menu on click.
|
|
181
|
+
*/
|
|
182
|
+
handleOnClick: (e: any) => void;
|
|
183
|
+
/**
|
|
184
|
+
* Keyboard event handler for the entire menu.
|
|
185
|
+
*/
|
|
186
|
+
handleOnKeyDown: (event: any) => void;
|
|
187
|
+
/**
|
|
188
|
+
* Handle our blur event from our underlying menuitems. Will mostly be used
|
|
189
|
+
* for closing our menu in response to a user clicking off or tabbing out of
|
|
190
|
+
* the menu or menubar.
|
|
191
|
+
*/
|
|
192
|
+
handleOnBlur: (event: any) => void;
|
|
193
|
+
/**
|
|
194
|
+
* ref handler for our menu button. If we are supplied a `focusRef` prop, we also
|
|
195
|
+
* forward along the node.
|
|
196
|
+
*
|
|
197
|
+
* This is useful when this component is a child in a
|
|
198
|
+
* menu or menubar as it will allow the parent to explicitly focus the menu
|
|
199
|
+
* button node when that child should receive focus.
|
|
200
|
+
*/
|
|
201
|
+
handleMenuButtonRef: (node: any) => void;
|
|
202
|
+
/**
|
|
203
|
+
* Handles individual menuitem refs. We assign them to a class instance
|
|
204
|
+
* property so that we can properly manage focus of our children.
|
|
205
|
+
*/
|
|
206
|
+
handleItemRef: (index: any) => (node: any) => void;
|
|
207
|
+
handleMenuClose: (event: any) => void;
|
|
208
|
+
render(): import("react/jsx-runtime").JSX.Element;
|
|
209
|
+
/**
|
|
210
|
+
* We capture the `ref` for each child inside of `this.items` to properly
|
|
211
|
+
* manage focus. In addition to this focus management, all items receive a
|
|
212
|
+
* `tabIndex: -1` so the user won't hit a large number of items in their tab
|
|
213
|
+
* sequence when they might not want to go through all the items.
|
|
214
|
+
*/
|
|
215
|
+
_renderMenuItem: (item: React.ReactNode, index: number) => React.ReactElement<any, string | React.JSXElementConstructor<any>> | undefined;
|
|
216
|
+
}
|
|
217
|
+
declare const HeaderMenuForwardRef: React.ForwardRefExoticComponent<React.RefAttributes<unknown>>;
|
|
218
|
+
export { HeaderMenu };
|
|
219
|
+
export default HeaderMenuForwardRef;
|
|
@@ -23,10 +23,13 @@ import { Enter, Space, Escape } from '../../internal/keyboard/keys.js';
|
|
|
23
23
|
* with managing focus. It also passes along refs to each child so that it can
|
|
24
24
|
* help manage focus state of its children.
|
|
25
25
|
*/
|
|
26
|
+
|
|
26
27
|
class HeaderMenu extends React__default.Component {
|
|
27
28
|
constructor(props) {
|
|
28
29
|
super(props);
|
|
29
30
|
_defineProperty(this, "_subMenus", /*#__PURE__*/React__default.createRef());
|
|
31
|
+
_defineProperty(this, "items", []);
|
|
32
|
+
_defineProperty(this, "menuButtonRef", null);
|
|
30
33
|
/**
|
|
31
34
|
* Toggle the expanded state of the menu on click.
|
|
32
35
|
*/
|
|
@@ -81,8 +84,12 @@ class HeaderMenu extends React__default.Component {
|
|
|
81
84
|
* button node when that child should receive focus.
|
|
82
85
|
*/
|
|
83
86
|
_defineProperty(this, "handleMenuButtonRef", node => {
|
|
84
|
-
|
|
85
|
-
|
|
87
|
+
const {
|
|
88
|
+
focusRef
|
|
89
|
+
} = this.props;
|
|
90
|
+
// Check if focusRef is a function before calling it
|
|
91
|
+
if (typeof focusRef === 'function') {
|
|
92
|
+
focusRef(node);
|
|
86
93
|
}
|
|
87
94
|
this.menuButtonRef = node;
|
|
88
95
|
});
|
|
@@ -104,7 +111,9 @@ class HeaderMenu extends React__default.Component {
|
|
|
104
111
|
}));
|
|
105
112
|
|
|
106
113
|
// Return focus to menu button when the user hits ESC.
|
|
107
|
-
this.menuButtonRef
|
|
114
|
+
if (this.menuButtonRef !== null) {
|
|
115
|
+
this.menuButtonRef.focus();
|
|
116
|
+
}
|
|
108
117
|
return;
|
|
109
118
|
}
|
|
110
119
|
});
|
|
@@ -141,23 +150,25 @@ class HeaderMenu extends React__default.Component {
|
|
|
141
150
|
children,
|
|
142
151
|
renderMenuContent: MenuContent,
|
|
143
152
|
menuLinkName,
|
|
153
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
144
154
|
focusRef,
|
|
145
|
-
// eslint-disable-line no-unused-vars
|
|
146
155
|
onBlur,
|
|
147
156
|
onClick,
|
|
148
157
|
onKeyDown,
|
|
149
158
|
...rest
|
|
150
159
|
} = this.props;
|
|
151
|
-
const hasActiveDescendant = childrenArg => React__default.Children.toArray(childrenArg).some(child =>
|
|
160
|
+
const hasActiveDescendant = childrenArg => React__default.Children.toArray(childrenArg).some(child => /*#__PURE__*/React__default.isValidElement(child) && (
|
|
161
|
+
// This is the type guard
|
|
162
|
+
child.props.isActive || child.props.isCurrentPage || Array.isArray(child.props.children) && hasActiveDescendant(child.props.children)));
|
|
152
163
|
const accessibilityLabel = {
|
|
153
164
|
'aria-label': ariaLabel,
|
|
154
165
|
'aria-labelledby': ariaLabelledBy
|
|
155
166
|
};
|
|
156
167
|
const itemClassName = cx({
|
|
157
168
|
[`${prefix}--header__submenu`]: true,
|
|
158
|
-
[customClassName]: !!customClassName
|
|
169
|
+
[`${customClassName}`]: !!customClassName
|
|
159
170
|
});
|
|
160
|
-
|
|
171
|
+
const isActivePage = isActive ? isActive : isCurrentPage;
|
|
161
172
|
const linkClassName = cx({
|
|
162
173
|
[`${prefix}--header__menu-item`]: true,
|
|
163
174
|
[`${prefix}--header__menu-title`]: true,
|
|
@@ -248,11 +259,12 @@ _defineProperty(HeaderMenu, "propTypes", {
|
|
|
248
259
|
});
|
|
249
260
|
_defineProperty(HeaderMenu, "contextType", PrefixContext);
|
|
250
261
|
const HeaderMenuForwardRef = /*#__PURE__*/React__default.forwardRef((props, ref) => {
|
|
251
|
-
return /*#__PURE__*/React__default.createElement(HeaderMenu, _extends({
|
|
262
|
+
return /*#__PURE__*/React__default.createElement(HeaderMenu, _extends({
|
|
263
|
+
menuLinkName: "link"
|
|
264
|
+
}, props, {
|
|
252
265
|
focusRef: ref
|
|
253
266
|
}));
|
|
254
267
|
});
|
|
255
268
|
HeaderMenuForwardRef.displayName = 'HeaderMenu';
|
|
256
|
-
var HeaderMenuForwardRef$1 = HeaderMenuForwardRef;
|
|
257
269
|
|
|
258
|
-
export { HeaderMenu, HeaderMenuForwardRef
|
|
270
|
+
export { HeaderMenu, HeaderMenuForwardRef as default };
|
|
@@ -68,7 +68,7 @@ export interface ComposedModalProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
68
68
|
*/
|
|
69
69
|
selectorPrimaryFocus?: string;
|
|
70
70
|
/** Specify the CSS selectors that match the floating menus. */
|
|
71
|
-
selectorsFloatingMenus?:
|
|
71
|
+
selectorsFloatingMenus?: string[];
|
|
72
72
|
size?: 'xs' | 'sm' | 'md' | 'lg';
|
|
73
73
|
/**
|
|
74
74
|
* **Experimental**: Provide a `Slug` component to be rendered inside the `ComposedModal` component
|
|
@@ -159,9 +159,9 @@ const ComposedModal = /*#__PURE__*/React__default["default"].forwardRef(function
|
|
|
159
159
|
onKeyDown?.(event);
|
|
160
160
|
}
|
|
161
161
|
function handleMousedown(evt) {
|
|
162
|
+
const target = evt.target;
|
|
162
163
|
evt.stopPropagation();
|
|
163
|
-
|
|
164
|
-
if (!isInside && !preventCloseOnClickOutside) {
|
|
164
|
+
if (!preventCloseOnClickOutside && !wrapFocus.elementOrParentIsFloatingMenu(target, selectorsFloatingMenus) && innerModal.current && !innerModal.current.contains(target)) {
|
|
165
165
|
closeModal(evt);
|
|
166
166
|
}
|
|
167
167
|
}
|
|
@@ -347,7 +347,7 @@ ComposedModal.propTypes = {
|
|
|
347
347
|
/**
|
|
348
348
|
* Specify the CSS selectors that match the floating menus
|
|
349
349
|
*/
|
|
350
|
-
selectorsFloatingMenus: PropTypes__default["default"].arrayOf(PropTypes__default["default"].string),
|
|
350
|
+
selectorsFloatingMenus: PropTypes__default["default"].arrayOf(PropTypes__default["default"].string.isRequired),
|
|
351
351
|
/**
|
|
352
352
|
* Specify the size variant.
|
|
353
353
|
*/
|
|
@@ -119,7 +119,7 @@ const Modal = /*#__PURE__*/React__default["default"].forwardRef(function Modal(_
|
|
|
119
119
|
function handleMousedown(evt) {
|
|
120
120
|
const target = evt.target;
|
|
121
121
|
evt.stopPropagation();
|
|
122
|
-
if (
|
|
122
|
+
if (!preventCloseOnClickOutside && !wrapFocus.elementOrParentIsFloatingMenu(target, selectorsFloatingMenus) && innerModal.current && !innerModal.current.contains(target)) {
|
|
123
123
|
onRequestClose(evt);
|
|
124
124
|
}
|
|
125
125
|
}
|
|
@@ -210,7 +210,8 @@ function TabList(_ref2) {
|
|
|
210
210
|
// TODO: V12 - Remove this class
|
|
211
211
|
[`${prefix}--layout--size-lg`]: iconSize === 'lg',
|
|
212
212
|
[`${prefix}--tabs--tall`]: hasSecondaryLabelTabs,
|
|
213
|
-
[`${prefix}--tabs--full-width`]: distributeWidth
|
|
213
|
+
[`${prefix}--tabs--full-width`]: distributeWidth,
|
|
214
|
+
[`${prefix}--tabs--dismissable`]: dismissable
|
|
214
215
|
}, customClassName);
|
|
215
216
|
|
|
216
217
|
// Previous Button
|
|
@@ -552,6 +553,25 @@ const Tab = /*#__PURE__*/React.forwardRef(function Tab(_ref5, forwardRef) {
|
|
|
552
553
|
const handleClose = evt => {
|
|
553
554
|
evt.stopPropagation();
|
|
554
555
|
onTabCloseRequest?.(index);
|
|
556
|
+
|
|
557
|
+
// set focus after removing tab
|
|
558
|
+
if (tabRef.current && tabRef.current.parentElement) {
|
|
559
|
+
// determine number of tabs, excluding disabled
|
|
560
|
+
const tabCount = Array.from(tabRef.current.parentElement.childNodes).filter(node => {
|
|
561
|
+
const element = node;
|
|
562
|
+
return element.classList.contains('cds--tabs__nav-link') && !element.classList.contains('cds--tabs__nav-item--disabled');
|
|
563
|
+
}).length;
|
|
564
|
+
|
|
565
|
+
// if not removing last tab focus on next tab
|
|
566
|
+
if (tabRef.current && index + 1 !== tabCount) {
|
|
567
|
+
tabRef.current.focus();
|
|
568
|
+
}
|
|
569
|
+
// if removing last tab focus on previous tab
|
|
570
|
+
else {
|
|
571
|
+
const prevTabIndex = (tabCount - 2) * 2;
|
|
572
|
+
tabRef.current.parentElement.childNodes[prevTabIndex]?.focus();
|
|
573
|
+
}
|
|
574
|
+
}
|
|
555
575
|
};
|
|
556
576
|
const handleKeyDown = event => {
|
|
557
577
|
if (dismissable && match.match(event, keys.Delete)) {
|
|
@@ -560,20 +580,31 @@ const Tab = /*#__PURE__*/React.forwardRef(function Tab(_ref5, forwardRef) {
|
|
|
560
580
|
onKeyDown?.(event);
|
|
561
581
|
};
|
|
562
582
|
const DismissIcon = /*#__PURE__*/React__default["default"].createElement("div", {
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
583
|
+
className: cx__default["default"]({
|
|
584
|
+
[`${prefix}--tabs__nav-item--close`]: dismissable,
|
|
585
|
+
[`${prefix}--tabs__nav-item--close--hidden`]: !dismissable
|
|
586
|
+
})
|
|
587
|
+
}, /*#__PURE__*/React__default["default"].createElement("button", {
|
|
588
|
+
type: "button",
|
|
589
|
+
tabIndex: selectedIndex === index && dismissable ? 0 : -1,
|
|
590
|
+
"aria-disabled": disabled,
|
|
591
|
+
"aria-hidden": selectedIndex === index && dismissable ? 'false' : 'true',
|
|
592
|
+
disabled: disabled,
|
|
593
|
+
className: cx__default["default"]({
|
|
594
|
+
[`${prefix}--tabs__nav-item--close-icon`]: dismissable,
|
|
595
|
+
[`${prefix}--visually-hidden`]: !dismissable,
|
|
596
|
+
[`${prefix}--tabs__nav-item--close-icon--selected`]: selectedIndex === index,
|
|
597
|
+
[`${prefix}--tabs__nav-item--close-icon--disabled`]: disabled
|
|
567
598
|
}),
|
|
568
599
|
onClick: handleClose,
|
|
569
|
-
title:
|
|
600
|
+
title: `Remove ${typeof children === 'string' ? children : ''} tab`,
|
|
570
601
|
ref: dismissIconRef
|
|
571
602
|
}, /*#__PURE__*/React__default["default"].createElement(iconsReact.Close, {
|
|
572
|
-
"aria-hidden": dismissable ? 'false' : 'true',
|
|
573
|
-
"aria-label":
|
|
574
|
-
}));
|
|
603
|
+
"aria-hidden": selectedIndex === index && dismissable ? 'false' : 'true',
|
|
604
|
+
"aria-label": `Press delete to remove ${typeof children === 'string' ? children : ''} tab`
|
|
605
|
+
})));
|
|
575
606
|
const hasIcon = Icon ?? dismissable;
|
|
576
|
-
return /*#__PURE__*/React__default["default"].createElement(BaseComponent, _rollupPluginBabelHelpers["extends"]({}, rest, {
|
|
607
|
+
return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement(BaseComponent, _rollupPluginBabelHelpers["extends"]({}, rest, {
|
|
577
608
|
"aria-controls": panelId,
|
|
578
609
|
"aria-disabled": disabled,
|
|
579
610
|
"aria-selected": selectedIndex === index,
|
|
@@ -600,17 +631,17 @@ const Tab = /*#__PURE__*/React.forwardRef(function Tab(_ref5, forwardRef) {
|
|
|
600
631
|
size: 16
|
|
601
632
|
})), /*#__PURE__*/React__default["default"].createElement(Text.Text, {
|
|
602
633
|
className: `${prefix}--tabs__nav-item-label`
|
|
603
|
-
}, children), /*#__PURE__*/React__default["default"].createElement("div", {
|
|
634
|
+
}, children), !dismissable && Icon && /*#__PURE__*/React__default["default"].createElement("div", {
|
|
604
635
|
className: cx__default["default"](`${prefix}--tabs__nav-item--icon`, {
|
|
605
636
|
[`${prefix}--visually-hidden`]: !hasIcon
|
|
606
637
|
})
|
|
607
|
-
},
|
|
638
|
+
}, !dismissable && Icon && /*#__PURE__*/React__default["default"].createElement(Icon, {
|
|
608
639
|
size: 16
|
|
609
640
|
}))), hasSecondaryLabel && secondaryLabel && /*#__PURE__*/React__default["default"].createElement(Text.Text, {
|
|
610
641
|
as: "div",
|
|
611
642
|
className: `${prefix}--tabs__nav-item-secondary-label`,
|
|
612
643
|
title: secondaryLabel
|
|
613
|
-
}, secondaryLabel));
|
|
644
|
+
}, secondaryLabel)), DismissIcon);
|
|
614
645
|
});
|
|
615
646
|
Tab.propTypes = {
|
|
616
647
|
/**
|