@patternfly/react-core 4.227.9 → 4.229.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 +33 -0
- package/dist/esm/components/DescriptionList/DescriptionList.d.ts +4 -0
- package/dist/esm/components/DescriptionList/DescriptionList.d.ts.map +1 -1
- package/dist/esm/components/DescriptionList/DescriptionList.js +5 -2
- package/dist/esm/components/DescriptionList/DescriptionList.js.map +1 -1
- package/dist/esm/components/DescriptionList/DescriptionListGroup.js.map +1 -1
- package/dist/esm/components/Menu/MenuItem.d.ts.map +1 -1
- package/dist/esm/components/Menu/MenuItem.js +5 -3
- package/dist/esm/components/Menu/MenuItem.js.map +1 -1
- package/dist/esm/components/Nav/NavItem.d.ts.map +1 -1
- package/dist/esm/components/Nav/NavItem.js +5 -1
- package/dist/esm/components/Nav/NavItem.js.map +1 -1
- package/dist/esm/components/Tabs/OverflowTab.d.ts +16 -0
- package/dist/esm/components/Tabs/OverflowTab.d.ts.map +1 -0
- package/dist/esm/components/Tabs/OverflowTab.js +74 -0
- package/dist/esm/components/Tabs/OverflowTab.js.map +1 -0
- package/dist/esm/components/Tabs/Tabs.d.ts +12 -0
- package/dist/esm/components/Tabs/Tabs.d.ts.map +1 -1
- package/dist/esm/components/Tabs/Tabs.js +36 -13
- package/dist/esm/components/Tabs/Tabs.js.map +1 -1
- package/dist/esm/components/TreeView/TreeView.d.ts +2 -0
- package/dist/esm/components/TreeView/TreeView.d.ts.map +1 -1
- package/dist/esm/components/TreeView/TreeView.js +3 -3
- package/dist/esm/components/TreeView/TreeView.js.map +1 -1
- package/dist/esm/components/TreeView/TreeViewListItem.d.ts +2 -0
- package/dist/esm/components/TreeView/TreeViewListItem.d.ts.map +1 -1
- package/dist/esm/components/TreeView/TreeViewListItem.js +23 -14
- package/dist/esm/components/TreeView/TreeViewListItem.js.map +1 -1
- package/dist/js/components/DescriptionList/DescriptionList.d.ts +4 -0
- package/dist/js/components/DescriptionList/DescriptionList.d.ts.map +1 -1
- package/dist/js/components/DescriptionList/DescriptionList.js +5 -2
- package/dist/js/components/DescriptionList/DescriptionList.js.map +1 -1
- package/dist/js/components/DescriptionList/DescriptionListGroup.js.map +1 -1
- package/dist/js/components/Menu/MenuItem.d.ts.map +1 -1
- package/dist/js/components/Menu/MenuItem.js +5 -3
- package/dist/js/components/Menu/MenuItem.js.map +1 -1
- package/dist/js/components/Nav/NavItem.d.ts.map +1 -1
- package/dist/js/components/Nav/NavItem.js +5 -1
- package/dist/js/components/Nav/NavItem.js.map +1 -1
- package/dist/js/components/Tabs/OverflowTab.d.ts +16 -0
- package/dist/js/components/Tabs/OverflowTab.d.ts.map +1 -0
- package/dist/js/components/Tabs/OverflowTab.js +78 -0
- package/dist/js/components/Tabs/OverflowTab.js.map +1 -0
- package/dist/js/components/Tabs/Tabs.d.ts +12 -0
- package/dist/js/components/Tabs/Tabs.d.ts.map +1 -1
- package/dist/js/components/Tabs/Tabs.js +36 -13
- package/dist/js/components/Tabs/Tabs.js.map +1 -1
- package/dist/js/components/TreeView/TreeView.d.ts +2 -0
- package/dist/js/components/TreeView/TreeView.d.ts.map +1 -1
- package/dist/js/components/TreeView/TreeView.js +3 -3
- package/dist/js/components/TreeView/TreeView.js.map +1 -1
- package/dist/js/components/TreeView/TreeViewListItem.d.ts +2 -0
- package/dist/js/components/TreeView/TreeViewListItem.d.ts.map +1 -1
- package/dist/js/components/TreeView/TreeViewListItem.js +23 -14
- package/dist/js/components/TreeView/TreeViewListItem.js.map +1 -1
- package/dist/styles/base-no-reset.css +9 -0
- package/dist/styles/base.css +9 -0
- package/dist/umd/react-core.css +37 -44
- package/dist/umd/react-core.js +147 -42
- package/dist/umd/react-core.min.css +6 -13
- package/dist/umd/react-core.min.js +4 -4
- package/package.json +6 -6
- package/src/components/DescriptionList/DescriptionList.tsx +14 -0
- package/src/components/DescriptionList/DescriptionListGroup.tsx +1 -0
- package/src/components/DescriptionList/examples/DescriptionList.md +31 -1
- package/src/components/DescriptionList/examples/DescriptionListDisplaySizeAndCard.tsx +63 -0
- package/src/components/DescriptionList/examples/DescriptionListDisplaySizeAndCardHorizontalTermWidth.tsx +34 -0
- package/src/components/DescriptionList/examples/DescriptionListDisplaySizeAndCardThreeColumn.tsx +34 -0
- package/src/components/DescriptionList/examples/DescriptionListWithCard.tsx +62 -0
- package/src/components/DescriptionList/examples/DescriptionListWithDisplaySize.tsx +63 -0
- package/src/components/Menu/MenuItem.tsx +7 -5
- package/src/components/Nav/NavItem.tsx +6 -0
- package/src/components/Tabs/OverflowTab.tsx +141 -0
- package/src/components/Tabs/Tabs.tsx +72 -25
- package/src/components/Tabs/examples/Tabs.md +6 -1
- package/src/components/Tabs/examples/TabsHorizontalOverflow.tsx +67 -0
- package/src/components/TreeView/TreeView.tsx +5 -0
- package/src/components/TreeView/TreeViewListItem.tsx +31 -17
- package/src/components/TreeView/__tests__/__snapshots__/TreeView.test.tsx.snap +299 -299
- package/src/components/TreeView/examples/TreeView.md +125 -7
|
@@ -10,6 +10,7 @@ import { getUniqueId, isElementInView, formatBreakpointMods } from '../../helper
|
|
|
10
10
|
import { TabContent } from './TabContent';
|
|
11
11
|
import { TabProps } from './Tab';
|
|
12
12
|
import { TabsContextProvider } from './TabsContext';
|
|
13
|
+
import { OverflowTab } from './OverflowTab';
|
|
13
14
|
import { Button } from '../Button';
|
|
14
15
|
import { getOUIAProps, OUIAProps, getDefaultOUIAId, canUseDOM } from '../../helpers';
|
|
15
16
|
import { GenerateId } from '../../helpers/GenerateId/GenerateId';
|
|
@@ -19,6 +20,15 @@ export enum TabsComponent {
|
|
|
19
20
|
nav = 'nav'
|
|
20
21
|
}
|
|
21
22
|
|
|
23
|
+
export interface HorizontalOverflowObject {
|
|
24
|
+
/** Flag which shows the count of overflowing tabs when enabled */
|
|
25
|
+
showTabCount?: boolean;
|
|
26
|
+
/** The text which displays when an overflowing tab isn't selected */
|
|
27
|
+
defaultTitleText?: string;
|
|
28
|
+
/** The aria label applied to the button which toggles the tab overflow menu */
|
|
29
|
+
toggleAriaLabel?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
22
32
|
export interface TabsProps extends Omit<React.HTMLProps<HTMLElement | HTMLDivElement>, 'onSelect'>, OUIAProps {
|
|
23
33
|
/** Content rendered inside the tabs component. Must be React.ReactElement<TabProps>[] */
|
|
24
34
|
children: React.ReactNode;
|
|
@@ -94,6 +104,8 @@ export interface TabsProps extends Omit<React.HTMLProps<HTMLElement | HTMLDivEle
|
|
|
94
104
|
toggleAriaLabel?: string;
|
|
95
105
|
/** Callback function to toggle the expandable tabs. */
|
|
96
106
|
onToggle?: (isExpanded: boolean) => void;
|
|
107
|
+
/** @beta Flag which places overflowing tabs into a menu triggered by the last tab. Additionally an object can be passed with custom settings for the overflow tab. */
|
|
108
|
+
isOverflowHorizontal?: boolean | HorizontalOverflowObject;
|
|
97
109
|
}
|
|
98
110
|
|
|
99
111
|
const variantStyle = {
|
|
@@ -109,6 +121,7 @@ interface TabsState {
|
|
|
109
121
|
uncontrolledActiveKey: number | string;
|
|
110
122
|
uncontrolledIsExpandedLocal: boolean;
|
|
111
123
|
ouiaStateId: string;
|
|
124
|
+
overflowingTabCount: number;
|
|
112
125
|
}
|
|
113
126
|
|
|
114
127
|
export class Tabs extends React.Component<TabsProps, TabsState> {
|
|
@@ -123,7 +136,8 @@ export class Tabs extends React.Component<TabsProps, TabsState> {
|
|
|
123
136
|
shownKeys: this.props.defaultActiveKey !== undefined ? [this.props.defaultActiveKey] : [this.props.activeKey], // only for mountOnEnter case
|
|
124
137
|
uncontrolledActiveKey: this.props.defaultActiveKey,
|
|
125
138
|
uncontrolledIsExpandedLocal: this.props.defaultIsExpanded,
|
|
126
|
-
ouiaStateId: getDefaultOUIAId(Tabs.displayName)
|
|
139
|
+
ouiaStateId: getDefaultOUIAId(Tabs.displayName),
|
|
140
|
+
overflowingTabCount: 0
|
|
127
141
|
};
|
|
128
142
|
|
|
129
143
|
if (this.props.isVertical && this.props.expandable !== undefined) {
|
|
@@ -192,7 +206,13 @@ export class Tabs extends React.Component<TabsProps, TabsState> {
|
|
|
192
206
|
}
|
|
193
207
|
}
|
|
194
208
|
|
|
209
|
+
countOverflowingElements = (container: HTMLUListElement) => {
|
|
210
|
+
const elements = Array.from(container.children);
|
|
211
|
+
return elements.filter(element => !isElementInView(container, element as HTMLElement, false)).length;
|
|
212
|
+
};
|
|
213
|
+
|
|
195
214
|
handleScrollButtons = () => {
|
|
215
|
+
const { isOverflowHorizontal: isOverflowHorizontal } = this.props;
|
|
196
216
|
// add debounce to the scroll event
|
|
197
217
|
clearTimeout(this.scrollTimeout);
|
|
198
218
|
this.scrollTimeout = setTimeout(() => {
|
|
@@ -200,8 +220,9 @@ export class Tabs extends React.Component<TabsProps, TabsState> {
|
|
|
200
220
|
let disableLeftScrollButton = true;
|
|
201
221
|
let disableRightScrollButton = true;
|
|
202
222
|
let showScrollButtons = false;
|
|
223
|
+
let overflowingTabCount = 0;
|
|
203
224
|
|
|
204
|
-
if (container && !this.props.isVertical) {
|
|
225
|
+
if (container && !this.props.isVertical && !isOverflowHorizontal) {
|
|
205
226
|
// get first element and check if it is in view
|
|
206
227
|
const overflowOnLeft = !isElementInView(container, container.firstChild as HTMLElement, false);
|
|
207
228
|
|
|
@@ -213,10 +234,16 @@ export class Tabs extends React.Component<TabsProps, TabsState> {
|
|
|
213
234
|
disableLeftScrollButton = !overflowOnLeft;
|
|
214
235
|
disableRightScrollButton = !overflowOnRight;
|
|
215
236
|
}
|
|
237
|
+
|
|
238
|
+
if (isOverflowHorizontal) {
|
|
239
|
+
overflowingTabCount = this.countOverflowingElements(container);
|
|
240
|
+
}
|
|
241
|
+
|
|
216
242
|
this.setState({
|
|
217
243
|
showScrollButtons,
|
|
218
244
|
disableLeftScrollButton,
|
|
219
|
-
disableRightScrollButton
|
|
245
|
+
disableRightScrollButton,
|
|
246
|
+
overflowingTabCount
|
|
220
247
|
});
|
|
221
248
|
}, 100);
|
|
222
249
|
};
|
|
@@ -280,8 +307,8 @@ export class Tabs extends React.Component<TabsProps, TabsState> {
|
|
|
280
307
|
}
|
|
281
308
|
|
|
282
309
|
componentDidUpdate(prevProps: TabsProps) {
|
|
283
|
-
const { activeKey, mountOnEnter, children } = this.props;
|
|
284
|
-
const { shownKeys } = this.state;
|
|
310
|
+
const { activeKey, mountOnEnter, isOverflowHorizontal, children } = this.props;
|
|
311
|
+
const { shownKeys, overflowingTabCount } = this.state;
|
|
285
312
|
if (prevProps.activeKey !== activeKey && mountOnEnter && shownKeys.indexOf(activeKey) < 0) {
|
|
286
313
|
this.setState({
|
|
287
314
|
shownKeys: shownKeys.concat(activeKey)
|
|
@@ -295,6 +322,11 @@ export class Tabs extends React.Component<TabsProps, TabsState> {
|
|
|
295
322
|
) {
|
|
296
323
|
this.handleScrollButtons();
|
|
297
324
|
}
|
|
325
|
+
|
|
326
|
+
const currentOverflowingTabCount = this.countOverflowingElements(this.tabList.current);
|
|
327
|
+
if (isOverflowHorizontal && currentOverflowingTabCount) {
|
|
328
|
+
this.setState({ overflowingTabCount: currentOverflowingTabCount + overflowingTabCount });
|
|
329
|
+
}
|
|
298
330
|
}
|
|
299
331
|
|
|
300
332
|
render() {
|
|
@@ -330,6 +362,7 @@ export class Tabs extends React.Component<TabsProps, TabsState> {
|
|
|
330
362
|
onToggle,
|
|
331
363
|
onClose,
|
|
332
364
|
onAdd,
|
|
365
|
+
isOverflowHorizontal: isOverflowHorizontal,
|
|
333
366
|
...props
|
|
334
367
|
} = this.props;
|
|
335
368
|
const {
|
|
@@ -338,12 +371,17 @@ export class Tabs extends React.Component<TabsProps, TabsState> {
|
|
|
338
371
|
disableRightScrollButton,
|
|
339
372
|
shownKeys,
|
|
340
373
|
uncontrolledActiveKey,
|
|
341
|
-
uncontrolledIsExpandedLocal
|
|
374
|
+
uncontrolledIsExpandedLocal,
|
|
375
|
+
overflowingTabCount
|
|
342
376
|
} = this.state;
|
|
343
377
|
const filteredChildren = (React.Children.toArray(children) as React.ReactElement<TabProps>[])
|
|
344
378
|
.filter(Boolean)
|
|
345
379
|
.filter(child => !child.props.isHidden);
|
|
346
380
|
|
|
381
|
+
const filteredChildrenWithoutOverflow = filteredChildren.slice(0, filteredChildren.length - overflowingTabCount);
|
|
382
|
+
const filteredChildrenOverflowing = filteredChildren.slice(filteredChildren.length - overflowingTabCount);
|
|
383
|
+
const overflowingTabProps = filteredChildrenOverflowing.map((child: React.ReactElement<TabProps>) => child.props);
|
|
384
|
+
|
|
347
385
|
const uniqueId = id || getUniqueId();
|
|
348
386
|
const Component: any = component === TabsComponent.nav ? 'nav' : 'div';
|
|
349
387
|
const localActiveKey = defaultActiveKey !== undefined ? uncontrolledActiveKey : activeKey;
|
|
@@ -358,6 +396,9 @@ export class Tabs extends React.Component<TabsProps, TabsState> {
|
|
|
358
396
|
}
|
|
359
397
|
};
|
|
360
398
|
|
|
399
|
+
const hasOverflowTab = isOverflowHorizontal && overflowingTabCount > 0;
|
|
400
|
+
const overflowObjectProps = typeof isOverflowHorizontal === 'object' ? { ...isOverflowHorizontal } : {};
|
|
401
|
+
|
|
361
402
|
return (
|
|
362
403
|
<TabsContextProvider
|
|
363
404
|
value={{
|
|
@@ -386,6 +427,7 @@ export class Tabs extends React.Component<TabsProps, TabsState> {
|
|
|
386
427
|
hasSecondaryBorderBottom && styles.modifiers.borderBottom,
|
|
387
428
|
formatBreakpointMods(inset, styles),
|
|
388
429
|
variantStyle[variant],
|
|
430
|
+
hasOverflowTab && styles.modifiers.overflow,
|
|
389
431
|
className
|
|
390
432
|
)}
|
|
391
433
|
{...getOUIAProps(Tabs.displayName, ouiaId !== undefined ? ouiaId : this.state.ouiaStateId, ouiaSafe)}
|
|
@@ -419,27 +461,32 @@ export class Tabs extends React.Component<TabsProps, TabsState> {
|
|
|
419
461
|
)}
|
|
420
462
|
</GenerateId>
|
|
421
463
|
)}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
464
|
+
{!isOverflowHorizontal && (
|
|
465
|
+
<button
|
|
466
|
+
className={css(styles.tabsScrollButton, isSecondary && buttonStyles.modifiers.secondary)}
|
|
467
|
+
aria-label={leftScrollAriaLabel}
|
|
468
|
+
onClick={this.scrollLeft}
|
|
469
|
+
disabled={disableLeftScrollButton}
|
|
470
|
+
aria-hidden={disableLeftScrollButton}
|
|
471
|
+
>
|
|
472
|
+
<AngleLeftIcon />
|
|
473
|
+
</button>
|
|
474
|
+
)}
|
|
431
475
|
<ul className={css(styles.tabsList)} ref={this.tabList} onScroll={this.handleScrollButtons} role="tablist">
|
|
432
|
-
{filteredChildren}
|
|
476
|
+
{isOverflowHorizontal ? filteredChildrenWithoutOverflow : filteredChildren}
|
|
477
|
+
{hasOverflowTab && <OverflowTab overflowingTabs={overflowingTabProps} {...overflowObjectProps} />}
|
|
433
478
|
</ul>
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
479
|
+
{!isOverflowHorizontal && (
|
|
480
|
+
<button
|
|
481
|
+
className={css(styles.tabsScrollButton, isSecondary && buttonStyles.modifiers.secondary)}
|
|
482
|
+
aria-label={rightScrollAriaLabel}
|
|
483
|
+
onClick={this.scrollRight}
|
|
484
|
+
disabled={disableRightScrollButton}
|
|
485
|
+
aria-hidden={disableRightScrollButton}
|
|
486
|
+
>
|
|
487
|
+
<AngleRightIcon />
|
|
488
|
+
</button>
|
|
489
|
+
)}
|
|
443
490
|
{onAdd !== undefined && (
|
|
444
491
|
<span className={css(styles.tabsAdd)}>
|
|
445
492
|
<Button variant="plain" aria-label={addButtonAriaLabel || 'Add tab'} onClick={onAdd}>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
id: Tabs
|
|
3
3
|
section: components
|
|
4
4
|
cssPrefix: pf-c-tabs
|
|
5
|
-
propComponents: ['Tabs', 'Tab', 'TabContent', 'TabTitleText', 'TabTitleIcon']
|
|
5
|
+
propComponents: ['Tabs', 'Tab', 'TabContent', 'TabTitleText', 'TabTitleIcon', 'horizontalOverflowObject']
|
|
6
6
|
ouia: true
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -410,6 +410,11 @@ class ScrollButtonsPrimaryTabs extends React.Component {
|
|
|
410
410
|
}
|
|
411
411
|
```
|
|
412
412
|
|
|
413
|
+
### Horizontal overflow
|
|
414
|
+
|
|
415
|
+
```ts file="./TabsHorizontalOverflow.tsx" isBeta
|
|
416
|
+
```
|
|
417
|
+
|
|
413
418
|
### Vertical
|
|
414
419
|
|
|
415
420
|
```js
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Tabs, Tab, TabTitleText, Checkbox } from '@patternfly/react-core';
|
|
3
|
+
|
|
4
|
+
export const TabsHorizontalOverflow: React.FunctionComponent = () => {
|
|
5
|
+
const [activeTabKey, setActiveTabKey] = React.useState<string | number>(0);
|
|
6
|
+
const [showTabCount, setShowTabCount] = React.useState(false);
|
|
7
|
+
|
|
8
|
+
const handleTabClick = (_event: any, tabIndex: string | number) => {
|
|
9
|
+
setActiveTabKey(tabIndex);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<div>
|
|
14
|
+
<Tabs
|
|
15
|
+
activeKey={activeTabKey}
|
|
16
|
+
onSelect={handleTabClick}
|
|
17
|
+
aria-label="Tabs in the horizontal overflow example"
|
|
18
|
+
role="region"
|
|
19
|
+
isOverflowHorizontal={{ showTabCount }}
|
|
20
|
+
>
|
|
21
|
+
<Tab eventKey={0} title={<TabTitleText>Users</TabTitleText>} aria-label="Horizontal overflow content users">
|
|
22
|
+
Users
|
|
23
|
+
</Tab>
|
|
24
|
+
<Tab eventKey={1} title={<TabTitleText>Containers</TabTitleText>}>
|
|
25
|
+
Containers
|
|
26
|
+
</Tab>
|
|
27
|
+
<Tab eventKey={2} title={<TabTitleText>Database</TabTitleText>}>
|
|
28
|
+
Database
|
|
29
|
+
</Tab>
|
|
30
|
+
<Tab eventKey={3} title={<TabTitleText>Server</TabTitleText>}>
|
|
31
|
+
Server
|
|
32
|
+
</Tab>
|
|
33
|
+
<Tab eventKey={4} title={<TabTitleText>System</TabTitleText>}>
|
|
34
|
+
System
|
|
35
|
+
</Tab>
|
|
36
|
+
<Tab eventKey={6} title={<TabTitleText>Network</TabTitleText>}>
|
|
37
|
+
Network
|
|
38
|
+
</Tab>
|
|
39
|
+
<Tab eventKey={7} title={<TabTitleText>Tab item 7</TabTitleText>}>
|
|
40
|
+
Tab 7 section
|
|
41
|
+
</Tab>
|
|
42
|
+
<Tab eventKey={8} title={<TabTitleText>Tab item 8</TabTitleText>}>
|
|
43
|
+
Tab 8 section
|
|
44
|
+
</Tab>
|
|
45
|
+
<Tab eventKey={9} title={<TabTitleText>Tab item 9</TabTitleText>}>
|
|
46
|
+
Tab 9 section
|
|
47
|
+
</Tab>
|
|
48
|
+
<Tab eventKey={10} title={<TabTitleText>Tab item 10</TabTitleText>}>
|
|
49
|
+
Tab 10 section
|
|
50
|
+
</Tab>
|
|
51
|
+
<Tab eventKey={11} title={<TabTitleText>Tab item 11</TabTitleText>}>
|
|
52
|
+
Tab 11 section
|
|
53
|
+
</Tab>
|
|
54
|
+
</Tabs>
|
|
55
|
+
<div style={{ marginTop: '20px' }}>
|
|
56
|
+
<Checkbox
|
|
57
|
+
label="Show overflowing tab count"
|
|
58
|
+
isChecked={showTabCount}
|
|
59
|
+
onChange={() => setShowTabCount(!showTabCount)}
|
|
60
|
+
aria-label="show overflow tab count variation checkbox"
|
|
61
|
+
id="toggle-show-count-overflow"
|
|
62
|
+
name="toggle-show-count-overflow"
|
|
63
|
+
/>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
@@ -45,6 +45,8 @@ export interface TreeViewProps {
|
|
|
45
45
|
hasBadges?: boolean;
|
|
46
46
|
/** Flag indicating if tree view has guide lines. */
|
|
47
47
|
hasGuides?: boolean;
|
|
48
|
+
/** Flag indicating that tree nodes should be independently selectable, even when having children */
|
|
49
|
+
hasSelectableNodes?: boolean;
|
|
48
50
|
/** Variant presentation styles for the tree view. */
|
|
49
51
|
variant?: 'default' | 'compact' | 'compactNoBackground';
|
|
50
52
|
/** Icon for all leaf or unexpanded node items */
|
|
@@ -79,6 +81,7 @@ export const TreeView: React.FunctionComponent<TreeViewProps> = ({
|
|
|
79
81
|
hasChecks = false,
|
|
80
82
|
hasBadges = false,
|
|
81
83
|
hasGuides = false,
|
|
84
|
+
hasSelectableNodes = false,
|
|
82
85
|
variant = 'default',
|
|
83
86
|
defaultAllExpanded = false,
|
|
84
87
|
allExpanded,
|
|
@@ -103,6 +106,7 @@ export const TreeView: React.FunctionComponent<TreeViewProps> = ({
|
|
|
103
106
|
title={item.title}
|
|
104
107
|
id={item.id}
|
|
105
108
|
isExpanded={allExpanded}
|
|
109
|
+
isSelectable={hasSelectableNodes}
|
|
106
110
|
defaultExpanded={item.defaultExpanded !== undefined ? item.defaultExpanded : defaultAllExpanded}
|
|
107
111
|
onSelect={onSelect}
|
|
108
112
|
onCheck={onCheck}
|
|
@@ -129,6 +133,7 @@ export const TreeView: React.FunctionComponent<TreeViewProps> = ({
|
|
|
129
133
|
hasChecks={hasChecks}
|
|
130
134
|
hasBadges={hasBadges}
|
|
131
135
|
hasGuides={hasGuides}
|
|
136
|
+
hasSelectableNodes={hasSelectableNodes}
|
|
132
137
|
variant={variant}
|
|
133
138
|
allExpanded={allExpanded}
|
|
134
139
|
defaultAllExpanded={defaultAllExpanded}
|
|
@@ -33,6 +33,8 @@ export interface TreeViewListItemProps {
|
|
|
33
33
|
checkProps?: TreeViewCheckProps;
|
|
34
34
|
/** Flag indicating if a tree view item has a badge */
|
|
35
35
|
hasBadge?: boolean;
|
|
36
|
+
/** Flag indicating that tree nodes should be independently selectable, even when having children */
|
|
37
|
+
isSelectable?: boolean;
|
|
36
38
|
/** Optional prop for custom badge */
|
|
37
39
|
customBadgeContent?: React.ReactNode;
|
|
38
40
|
/** Additional properties of the tree view item badge */
|
|
@@ -73,6 +75,7 @@ const TreeViewListItemBase: React.FunctionComponent<TreeViewListItemProps> = ({
|
|
|
73
75
|
hasBadge = false,
|
|
74
76
|
customBadgeContent,
|
|
75
77
|
badgeProps = { isRead: true },
|
|
78
|
+
isSelectable = false,
|
|
76
79
|
isCompact,
|
|
77
80
|
activeItems = [],
|
|
78
81
|
itemData,
|
|
@@ -93,17 +96,27 @@ const TreeViewListItemBase: React.FunctionComponent<TreeViewListItemProps> = ({
|
|
|
93
96
|
}
|
|
94
97
|
}, [isExpanded, defaultExpanded]);
|
|
95
98
|
|
|
96
|
-
|
|
97
|
-
|
|
99
|
+
let Component: 'label' | 'div' | 'button' = 'button';
|
|
100
|
+
if (hasCheck) {
|
|
101
|
+
Component = 'label';
|
|
102
|
+
} else if (isSelectable) {
|
|
103
|
+
Component = 'div';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const ToggleComponent = hasCheck || isSelectable ? 'button' : 'span';
|
|
107
|
+
|
|
98
108
|
const renderToggle = (randomId: string) => (
|
|
99
109
|
<ToggleComponent
|
|
100
110
|
className={css(styles.treeViewNodeToggle)}
|
|
101
|
-
onClick={() => {
|
|
102
|
-
if (hasCheck) {
|
|
111
|
+
onClick={(evt: React.MouseEvent) => {
|
|
112
|
+
if (isSelectable || hasCheck) {
|
|
103
113
|
setIsExpanded(!internalIsExpanded);
|
|
104
114
|
}
|
|
115
|
+
if (isSelectable) {
|
|
116
|
+
evt.stopPropagation();
|
|
117
|
+
}
|
|
105
118
|
}}
|
|
106
|
-
{...(hasCheck && { 'aria-labelledby': `label-${randomId}` })}
|
|
119
|
+
{...((hasCheck || isSelectable) && { 'aria-labelledby': `label-${randomId}` })}
|
|
107
120
|
tabIndex={-1}
|
|
108
121
|
>
|
|
109
122
|
<span className={css(styles.treeViewNodeToggleIcon)}>
|
|
@@ -131,20 +144,18 @@ const TreeViewListItemBase: React.FunctionComponent<TreeViewListItemProps> = ({
|
|
|
131
144
|
{internalIsExpanded && (expandedIcon || icon)}
|
|
132
145
|
</span>
|
|
133
146
|
);
|
|
134
|
-
const renderNodeContent = (
|
|
147
|
+
const renderNodeContent = () => {
|
|
135
148
|
const content = (
|
|
136
149
|
<>
|
|
137
150
|
{isCompact && title && <span className={css(styles.treeViewNodeTitle)}>{title}</span>}
|
|
138
151
|
{hasCheck ? (
|
|
139
|
-
<
|
|
140
|
-
{name}
|
|
141
|
-
</label>
|
|
152
|
+
<span className={css(styles.treeViewNodeText)}>{name}</span>
|
|
142
153
|
) : (
|
|
143
154
|
<span className={css(styles.treeViewNodeText)}>{name}</span>
|
|
144
155
|
)}
|
|
145
156
|
</>
|
|
146
157
|
);
|
|
147
|
-
return isCompact ? <
|
|
158
|
+
return isCompact ? <span className={css(styles.treeViewNodeContent)}>{content}</span> : content;
|
|
148
159
|
};
|
|
149
160
|
const badgeRendered = (
|
|
150
161
|
<>
|
|
@@ -171,12 +182,13 @@ const TreeViewListItemBase: React.FunctionComponent<TreeViewListItemProps> = ({
|
|
|
171
182
|
tabIndex={-1}
|
|
172
183
|
>
|
|
173
184
|
<div className={css(styles.treeViewContent)}>
|
|
174
|
-
<GenerateId prefix=
|
|
185
|
+
<GenerateId prefix={isSelectable ? 'selectable-id' : 'checkbox-id'}>
|
|
175
186
|
{randomId => (
|
|
176
187
|
<Component
|
|
177
188
|
className={css(
|
|
178
189
|
styles.treeViewNode,
|
|
179
|
-
|
|
190
|
+
children && (isSelectable || hasCheck) && styles.modifiers.selectable,
|
|
191
|
+
(!children || isSelectable) &&
|
|
180
192
|
activeItems &&
|
|
181
193
|
activeItems.length > 0 &&
|
|
182
194
|
activeItems.some(item => compareItems && item && compareItems(item, itemData))
|
|
@@ -186,20 +198,22 @@ const TreeViewListItemBase: React.FunctionComponent<TreeViewListItemProps> = ({
|
|
|
186
198
|
onClick={(evt: React.MouseEvent) => {
|
|
187
199
|
if (!hasCheck) {
|
|
188
200
|
onSelect && onSelect(evt, itemData, parentItem);
|
|
189
|
-
if (children && evt.isDefaultPrevented() !== true) {
|
|
201
|
+
if (!isSelectable && children && evt.isDefaultPrevented() !== true) {
|
|
190
202
|
setIsExpanded(!internalIsExpanded);
|
|
191
203
|
}
|
|
192
204
|
}
|
|
193
205
|
}}
|
|
194
|
-
tabIndex={-1}
|
|
206
|
+
tabIndex={isSelectable ? 0 : -1}
|
|
207
|
+
{...(hasCheck && { htmlFor: randomId })}
|
|
208
|
+
{...((hasCheck || (isSelectable && children)) && { id: `label-${randomId}` })}
|
|
195
209
|
>
|
|
196
|
-
<
|
|
210
|
+
<span className={css(styles.treeViewNodeContainer)}>
|
|
197
211
|
{children && renderToggle(randomId)}
|
|
198
212
|
{hasCheck && renderCheck(randomId)}
|
|
199
213
|
{icon && iconRendered}
|
|
200
|
-
{renderNodeContent(
|
|
214
|
+
{renderNodeContent()}
|
|
201
215
|
{badgeRendered}
|
|
202
|
-
</
|
|
216
|
+
</span>
|
|
203
217
|
</Component>
|
|
204
218
|
)}
|
|
205
219
|
</GenerateId>
|