@jetbrains/ring-ui 7.0.7 → 7.0.9

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/README.md CHANGED
@@ -8,6 +8,7 @@ This collection of UI components aims to provide all the necessary building bloc
8
8
  ## Try now
9
9
  * Try the [codesandbox](https://codesandbox.io/p/sandbox/ring-ui-7-0-demo-z6v6ym), based on `create-react-app` tooling, to see and try the UI components
10
10
  * Check out [list of examples](https://jetbrains.github.io/ring-ui/master/index.html) for each component
11
+ * Check out [Ring UI Design Guidelines](http://www.jetbrains.com/help/ring-ui)
11
12
 
12
13
  ## Installation
13
14
 
@@ -1,5 +1,4 @@
1
- import { PropsWithChildren } from 'react';
2
- import * as React from 'react';
1
+ import React, { PropsWithChildren } from 'react';
3
2
  type Props = {
4
3
  minHeight?: number;
5
4
  className?: string;
@@ -1,5 +1,4 @@
1
- import { useState, useEffect, useRef, useContext, useMemo } from 'react';
2
- import * as React from 'react';
1
+ import React, { useState, useEffect, useRef, useContext, useMemo } from 'react';
3
2
  import classNames from 'classnames';
4
3
  import dataTests from '../global/data-tests';
5
4
  import { getRect } from '../global/dom';
@@ -20,19 +19,26 @@ export const CollapseContent = ({ children, minHeight = DEFAULT_HEIGHT, 'data-te
20
19
  const contentRef = useRef(null);
21
20
  const initialContentHeight = useRef(minHeight);
22
21
  const contentHeight = useRef(DEFAULT_HEIGHT);
23
- const [dimensions, setDimensions] = useState({
24
- width: 0,
25
- height: 0,
26
- });
22
+ const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
27
23
  const [height, setHeight] = useState(toPx(minHeight));
28
- const [showFade, setShowFade] = useState(true);
24
+ const [showFade, setShowFade] = useState(collapsed);
25
+ const [shouldHideContent, setShouldHideContent] = useState(collapsed && minHeight <= DEFAULT_HEIGHT);
29
26
  useEffect(() => {
30
- if (!collapsed) {
31
- setShowFade(false);
32
- }
33
- else {
34
- setShowFade(true);
27
+ function onTransitionEnd() {
28
+ if (initialContentHeight.current <= DEFAULT_HEIGHT) {
29
+ setShouldHideContent(collapsed);
30
+ }
35
31
  }
32
+ const container = containerRef.current;
33
+ container?.addEventListener('transitionend', onTransitionEnd);
34
+ return () => {
35
+ container?.removeEventListener('transitionend', onTransitionEnd);
36
+ };
37
+ }, [collapsed]);
38
+ useEffect(() => {
39
+ setShowFade(collapsed);
40
+ if (!collapsed)
41
+ setShouldHideContent(false);
36
42
  }, [collapsed]);
37
43
  useEffect(() => {
38
44
  if (contentRef.current) {
@@ -63,9 +69,10 @@ export const CollapseContent = ({ children, minHeight = DEFAULT_HEIGHT, 'data-te
63
69
  };
64
70
  }, [duration, height, collapsed, minHeight]);
65
71
  const fadeShouldBeVisible = useMemo(() => Boolean(minHeight && showFade), [minHeight, showFade]);
72
+ const shouldRenderContent = disableAnimation ? !collapsed : !shouldHideContent;
66
73
  return (<div ref={containerRef} id={`collapse-content-${id}`} data-test={dataTests(COLLAPSE_CONTENT_CONTAINER_TEST_ID)} className={classNames(styles.container, { [styles.transition]: !disableAnimation })} style={style}>
67
74
  <div ref={contentRef} data-test={dataTests(COLLAPSE_CONTENT_TEST_ID, dataTest)}>
68
- {children}
75
+ {shouldRenderContent ? children : null}
69
76
  </div>
70
77
  {fadeShouldBeVisible && <div className={styles.fade}/>}
71
78
  </div>);
@@ -5,6 +5,8 @@ type Props = {
5
5
  duration?: number;
6
6
  disableAnimation?: boolean;
7
7
  className?: string;
8
+ defaultCollapsed?: boolean;
9
+ collapsed?: boolean | null;
8
10
  };
9
11
  /**
10
12
  * @name Collapse
@@ -1,20 +1,21 @@
1
- import { useCallback, useId, useState } from 'react';
1
+ import { useCallback, useId, useMemo, useState } from 'react';
2
2
  import * as React from 'react';
3
3
  import { CollapseContext } from './collapse-context';
4
4
  import { BASE_ANIMATION_DURATION } from './consts';
5
5
  /**
6
6
  * @name Collapse
7
7
  */
8
- export const Collapse = ({ children, duration = BASE_ANIMATION_DURATION, disableAnimation = false, className = '', onChange = () => { }, }) => {
9
- const [collapsed, toggle] = useState(true);
8
+ export const Collapse = ({ children, duration = BASE_ANIMATION_DURATION, disableAnimation = false, className = '', onChange = () => { }, defaultCollapsed = true, collapsed = null, }) => {
9
+ const [innerCollapsed, setInnerCollapsed] = useState(defaultCollapsed);
10
10
  const id = useId();
11
+ const finalCollapsedValue = useMemo(() => collapsed ?? innerCollapsed, [innerCollapsed, collapsed]);
11
12
  const setCollapsed = useCallback(() => {
12
- toggle(!collapsed);
13
- onChange(!collapsed);
14
- }, [toggle, onChange, collapsed]);
13
+ setInnerCollapsed(!finalCollapsedValue);
14
+ onChange(!finalCollapsedValue);
15
+ }, [setInnerCollapsed, onChange, finalCollapsedValue]);
15
16
  return (<div className={className}>
16
17
  <CollapseContext.Provider value={{
17
- collapsed,
18
+ collapsed: finalCollapsedValue,
18
19
  setCollapsed,
19
20
  duration,
20
21
  disableAnimation,
@@ -48,20 +48,24 @@
48
48
 
49
49
  box-sizing: border-box;
50
50
 
51
- width: 100%;
51
+ width: calc(100% - 2 * var(--ring-unit));
52
52
 
53
53
  text-align: left;
54
54
  vertical-align: bottom;
55
55
  white-space: nowrap;
56
56
  text-decoration: none;
57
57
 
58
+ border-radius: var(--ring-border-radius);
59
+
58
60
  outline: none;
59
61
 
60
62
  font-size: var(--ring-font-size);
63
+
64
+ margin-inline: var(--ring-unit);
61
65
  }
62
66
 
63
67
  .item.item {
64
- padding: 3px calc(var(--ring-unit) * 2) 5px;
68
+ padding: 3px var(--ring-unit) 5px;
65
69
 
66
70
  line-height: calc(var(--ring-unit) * 3);
67
71
  }
@@ -160,12 +164,17 @@
160
164
 
161
165
  /* Override :last-child */
162
166
  .hint.hint {
167
+ width: 100%;
168
+
163
169
  margin-bottom: 0;
164
170
 
165
171
  border-top: 1px solid var(--ring-line-color);
172
+ border-radius: 0;
166
173
  background-color: var(--ring-sidebar-background-color);
167
174
 
168
175
  font-size: var(--ring-font-size-smaller);
176
+ margin-inline: 0;
177
+ padding-inline: calc(2 * var(--ring-unit));
169
178
  }
170
179
 
171
180
  .action {
@@ -179,7 +188,12 @@
179
188
  transition: none;
180
189
  }
181
190
 
182
- .hover:not(.error) {
191
+ .item:hover:not(.error) {
192
+ background-color: var(--ring-hover-background-color);
193
+ }
194
+
195
+ /* TODO rename .hover to .selected in 8.0 */
196
+ .item.hover:not(.error) {
183
197
  background-color: var(--ring-selected-background-color);
184
198
  }
185
199
 
@@ -67,7 +67,6 @@ export interface ListState<T = unknown> {
67
67
  needScrollToActive: boolean;
68
68
  scrolling: boolean;
69
69
  hasOverflow: boolean;
70
- disabledHover: boolean;
71
70
  scrolledToBottom: boolean;
72
71
  }
73
72
  interface RenderVirtualizedInnerParams extends Partial<WindowScrollerChildProps> {
@@ -109,7 +108,6 @@ export default class List<T = unknown> extends Component<ListProps<T>, ListState
109
108
  componentDidUpdate(prevProps: ListProps<T>): void;
110
109
  componentWillUnmount(): void;
111
110
  scheduleScrollListener: (cb: () => void) => void;
112
- scheduleHoverListener: (cb: () => void) => void;
113
111
  static isItemType: typeof isItemType;
114
112
  static ListHint: typeof ListHint;
115
113
  static ListProps: {
@@ -129,7 +127,6 @@ export default class List<T = unknown> extends Component<ListProps<T>, ListState
129
127
  virtualizedList?: VirtualizedList | null;
130
128
  unmounted?: boolean;
131
129
  container?: HTMLElement | null;
132
- hoverHandler: (arg: number) => () => void;
133
130
  private _bufferSize;
134
131
  sizeCacheKey: (index: number) => string | Type.ITEM | Type.MARGIN;
135
132
  private _cache;
@@ -142,15 +139,12 @@ export default class List<T = unknown> extends Component<ListProps<T>, ListState
142
139
  downHandler: (e: KeyboardEvent) => void;
143
140
  homeHandler: (e: KeyboardEvent) => void;
144
141
  endHandler: (e: KeyboardEvent) => void;
145
- onDocumentMouseMove: () => void;
146
- onDocumentKeyDown: (e: KeyboardEvent) => void;
147
142
  moveHandler(index: number, retryCallback: (e: KeyboardEvent) => void, e: KeyboardEvent): void;
148
143
  mouseHandler: () => void;
149
144
  scrollHandler: () => void;
150
145
  enterHandler: (event: KeyboardEvent, shortcut?: string) => boolean;
151
146
  getFirst(): ListDataItem<T> | undefined;
152
147
  getSelected(): ListDataItem<T> | null;
153
- clearSelected: () => void;
154
148
  defaultItemHeight(): number;
155
149
  scrollEndHandler: () => void;
156
150
  checkOverflow: () => void;
@@ -77,7 +77,6 @@ export default class List extends Component {
77
77
  needScrollToActive: false,
78
78
  scrolling: false,
79
79
  hasOverflow: false,
80
- disabledHover: false,
81
80
  scrolledToBottom: false,
82
81
  };
83
82
  static getDerivedStateFromProps(nextProps, prevState) {
@@ -110,8 +109,6 @@ export default class List extends Component {
110
109
  return nextState;
111
110
  }
112
111
  componentDidMount() {
113
- document.addEventListener('mousemove', this.onDocumentMouseMove);
114
- document.addEventListener('keydown', this.onDocumentKeyDown, true);
115
112
  if (this.props.activeIndex == null && shouldActivateFirstItem(this.props)) {
116
113
  this.activateFirst();
117
114
  }
@@ -133,11 +130,8 @@ export default class List extends Component {
133
130
  }
134
131
  componentWillUnmount() {
135
132
  this.unmounted = true;
136
- document.removeEventListener('mousemove', this.onDocumentMouseMove);
137
- document.removeEventListener('keydown', this.onDocumentKeyDown, true);
138
133
  }
139
134
  scheduleScrollListener = scheduleRAF();
140
- scheduleHoverListener = scheduleRAF();
141
135
  static isItemType = isItemType;
142
136
  static ListHint = ListHint;
143
137
  static ListProps = {
@@ -147,18 +141,6 @@ export default class List extends Component {
147
141
  virtualizedList;
148
142
  unmounted;
149
143
  container;
150
- hoverHandler = memoize((index) => () => this.scheduleHoverListener(() => {
151
- if (this.state.disabledHover) {
152
- return;
153
- }
154
- if (this.container) {
155
- this.setState({
156
- activeIndex: index,
157
- activeItem: this.props.data[index],
158
- needScrollToActive: false,
159
- });
160
- }
161
- }));
162
144
  // eslint-disable-next-line @typescript-eslint/no-magic-numbers
163
145
  _bufferSize = 10; // keep X items above and below of the visible area
164
146
  // reuse size cache for similar items
@@ -260,17 +242,6 @@ export default class List extends Component {
260
242
  endHandler = (e) => {
261
243
  this.moveHandler(this.props.data.length - 1, this.upHandler, e);
262
244
  };
263
- onDocumentMouseMove = () => {
264
- if (this.state.disabledHover) {
265
- this.setState({ disabledHover: false });
266
- }
267
- };
268
- onDocumentKeyDown = (e) => {
269
- const metaKeys = [16, 17, 18, 19, 20, 91]; // eslint-disable-line @typescript-eslint/no-magic-numbers
270
- if (!this.state.disabledHover && !metaKeys.includes(e.keyCode)) {
271
- this.setState({ disabledHover: true });
272
- }
273
- };
274
245
  moveHandler(index, retryCallback, e) {
275
246
  let correctedIndex;
276
247
  if (this.props.data.length === 0 || !this.hasActivatableItems()) {
@@ -330,12 +301,6 @@ export default class List extends Component {
330
301
  getSelected() {
331
302
  return this.state.activeIndex != null ? this.props.data[this.state.activeIndex] : null;
332
303
  }
333
- clearSelected = () => {
334
- this.setState({
335
- activeIndex: null,
336
- needScrollToActive: false,
337
- });
338
- };
339
304
  defaultItemHeight() {
340
305
  return this.props.compact ? Dimension.COMPACT_ITEM_HEIGHT : Dimension.ITEM_HEIGHT;
341
306
  }
@@ -404,7 +369,6 @@ export default class List extends Component {
404
369
  if (itemProps.hoverClassName != null && itemProps.hover) {
405
370
  itemProps.className = classNames(itemProps.className, itemProps.hoverClassName);
406
371
  }
407
- itemProps.onMouseOver = this.hoverHandler(realIndex);
408
372
  itemProps.tabIndex = -1;
409
373
  itemProps.scrolling = isScrolling;
410
374
  const selectHandler = this.selectHandler(realIndex);
@@ -528,7 +492,7 @@ export default class List extends Component {
528
492
  const classes = classNames(styles.list, this.props.className);
529
493
  return (<>
530
494
  <ActiveItemContext.Updater value={this.getId(this.state.activeItem)} skipUpdate={this.props.hidden || !isActivatable(this.state.activeItem)}/>
531
- <div id={this.props.id} ref={this.containerRef} className={classes} onMouseOut={this.props.onMouseOut} onBlur={this.props.onMouseOut} onMouseLeave={this.clearSelected} data-test="ring-list">
495
+ <div id={this.props.id} ref={this.containerRef} className={classes} onMouseOut={this.props.onMouseOut} onBlur={this.props.onMouseOut} data-test="ring-list">
532
496
  {this.props.shortcuts && (<Shortcuts map={this.props.shortcutsMap ? { ...this.shortcutsMap, ...this.props.shortcutsMap } : this.shortcutsMap} scope={this.shortcutsScope}/>)}
533
497
  {this.props.renderOptimization
534
498
  ? this.renderVirtualized(maxHeight, rowCount)
@@ -12,7 +12,7 @@ import styles from './list.css';
12
12
  * @extends {ReactComponent}
13
13
  */
14
14
  const RING_UNIT = 8;
15
- const DEFAULT_PADDING = 16;
15
+ const DEFAULT_PADDING = 8;
16
16
  const CHECKBOX_WIDTH = 28;
17
17
  export default class ListItem extends PureComponent {
18
18
  id = getUID('list-item-');
@@ -19,6 +19,10 @@
19
19
  box-shadow: var(--ring-popup-shadow);
20
20
  }
21
21
 
22
+ .largeBorderRadius {
23
+ border-radius: var(--ring-border-radius-large);
24
+ }
25
+
22
26
  .hidden {
23
27
  display: none;
24
28
  }
@@ -31,6 +31,7 @@ export interface BasePopupProps {
31
31
  legacy: boolean;
32
32
  withTail?: boolean;
33
33
  tailOffset?: number;
34
+ largeBorderRadius?: boolean;
34
35
  anchorElement?: HTMLElement | null | undefined;
35
36
  target?: string | Element | null | undefined;
36
37
  className?: string | null | undefined;
@@ -256,12 +256,13 @@ export default class Popup extends PureComponent {
256
256
  esc: this._onEscPress,
257
257
  };
258
258
  render() {
259
- const { className, style, hidden, attached, keepMounted, client, onMouseDown, onMouseUp, onMouseOver, onMouseOut, onContextMenu, 'data-test': dataTest, } = this.props;
259
+ const { className, style, hidden, attached, keepMounted, client, onMouseDown, onMouseUp, onMouseOver, onMouseOut, onContextMenu, 'data-test': dataTest, largeBorderRadius, } = this.props;
260
260
  const showing = this.state.display === Display.SHOWING;
261
261
  const classes = classNames(className, styles.popup, {
262
262
  [styles.attached]: attached,
263
263
  [styles.hidden]: hidden,
264
264
  [styles.showing]: showing,
265
+ [styles.largeBorderRadius]: largeBorderRadius,
265
266
  });
266
267
  const direction = (this.state.direction || '').toLowerCase().replace(/[_]/g, '-');
267
268
  return (<PopupTargetContext.Consumer>
@@ -27,6 +27,7 @@ export default class PopupMenu<T = unknown> extends Popup<PopupMenuProps<T>> {
27
27
  static defaultProps: {
28
28
  renderOptimization: boolean;
29
29
  closeOnSelect: boolean;
30
+ largeBorderRadius: boolean;
30
31
  shortcuts: boolean;
31
32
  hidden: boolean;
32
33
  onOutsideClick(): void;
@@ -11,6 +11,7 @@ export default class PopupMenu extends Popup {
11
11
  ...Popup.defaultProps,
12
12
  renderOptimization: false,
13
13
  closeOnSelect: false,
14
+ largeBorderRadius: true,
14
15
  };
15
16
  onSelect = (item, event) => {
16
17
  if (this.props.closeOnSelect) {
@@ -328,7 +328,7 @@ export default class SelectPopup extends PureComponent {
328
328
  const list = this.getList(this.props.ringPopupTarget || ringPopupTarget);
329
329
  const bottomLine = this.getBottomLine();
330
330
  const hasContent = filterWithTags || selectAll || list || bottomLine || toolbar || topbar;
331
- return (<Popup trapFocus={false} ref={this.popupRef} hidden={hidden || !hasContent} attached={isInputMode} className={classes} dontCloseOnAnchorClick anchorElement={anchorElement} minWidth={minWidth} onCloseAttempt={onCloseAttempt} onOutsideClick={onOutsideClick} directions={directions} top={top} left={left} offset={offset} onMouseDown={this.mouseDownHandler} target={this.props.ringPopupTarget} autoCorrectTopOverflow={false} style={style}>
331
+ return (<Popup trapFocus={false} ref={this.popupRef} hidden={hidden || !hasContent} attached={isInputMode} className={classes} dontCloseOnAnchorClick anchorElement={anchorElement} minWidth={minWidth} onCloseAttempt={onCloseAttempt} onOutsideClick={onOutsideClick} directions={directions} top={top} left={left} offset={offset} onMouseDown={this.mouseDownHandler} target={this.props.ringPopupTarget} autoCorrectTopOverflow={false} style={style} largeBorderRadius>
332
332
  <div dir={dir}>
333
333
  {!hidden && filter && <Shortcuts map={this.shortcutsMap} scope={this.shortcutsScope}/>}
334
334
  {topbar}
@@ -10,7 +10,7 @@ export declare const AnchorLink: ({ hasActiveChildren, moreClassName, moreActive
10
10
  export interface MoreButtonProps {
11
11
  items: ReactElement<TabProps>[];
12
12
  selected?: string | undefined;
13
- onSelect: (key: string) => () => void;
13
+ onSelect?: ((key: string) => () => void) | null | undefined;
14
14
  moreClassName?: string | null | undefined;
15
15
  moreActiveClassName?: string | null | undefined;
16
16
  morePopupClassName?: string | null | undefined;
@@ -1,4 +1,4 @@
1
- import { memo, useCallback, useMemo } from 'react';
1
+ import { memo, useMemo } from 'react';
2
2
  import classNames from 'classnames';
3
3
  import { Directions } from '../popup/popup.consts';
4
4
  import PopupMenu, { ListProps } from '../popup-menu/popup-menu';
@@ -15,13 +15,15 @@ export const AnchorLink = ({ hasActiveChildren, moreClassName, moreActiveClassNa
15
15
  };
16
16
  const morePopupDirections = [Directions.BOTTOM_CENTER, Directions.BOTTOM_LEFT, Directions.BOTTOM_RIGHT];
17
17
  export const MoreButton = memo(({ items, selected, onSelect, moreClassName, moreActiveClassName, morePopupClassName, morePopupItemClassName, morePopupBeforeEnd, }) => {
18
- const onSelectHandler = useCallback((listItem) => {
19
- if (listItem.disabled === true || listItem.custom === true) {
20
- return;
18
+ const onSelectHandler = useMemo(() => onSelect != null
19
+ ? (listItem) => {
20
+ if (listItem.disabled === true || listItem.custom === true) {
21
+ return;
22
+ }
23
+ const cb = onSelect(String(listItem.key));
24
+ cb();
21
25
  }
22
- const cb = onSelect(String(listItem.key));
23
- cb();
24
- }, [onSelect]);
26
+ : undefined, [onSelect]);
25
27
  const hasActiveChild = useMemo(() => items.some(item => item.props.alwaysHidden && item.props.id === selected), [items, selected]);
26
28
  const data = useMemo(() => {
27
29
  const popupItems = getTabTitles({
@@ -3,8 +3,7 @@ import classNames from 'classnames';
3
3
  import styles from './tabs.css';
4
4
  import TabLink from './tab-link';
5
5
  import { CustomItem } from './custom-item';
6
- function noop() { }
7
- const TabTitle = React.memo(function TabTitle({ selected, child, handleSelect = noop, collapsed = false, tabIndex, }) {
6
+ const TabTitle = React.memo(function TabTitle({ selected, child, handleSelect, collapsed = false, tabIndex, }) {
8
7
  if (child == null || typeof child !== 'object' || child.type === CustomItem) {
9
8
  return child;
10
9
  }
@@ -3,7 +3,7 @@ import { TabProps } from './tab';
3
3
  export interface CollapsibleTabsProps {
4
4
  children: ReactElement<TabProps>[];
5
5
  selected?: string | undefined;
6
- onSelect: (key: string) => () => void;
6
+ onSelect?: ((key: string) => () => void) | undefined;
7
7
  moreClassName?: string | null | undefined;
8
8
  moreActiveClassName?: string | null | undefined;
9
9
  morePopupClassName?: string | null | undefined;
@@ -7,17 +7,14 @@ export { CustomItem };
7
7
  export type Children = readonly (Children | null | boolean)[] | ReactElement<TabProps> | null | boolean;
8
8
  export interface TabsProps extends Omit<CollapsibleTabsProps, 'onSelect' | 'children'> {
9
9
  children: Children;
10
- onSelect: (key: string) => void;
10
+ onSelect?: ((key: string) => void) | null | undefined;
11
11
  className?: string | null | undefined;
12
12
  tabContainerClassName?: string | null | undefined;
13
13
  autoCollapse?: boolean | null | undefined;
14
14
  'data-test'?: string | null | undefined;
15
15
  }
16
16
  declare class Tabs extends PureComponent<TabsProps> {
17
- static defaultProps: {
18
- onSelect(): void;
19
- };
20
- handleSelect: (arg: string) => () => void;
17
+ handleSelect: (arg: string) => () => void | undefined;
21
18
  getTabTitle: (child: ReactElement<TabProps>, i: number) => React.JSX.Element;
22
19
  render(): React.JSX.Element;
23
20
  }
@@ -9,29 +9,26 @@ import CollapsibleTabs from './collapsible-tabs';
9
9
  import { CustomItem } from './custom-item';
10
10
  export { CustomItem };
11
11
  class Tabs extends PureComponent {
12
- static defaultProps = {
13
- onSelect() { },
14
- };
15
- handleSelect = memoize((key) => () => this.props.onSelect(key));
12
+ handleSelect = memoize((key) => () => this.props.onSelect?.(key));
16
13
  getTabTitle = (child, i) => {
17
14
  if (child == null || typeof child !== 'object' || child.type === CustomItem) {
18
15
  return child;
19
16
  }
20
- const { selected } = this.props;
17
+ const { selected, onSelect } = this.props;
21
18
  const { title, titleProps, id, disabled, href, className, activeClassName } = child.props;
22
19
  const key = id || String(i);
23
20
  const isSelected = key === selected;
24
21
  const titleClasses = classNames(styles.title, className, isSelected && activeClassName, {
25
22
  [styles.selected]: isSelected,
26
23
  });
27
- return (<TabLink title={title} isSelected={isSelected} key={key} href={href} className={titleClasses} disabled={disabled} onPlainLeftClick={this.handleSelect(key)} {...titleProps}/>);
24
+ return (<TabLink title={title} isSelected={isSelected} key={key} href={href} className={titleClasses} disabled={disabled} onPlainLeftClick={onSelect != null ? this.handleSelect(key) : undefined} {...titleProps}/>);
28
25
  };
29
26
  render() {
30
- const { className, tabContainerClassName, children, selected, autoCollapse, 'data-test': dataTest, ...restProps } = this.props;
27
+ const { className, tabContainerClassName, children, selected, autoCollapse, 'data-test': dataTest, onSelect, ...restProps } = this.props;
31
28
  const classes = classNames(styles.tabs, className);
32
29
  const childrenArray = React.Children.toArray(children).filter(Boolean);
33
30
  return (<div className={classes} data-test={dataTests('ring-dumb-tabs', dataTest)}>
34
- {autoCollapse === true ? (<CollapsibleTabs {...restProps} onSelect={this.handleSelect} selected={selected}>
31
+ {autoCollapse === true ? (<CollapsibleTabs {...restProps} onSelect={onSelect != null ? this.handleSelect : undefined} selected={selected}>
35
32
  {childrenArray}
36
33
  </CollapsibleTabs>) : (<div className={styles.titles}>{childrenArray.map(this.getTabTitle)}</div>)}
37
34
  <div className={classNames(tabContainerClassName)}>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jetbrains/ring-ui",
3
- "version": "7.0.7",
3
+ "version": "7.0.9",
4
4
  "description": "JetBrains UI library",
5
5
  "author": "JetBrains",
6
6
  "license": "Apache-2.0",
@@ -94,18 +94,18 @@
94
94
  "@rollup/plugin-json": "^6.1.0",
95
95
  "@rollup/plugin-node-resolve": "^15.3.0",
96
96
  "@rollup/plugin-replace": "^6.0.1",
97
- "@storybook/addon-a11y": "8.4.4",
98
- "@storybook/addon-docs": "8.4.4",
99
- "@storybook/addon-essentials": "8.4.4",
100
- "@storybook/addon-themes": "^8.4.4",
101
- "@storybook/components": "8.4.4",
97
+ "@storybook/addon-a11y": "8.4.5",
98
+ "@storybook/addon-docs": "8.4.5",
99
+ "@storybook/addon-essentials": "8.4.5",
100
+ "@storybook/addon-themes": "^8.4.5",
101
+ "@storybook/components": "8.4.5",
102
102
  "@storybook/csf": "^0.1.11",
103
- "@storybook/manager-api": "8.4.4",
104
- "@storybook/preview-api": "8.4.4",
105
- "@storybook/react": "8.4.4",
106
- "@storybook/react-webpack5": "8.4.4",
107
- "@storybook/test-runner": "^0.19.1",
108
- "@storybook/theming": "8.4.4",
103
+ "@storybook/manager-api": "8.4.5",
104
+ "@storybook/preview-api": "8.4.5",
105
+ "@storybook/react": "8.4.5",
106
+ "@storybook/react-webpack5": "8.4.5",
107
+ "@storybook/test-runner": "^0.20.0",
108
+ "@storybook/theming": "8.4.5",
109
109
  "@testing-library/dom": "^10.4.0",
110
110
  "@testing-library/react": "^16.0.1",
111
111
  "@testing-library/user-event": "^14.5.2",
@@ -121,22 +121,22 @@
121
121
  "@types/sinon": "^17.0.3",
122
122
  "@types/sinon-chai": "^4.0.0",
123
123
  "@types/webpack-env": "^1.18.5",
124
- "@vitejs/plugin-react": "^4.3.3",
125
- "@vitest/eslint-plugin": "^1.1.10",
124
+ "@vitejs/plugin-react": "^4.3.4",
125
+ "@vitest/eslint-plugin": "^1.1.12",
126
126
  "@wojtekmaj/enzyme-adapter-react-17": "^0.8.0",
127
127
  "acorn": "^8.14.0",
128
128
  "axe-playwright": "^2.0.3",
129
129
  "babel-plugin-require-context-hook": "^1.0.0",
130
- "caniuse-lite": "^1.0.30001680",
130
+ "caniuse-lite": "^1.0.30001684",
131
131
  "chai": "^5.1.2",
132
- "chai-as-promised": "^8.0.0",
132
+ "chai-as-promised": "^8.0.1",
133
133
  "chai-dom": "^1.10.0",
134
134
  "chai-enzyme": "1.0.0-beta.1",
135
135
  "cheerio": "^1.0.0-rc.12",
136
136
  "core-js": "^3.39.0",
137
137
  "cpy-cli": "^5.0.0",
138
138
  "enzyme": "^3.11.0",
139
- "eslint": "^9.14.0",
139
+ "eslint": "^9.15.0",
140
140
  "eslint-config-prettier": "^9.1.0",
141
141
  "eslint-formatter-jslint-xml": "^8.40.0",
142
142
  "eslint-import-resolver-webpack": "^0.13.9",
@@ -145,13 +145,13 @@
145
145
  "eslint-plugin-prettier": "^5.2.1",
146
146
  "eslint-plugin-react": "^7.37.2",
147
147
  "eslint-plugin-react-hooks": "^5.0.0",
148
- "eslint-plugin-storybook": "^0.11.0",
148
+ "eslint-plugin-storybook": "^0.11.1",
149
149
  "events": "^3.3.0",
150
150
  "glob": "^11.0.0",
151
151
  "globals": "^15.12.0",
152
152
  "html-webpack-plugin": "^5.6.3",
153
153
  "http-server": "^14.1.1",
154
- "husky": "^9.1.6",
154
+ "husky": "^9.1.7",
155
155
  "identity-obj-proxy": "^3.0.0",
156
156
  "jest": "~29.7.0",
157
157
  "jest-environment-jsdom": "^29.7.0",
@@ -160,26 +160,26 @@
160
160
  "markdown-it": "^14.1.0",
161
161
  "merge-options": "^3.0.4",
162
162
  "pinst": "^3.0.0",
163
- "prettier": "^3.3.3",
163
+ "prettier": "^3.4.1",
164
164
  "raw-loader": "^4.0.2",
165
165
  "react": "^18.3.1",
166
166
  "react-dom": "^18.3.1",
167
167
  "react-test-renderer": "^18.3.1",
168
168
  "regenerator-runtime": "^0.14.1",
169
169
  "rimraf": "^6.0.1",
170
- "rollup": "^4.27.2",
170
+ "rollup": "^4.27.4",
171
171
  "rollup-plugin-clear": "^2.0.7",
172
172
  "sinon": "^19.0.2",
173
173
  "sinon-chai": "^4.0.0",
174
174
  "storage-mock": "^2.1.0",
175
- "storybook": "8.4.4",
176
- "stylelint": "^16.10.0",
175
+ "storybook": "8.4.5",
176
+ "stylelint": "^16.11.0",
177
177
  "svg-inline-loader": "^0.8.2",
178
178
  "teamcity-service-messages": "^0.1.14",
179
179
  "terser-webpack-plugin": "^5.3.10",
180
180
  "typescript": "~5.6.3",
181
- "typescript-eslint": "^8.14.0",
182
- "vitest": "^2.1.5",
181
+ "typescript-eslint": "^8.16.0",
182
+ "vitest": "^2.1.6",
183
183
  "vitest-teamcity-reporter": "^0.3.1",
184
184
  "wallaby-webpack": "^3.9.16",
185
185
  "webpack": "^5.96.1",
@@ -209,11 +209,11 @@
209
209
  "@babel/core": "^7.26.0",
210
210
  "@babel/preset-typescript": "^7.26.0",
211
211
  "@jetbrains/babel-preset-jetbrains": "^2.4.0",
212
- "@jetbrains/icons": "^5.2.0",
212
+ "@jetbrains/icons": "^5.3.0",
213
213
  "@jetbrains/postcss-require-hover": "^0.1.2",
214
214
  "@types/combokeys": "^2.4.9",
215
215
  "@types/element-resize-detector": "^1.1.6",
216
- "@types/react-virtualized": "9.21.30",
216
+ "@types/react-virtualized": "9.22.0",
217
217
  "@types/util-deprecate": "^1.0.4",
218
218
  "babel-loader": "9.2.1",
219
219
  "babel-plugin-transform-define": "^2.1.4",