@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.
Files changed (80) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/esm/components/DescriptionList/DescriptionList.d.ts +4 -0
  3. package/dist/esm/components/DescriptionList/DescriptionList.d.ts.map +1 -1
  4. package/dist/esm/components/DescriptionList/DescriptionList.js +5 -2
  5. package/dist/esm/components/DescriptionList/DescriptionList.js.map +1 -1
  6. package/dist/esm/components/DescriptionList/DescriptionListGroup.js.map +1 -1
  7. package/dist/esm/components/Menu/MenuItem.d.ts.map +1 -1
  8. package/dist/esm/components/Menu/MenuItem.js +5 -3
  9. package/dist/esm/components/Menu/MenuItem.js.map +1 -1
  10. package/dist/esm/components/Nav/NavItem.d.ts.map +1 -1
  11. package/dist/esm/components/Nav/NavItem.js +5 -1
  12. package/dist/esm/components/Nav/NavItem.js.map +1 -1
  13. package/dist/esm/components/Tabs/OverflowTab.d.ts +16 -0
  14. package/dist/esm/components/Tabs/OverflowTab.d.ts.map +1 -0
  15. package/dist/esm/components/Tabs/OverflowTab.js +74 -0
  16. package/dist/esm/components/Tabs/OverflowTab.js.map +1 -0
  17. package/dist/esm/components/Tabs/Tabs.d.ts +12 -0
  18. package/dist/esm/components/Tabs/Tabs.d.ts.map +1 -1
  19. package/dist/esm/components/Tabs/Tabs.js +36 -13
  20. package/dist/esm/components/Tabs/Tabs.js.map +1 -1
  21. package/dist/esm/components/TreeView/TreeView.d.ts +2 -0
  22. package/dist/esm/components/TreeView/TreeView.d.ts.map +1 -1
  23. package/dist/esm/components/TreeView/TreeView.js +3 -3
  24. package/dist/esm/components/TreeView/TreeView.js.map +1 -1
  25. package/dist/esm/components/TreeView/TreeViewListItem.d.ts +2 -0
  26. package/dist/esm/components/TreeView/TreeViewListItem.d.ts.map +1 -1
  27. package/dist/esm/components/TreeView/TreeViewListItem.js +23 -14
  28. package/dist/esm/components/TreeView/TreeViewListItem.js.map +1 -1
  29. package/dist/js/components/DescriptionList/DescriptionList.d.ts +4 -0
  30. package/dist/js/components/DescriptionList/DescriptionList.d.ts.map +1 -1
  31. package/dist/js/components/DescriptionList/DescriptionList.js +5 -2
  32. package/dist/js/components/DescriptionList/DescriptionList.js.map +1 -1
  33. package/dist/js/components/DescriptionList/DescriptionListGroup.js.map +1 -1
  34. package/dist/js/components/Menu/MenuItem.d.ts.map +1 -1
  35. package/dist/js/components/Menu/MenuItem.js +5 -3
  36. package/dist/js/components/Menu/MenuItem.js.map +1 -1
  37. package/dist/js/components/Nav/NavItem.d.ts.map +1 -1
  38. package/dist/js/components/Nav/NavItem.js +5 -1
  39. package/dist/js/components/Nav/NavItem.js.map +1 -1
  40. package/dist/js/components/Tabs/OverflowTab.d.ts +16 -0
  41. package/dist/js/components/Tabs/OverflowTab.d.ts.map +1 -0
  42. package/dist/js/components/Tabs/OverflowTab.js +78 -0
  43. package/dist/js/components/Tabs/OverflowTab.js.map +1 -0
  44. package/dist/js/components/Tabs/Tabs.d.ts +12 -0
  45. package/dist/js/components/Tabs/Tabs.d.ts.map +1 -1
  46. package/dist/js/components/Tabs/Tabs.js +36 -13
  47. package/dist/js/components/Tabs/Tabs.js.map +1 -1
  48. package/dist/js/components/TreeView/TreeView.d.ts +2 -0
  49. package/dist/js/components/TreeView/TreeView.d.ts.map +1 -1
  50. package/dist/js/components/TreeView/TreeView.js +3 -3
  51. package/dist/js/components/TreeView/TreeView.js.map +1 -1
  52. package/dist/js/components/TreeView/TreeViewListItem.d.ts +2 -0
  53. package/dist/js/components/TreeView/TreeViewListItem.d.ts.map +1 -1
  54. package/dist/js/components/TreeView/TreeViewListItem.js +23 -14
  55. package/dist/js/components/TreeView/TreeViewListItem.js.map +1 -1
  56. package/dist/styles/base-no-reset.css +9 -0
  57. package/dist/styles/base.css +9 -0
  58. package/dist/umd/react-core.css +37 -44
  59. package/dist/umd/react-core.js +147 -42
  60. package/dist/umd/react-core.min.css +6 -13
  61. package/dist/umd/react-core.min.js +4 -4
  62. package/package.json +6 -6
  63. package/src/components/DescriptionList/DescriptionList.tsx +14 -0
  64. package/src/components/DescriptionList/DescriptionListGroup.tsx +1 -0
  65. package/src/components/DescriptionList/examples/DescriptionList.md +31 -1
  66. package/src/components/DescriptionList/examples/DescriptionListDisplaySizeAndCard.tsx +63 -0
  67. package/src/components/DescriptionList/examples/DescriptionListDisplaySizeAndCardHorizontalTermWidth.tsx +34 -0
  68. package/src/components/DescriptionList/examples/DescriptionListDisplaySizeAndCardThreeColumn.tsx +34 -0
  69. package/src/components/DescriptionList/examples/DescriptionListWithCard.tsx +62 -0
  70. package/src/components/DescriptionList/examples/DescriptionListWithDisplaySize.tsx +63 -0
  71. package/src/components/Menu/MenuItem.tsx +7 -5
  72. package/src/components/Nav/NavItem.tsx +6 -0
  73. package/src/components/Tabs/OverflowTab.tsx +141 -0
  74. package/src/components/Tabs/Tabs.tsx +72 -25
  75. package/src/components/Tabs/examples/Tabs.md +6 -1
  76. package/src/components/Tabs/examples/TabsHorizontalOverflow.tsx +67 -0
  77. package/src/components/TreeView/TreeView.tsx +5 -0
  78. package/src/components/TreeView/TreeViewListItem.tsx +31 -17
  79. package/src/components/TreeView/__tests__/__snapshots__/TreeView.test.tsx.snap +299 -299
  80. 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
- <button
423
- className={css(styles.tabsScrollButton, isSecondary && buttonStyles.modifiers.secondary)}
424
- aria-label={leftScrollAriaLabel}
425
- onClick={this.scrollLeft}
426
- disabled={disableLeftScrollButton}
427
- aria-hidden={disableLeftScrollButton}
428
- >
429
- <AngleLeftIcon />
430
- </button>
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
- <button
435
- className={css(styles.tabsScrollButton, isSecondary && buttonStyles.modifiers.secondary)}
436
- aria-label={rightScrollAriaLabel}
437
- onClick={this.scrollRight}
438
- disabled={disableRightScrollButton}
439
- aria-hidden={disableRightScrollButton}
440
- >
441
- <AngleRightIcon />
442
- </button>
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
- const Component = hasCheck ? 'div' : 'button';
97
- const ToggleComponent = hasCheck ? 'button' : 'div';
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 = (randomId: string) => {
147
+ const renderNodeContent = () => {
135
148
  const content = (
136
149
  <>
137
150
  {isCompact && title && <span className={css(styles.treeViewNodeTitle)}>{title}</span>}
138
151
  {hasCheck ? (
139
- <label className={css(styles.treeViewNodeText)} htmlFor={randomId} id={`label-${randomId}`}>
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 ? <div className={css(styles.treeViewNodeContent)}>{content}</div> : content;
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="checkbox-id">
185
+ <GenerateId prefix={isSelectable ? 'selectable-id' : 'checkbox-id'}>
175
186
  {randomId => (
176
187
  <Component
177
188
  className={css(
178
189
  styles.treeViewNode,
179
- !children &&
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
- <div className={css(styles.treeViewNodeContainer)}>
210
+ <span className={css(styles.treeViewNodeContainer)}>
197
211
  {children && renderToggle(randomId)}
198
212
  {hasCheck && renderCheck(randomId)}
199
213
  {icon && iconRendered}
200
- {renderNodeContent(randomId)}
214
+ {renderNodeContent()}
201
215
  {badgeRendered}
202
- </div>
216
+ </span>
203
217
  </Component>
204
218
  )}
205
219
  </GenerateId>