@jetbrains/ring-ui 7.0.85 → 7.0.86

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.
@@ -21,6 +21,7 @@ export interface InputBaseProps {
21
21
  enableShortcuts?: boolean | string[];
22
22
  children?: string | undefined;
23
23
  inputClassName?: string | null | undefined;
24
+ clearButtonClassName?: string | null | undefined;
24
25
  label?: ReactNode;
25
26
  labelType?: LabelType;
26
27
  error?: ReactNode | null | undefined;
@@ -129,7 +129,7 @@ export class Input extends PureComponent {
129
129
  {icon && <Icon glyph={icon} className={styles.icon}/>}
130
130
  {beforeInput}
131
131
  {multiline ? (<textarea onChange={this.handleTextareaChange} rows={1} {...commonProps} {...restProps}/>) : (<input onChange={this.handleInputChange} {...commonProps} {...restProps}/>)}
132
- {clearable && !disabled && (<Button title={translations?.clear ?? translate('clear')} data-test='ring-input-clear' className={styles.clear} icon={closeIcon} onClick={this.clear}/>)}
132
+ {clearable && !disabled && (<Button title={translations?.clear ?? translate('clear')} data-test='ring-input-clear' className={classNames(styles.clear, this.props.clearButtonClassName)} icon={closeIcon} onClick={this.clear}/>)}
133
133
  {afterInput}
134
134
  </div>
135
135
  {error ? (<div className={styles.errorText}>{error}</div>) : (help && <ControlHelp className={styles.helpText}>{help}</ControlHelp>)}
@@ -77,5 +77,9 @@ export interface ListDataItemAddProps {
77
77
  onCheckboxChange: (e: React.ChangeEvent) => void;
78
78
  isFirst?: boolean;
79
79
  'data-test'?: string | null | undefined;
80
+ itemClassName?: string | null | undefined;
81
+ labelClassName?: string | null | undefined;
82
+ descriptionClassName?: string | null | undefined;
83
+ detailsClassName?: string | null | undefined;
80
84
  }
81
85
  export type ListDataItemProps<T = unknown> = Omit<ListDataItem<T>, keyof ListDataItemAddProps> & ListDataItemAddProps;
@@ -22,7 +22,7 @@ export default class ListItem extends PureComponent {
22
22
  _isString = (val) => typeof val === 'string' || val instanceof String;
23
23
  // eslint-disable-next-line complexity
24
24
  render() {
25
- const { disabled, checkbox, avatar, subavatar, glyph, icon, rightGlyph, description, label, title, details, hover, level, tabIndex, onClick, onCheckboxChange, onMouseOver, onMouseDown, onMouseUp, rightNodes, leftNodes, showGeneratedAvatar, username, labelWrapper, rgItemType, scrolling, 'data-test': dataTest, className, url, LinkComponent, compact, hoverClassName, children, ...restLinkProps // TODO use an allow list in 8.0
25
+ const { disabled, checkbox, avatar, subavatar, glyph, icon, rightGlyph, description, label, title, details, hover, level, tabIndex, onClick, onCheckboxChange, onMouseOver, onMouseDown, onMouseUp, rightNodes, leftNodes, showGeneratedAvatar, username, labelWrapper, rgItemType, scrolling, 'data-test': dataTest, className, url, LinkComponent, compact, hoverClassName, children, itemClassName, labelClassName, descriptionClassName, detailsClassName, ...restLinkProps // TODO use an allow list in 8.0
26
26
  } = this.props;
27
27
  const checkable = checkbox !== undefined;
28
28
  const shouldShowGeneratedAvatar = showGeneratedAvatar && !!username;
@@ -32,7 +32,7 @@ export default class ListItem extends PureComponent {
32
32
  const detailsClasses = classNames({
33
33
  [styles.details]: details,
34
34
  [styles.padded]: icon !== undefined || checkbox !== undefined || glyph !== undefined,
35
- });
35
+ }, detailsClassName);
36
36
  const style = {
37
37
  paddingLeft: `${(Number(level) || 0) * RING_UNIT + DEFAULT_PADDING + (showCheckbox ? CHECKBOX_WIDTH : 0)}px`,
38
38
  };
@@ -53,7 +53,7 @@ export default class ListItem extends PureComponent {
53
53
  'ring-list-item-selected': checkbox,
54
54
  'ring-list-link': isLink,
55
55
  }, dataTest);
56
- const labelElement = (<span className={styles.label} title={computedTitle} data-test='ring-list-item-label'>
56
+ const labelElement = (<span className={classNames(styles.label, labelClassName)} title={computedTitle} data-test='ring-list-item-label'>
57
57
  {label ?? children}
58
58
  </span>);
59
59
  const commonProps = {
@@ -77,7 +77,7 @@ export default class ListItem extends PureComponent {
77
77
 
78
78
  {labelWrapper ? labelWrapper(labelElement) : labelElement}
79
79
 
80
- {description && (<span className={styles.description} data-test='ring-list-item-description'>
80
+ {description && (<span className={classNames(styles.description, descriptionClassName)} data-test='ring-list-item-description'>
81
81
  {description}
82
82
  </span>)}
83
83
 
@@ -92,7 +92,7 @@ export default class ListItem extends PureComponent {
92
92
  </>),
93
93
  };
94
94
  const LinkComponentToUse = LinkComponent ? linkHOC(LinkComponent) : Link;
95
- return (<div className={styles.itemContainer} data-test={combinedDataTest}>
95
+ return (<div className={classNames(styles.itemContainer, itemClassName)} data-test={combinedDataTest}>
96
96
  {showCheckbox && (<div className={styles.checkboxContainer}>
97
97
  <Checkbox aria-labelledby={this.id} checked={checkbox} disabled={disabled} onChange={onCheckboxChange} onClick={this.stopBubbling}/>
98
98
  </div>)}
@@ -34,6 +34,10 @@ export interface ListProps<T = unknown> {
34
34
  ariaLabel: string;
35
35
  id?: string | undefined;
36
36
  className?: string | null | undefined;
37
+ itemClassName?: string | null | undefined;
38
+ labelClassName?: string | null | undefined;
39
+ descriptionClassName?: string | null | undefined;
40
+ detailsClassName?: string | null | undefined;
37
41
  hint?: ReactNode;
38
42
  hintOnSelection?: string | null | undefined;
39
43
  maxHeight?: number | null | undefined;
@@ -399,6 +399,11 @@ export default class List extends Component {
399
399
  if (itemProps.compact === null || itemProps.compact === undefined) {
400
400
  itemProps.compact = this.props.compact;
401
401
  }
402
+ // Add global className props for sub-elements
403
+ itemProps.itemClassName = this.props.itemClassName;
404
+ itemProps.labelClassName = this.props.labelClassName;
405
+ itemProps.descriptionClassName = this.props.descriptionClassName;
406
+ itemProps.detailsClassName = this.props.detailsClassName;
402
407
  let ItemComponent;
403
408
  const isFirst = index === 1;
404
409
  switch (itemProps.rgItemType) {
@@ -81,6 +81,14 @@ export interface QueryAssistProps {
81
81
  * Additional class for the input
82
82
  */
83
83
  inputClassName?: string | null | undefined;
84
+ /**
85
+ * Additional class for the search button/icon
86
+ */
87
+ searchButtonClassName?: string | null | undefined;
88
+ /**
89
+ * Additional class for the clear icon
90
+ */
91
+ clearIconClassName?: string | null | undefined;
84
92
  /**
85
93
  * Data source function
86
94
  */
@@ -679,7 +679,7 @@ export default class QueryAssist extends Component {
679
679
  const renderClear = this.props.clear && !!this.state.query;
680
680
  if (renderClear) {
681
681
  actions.push(<I18nContext.Consumer key={'clearAction'}>
682
- {({ translate }) => (<Button icon={closeIcon} className={styles.clear} title={this.props.translations?.clearTitle ?? translate('clearTitle')} ref={this.clearRef} onClick={this.clearQuery} data-test='query-assist-clear-icon'/>)}
682
+ {({ translate }) => (<Button icon={closeIcon} className={classNames(styles.clear, this.props.clearIconClassName)} title={this.props.translations?.clearTitle ?? translate('clearTitle')} ref={this.clearRef} onClick={this.clearQuery} data-test='query-assist-clear-icon'/>)}
683
683
  </I18nContext.Consumer>);
684
684
  }
685
685
  return actions;
@@ -712,7 +712,7 @@ export default class QueryAssist extends Component {
712
712
  {({ translate }) => (<div data-test={dataTests('ring-query-assist', dataTest)} className={containerClasses} role='presentation' ref={this.nodeRef}>
713
713
  {this.state.shortcuts && <Shortcuts map={this.shortcutsMap} scope={this.shortcutsScope}/>}
714
714
 
715
- {renderGlass && !huge && (<Icon glyph={searchIcon} className={styles.icon} title={translations?.searchTitle ?? translate('searchTitle')} ref={this.glassRef} data-test='query-assist-search-icon'/>)}
715
+ {renderGlass && !huge && (<Icon glyph={searchIcon} className={classNames(styles.icon, this.props.searchButtonClassName)} title={translations?.searchTitle ?? translate('searchTitle')} ref={this.glassRef} data-test='query-assist-search-icon'/>)}
716
716
 
717
717
  {renderLoader && (<div className={classNames(styles.icon, styles.loader, {
718
718
  [styles.loaderOnTheRight]: !glass && !huge,
@@ -738,7 +738,7 @@ export default class QueryAssist extends Component {
738
738
  <PopupMenu hidden={!this.state.showPopup} onCloseAttempt={this.closePopup} ref={this.popupRef} anchorElement={this.node} keepMounted attached className={this.props.popupClassName} directions={[PopupMenu.PopupProps.Directions.BOTTOM_RIGHT]} data={useCustomItemRender ? this.state.suggestions : this.renderSuggestions()} data-test='ring-query-assist-popup' hint={this.props.hint} shortcutsMap={this.listShortcutsMap} hintOnSelection={this.props.hintOnSelection} left={this.getPopupOffset(this.state.suggestions)} maxHeight={PopupMenu.PopupProps.MaxHeight.SCREEN} onMouseDown={this.trackPopupMouseState} onMouseUp={this.trackPopupMouseState} onSelect={item => this.handleComplete(item)}/>
739
739
 
740
740
  {glass && huge && (<div className={styles.rightSearchButton} data-test='query-assist-search-button'>
741
- <Icon glyph={searchIcon} className={styles.rightSearchIcon} title={translations?.searchTitle ?? translate('searchTitle')} onClick={this.handleApply} ref={this.glassRef} data-test='query-assist-search-icon'/>
741
+ <Icon glyph={searchIcon} className={classNames(styles.rightSearchIcon, this.props.searchButtonClassName)} title={translations?.searchTitle ?? translate('searchTitle')} onClick={this.handleApply} ref={this.glassRef} data-test='query-assist-search-icon'/>
742
742
  </div>)}
743
743
  </div>)}
744
744
  </I18nContext.Consumer>
@@ -44,7 +44,7 @@ export interface CustomAnchorProps {
44
44
  wrapperProps: HTMLAttributes<HTMLElement> & DataTestProps & {
45
45
  ref: RefCallback<HTMLElement>;
46
46
  };
47
- buttonProps: Pick<ButtonHTMLAttributes<HTMLButtonElement>, 'id' | 'disabled' | 'children'> & {
47
+ buttonProps: Pick<ButtonHTMLAttributes<HTMLButtonElement>, 'id' | 'disabled' | 'children' | 'className'> & {
48
48
  onClick: () => void;
49
49
  } & DataTestProps;
50
50
  popup: ReactNode;
@@ -94,6 +94,7 @@ export interface BaseSelectProps<T = unknown> {
94
94
  targetElement?: HTMLElement | null | undefined;
95
95
  className?: string | null | undefined;
96
96
  buttonClassName?: string | null | undefined;
97
+ toolbarClassName?: string | null | undefined;
97
98
  id?: string | undefined;
98
99
  getInitial?: (() => string) | null | undefined;
99
100
  minWidth?: number | undefined;
@@ -484,7 +484,7 @@ export default class Select extends Component {
484
484
  }
485
485
  return (<div className={classNames({
486
486
  [styles.toolbar]: Boolean(this.state.addButton || renderBottomToolbar),
487
- })} data-test='ring-select-toolbar'>
487
+ }, this.props.toolbarClassName)} data-test='ring-select-toolbar'>
488
488
  {renderBottomToolbar && renderBottomToolbar()}
489
489
  {this.state.addButton && (<Button inline delayed={delayed} className={classNames(styles.button, styles.buttonSpaced)} onClick={this.addHandler} data-test='ring-select-toolbar-button'>
490
490
  {prefix ? `${prefix} ${label}` : label}
@@ -872,6 +872,7 @@ export default class Select extends Component {
872
872
  disabled: this.props.disabled,
873
873
  children: this._getPlaceholder(),
874
874
  'data-test': 'ring-select__focus',
875
+ className: this.props.buttonClassName ?? undefined,
875
876
  },
876
877
  popup: this._renderPopup(),
877
878
  })}
@@ -13,6 +13,7 @@ export interface HeaderProps {
13
13
  sortOrder: boolean;
14
14
  caption?: string | null | undefined;
15
15
  checkboxDisabled?: boolean | undefined;
16
+ className?: string | null | undefined;
16
17
  }
17
18
  export default class Header extends PureComponent<HeaderProps> {
18
19
  static defaultProps: {
@@ -40,7 +40,7 @@ export default class Header extends PureComponent {
40
40
  return (<thead id={this.id} data-test='ring-table-header' style={{ top: topStickOffset }} className={classNames({
41
41
  [style.tableHead]: true,
42
42
  [style.subHeaderSticky]: sticky,
43
- })}>
43
+ }, this.props.className)}>
44
44
  {caption && (<tr data-test='ring-table-header-row'>
45
45
  <th className={classNames(style.headerCell, style.caption)} colSpan={regularCells.length + 1} data-test='ring-table-header-cell'>
46
46
  {caption}
@@ -1,14 +1,13 @@
1
1
  import { PureComponent } from 'react';
2
2
  import { type FocusSensorProps } from '../global/focus-sensor-hoc';
3
3
  import Row, { type RowProps } from './row';
4
- import { type SelectionItem } from './selection';
5
- export interface RowWithFocusSensorCallbacksProps<T extends SelectionItem> extends Omit<FocusSensorProps<RowProps<T>, HTMLTableRowElement, typeof Row>, 'onFocus'> {
4
+ export interface RowWithFocusSensorCallbacksProps<T extends object> extends Omit<FocusSensorProps<RowProps<T>, HTMLTableRowElement, typeof Row>, 'onFocus'> {
6
5
  onFocus?: (item: T) => void;
7
6
  onSelect?: (item: T, selected: boolean) => void;
8
7
  onCollapse?: (item: T) => void;
9
8
  onExpand?: (item: T) => void;
10
9
  }
11
- export default class RowWithFocusSensorCallbacks<T extends SelectionItem> extends PureComponent<RowWithFocusSensorCallbacksProps<T>> {
10
+ export default class RowWithFocusSensorCallbacks<T extends object> extends PureComponent<RowWithFocusSensorCallbacksProps<T>> {
12
11
  RowWithFocusSensor: import("react").ComponentClass<FocusSensorProps<RowProps<T>, HTMLDivElement, typeof Row>, any>;
13
12
  onFocus: () => void;
14
13
  onSelect: (item: T, selected: boolean) => void;
@@ -2,8 +2,7 @@ import { type HTMLAttributes, PureComponent } from 'react';
2
2
  import * as React from 'react';
3
3
  import { type FocusSensorAddProps } from '../global/focus-sensor-hoc';
4
4
  import { type Column } from './header-cell';
5
- import { type SelectionItem } from './selection';
6
- export interface RowProps<T extends SelectionItem> extends Omit<HTMLAttributes<HTMLTableRowElement>, 'onClick' | 'onDoubleClick' | 'onSelect'>, FocusSensorAddProps<HTMLTableRowElement> {
5
+ export interface RowProps<T extends object> extends Omit<HTMLAttributes<HTMLTableRowElement>, 'onClick' | 'onDoubleClick' | 'onSelect'>, FocusSensorAddProps<HTMLTableRowElement> {
7
6
  item: T;
8
7
  columns: readonly Column<T>[] | ((item: T) => readonly Column<T>[]);
9
8
  selectable: boolean;
@@ -27,8 +26,9 @@ export interface RowProps<T extends SelectionItem> extends Omit<HTMLAttributes<H
27
26
  autofocus?: boolean | null | undefined;
28
27
  'data-test'?: string | null | undefined;
29
28
  metaColumnClassName?: string | null | undefined;
29
+ cellClassName?: string | null | undefined;
30
30
  }
31
- export default class Row<T extends SelectionItem> extends PureComponent<RowProps<T>> {
31
+ export default class Row<T extends object> extends PureComponent<RowProps<T>> {
32
32
  static defaultProps: {
33
33
  selectable: boolean;
34
34
  showFocus: boolean;
@@ -58,4 +58,4 @@ export default class Row<T extends SelectionItem> extends PureComponent<RowProps
58
58
  composedRowRef: import("memoize-one").MemoizedFn<(...refs: (React.Ref<HTMLElement> | undefined)[]) => (value: T_1 | null) => void>;
59
59
  render(): React.JSX.Element;
60
60
  }
61
- export type RowAttrs<T extends SelectionItem> = React.JSX.LibraryManagedAttributes<typeof Row, RowProps<T>>;
61
+ export type RowAttrs<T extends object> = React.JSX.LibraryManagedAttributes<typeof Row, RowProps<T>>;
@@ -70,7 +70,7 @@ export default class Row extends PureComponent {
70
70
  };
71
71
  composedRowRef = createComposedRef();
72
72
  render() {
73
- const { item, columns: columnProps, selectable, selected, showFocus, draggable, alwaysShowDragHandle, dragHandleTitle, level, collapsible, parentCollapsible, collapsed, onCollapse, onExpand, showDisabledSelection, onSelect, checkboxTooltip, innerRef, focused, autofocus, onFocusReset, onFocusRestore, onHover, className, metaColumnClassName, 'data-test': dataTest, ...restProps } = this.props;
73
+ const { item, columns: columnProps, selectable, selected, showFocus, draggable, alwaysShowDragHandle, dragHandleTitle, level, collapsible, parentCollapsible, collapsed, onCollapse, onExpand, showDisabledSelection, onSelect, checkboxTooltip, innerRef, focused, autofocus, onFocusReset, onFocusRestore, onHover, className, metaColumnClassName, cellClassName, 'data-test': dataTest, ...restProps } = this.props;
74
74
  const classes = classNames(className, {
75
75
  [style.row]: true,
76
76
  [style.rowFocused]: showFocus,
@@ -107,7 +107,7 @@ export default class Row extends PureComponent {
107
107
  const getValue = column.getValue || (() => item[column.id]);
108
108
  const getDataTest = column.getDataTest || (() => column.id);
109
109
  const value = getValue(item, column);
110
- const cellClasses = classNames({ [style.cellRight]: column.rightAlign }, column.className);
110
+ const cellClasses = classNames({ [style.cellRight]: column.rightAlign }, column.className, cellClassName);
111
111
  const showMetaColumn = draggable || selectable || collapsible || showDisabledSelection || !!level;
112
112
  return (<Cell colSpan={column.colSpan} key={column.id} className={cellClasses} data-test={getDataTest(item, column)}>
113
113
  {index === 0 && showMetaColumn && metaColumn}
@@ -1,18 +1,17 @@
1
1
  import { type ComponentClass } from 'react';
2
2
  import { type ShortcutsMap } from '../shortcuts/core';
3
3
  import type Selection from './selection';
4
- import type { SelectionItem } from './selection';
5
- export interface SelectionShortcutsOuterProps<T extends SelectionItem> {
4
+ export interface SelectionShortcutsOuterProps<T extends object> {
6
5
  selection: Selection<T>;
7
6
  selectable?: boolean | undefined;
8
7
  onSelect?: ((selection: Selection<T>) => void) | undefined;
9
8
  shortcuts?: ShortcutsMap | undefined;
10
9
  }
11
- export interface SelectionShortcutsAddProps<T extends SelectionItem> {
10
+ export interface SelectionShortcutsAddProps<T extends object> {
12
11
  selection: Selection<T>;
13
12
  selectable: boolean;
14
13
  onSelect: (selection: Selection<T>) => void;
15
14
  shortcutsMap: ShortcutsMap;
16
15
  }
17
- export type SelectionShortcutsProps<T extends SelectionItem, P> = Omit<P, keyof SelectionShortcutsAddProps<T>> & SelectionShortcutsOuterProps<T>;
18
- export default function selectionShortcutsHOC<T extends SelectionItem, P extends SelectionShortcutsAddProps<T>>(ComposedComponent: ComponentClass<P>): ComponentClass<SelectionShortcutsProps<T, P>>;
16
+ export type SelectionShortcutsProps<T extends object, P> = Omit<P, keyof SelectionShortcutsAddProps<T>> & SelectionShortcutsOuterProps<T>;
17
+ export default function selectionShortcutsHOC<T extends object, P extends SelectionShortcutsAddProps<T>>(ComposedComponent: ComponentClass<P>): ComponentClass<SelectionShortcutsProps<T, P>>;
@@ -1,7 +1,10 @@
1
+ /**
2
+ * @deprecated SelectionItem is deprecated. Use your own item type and provide a `getKey` function instead if there is no `id` identifier in your item type.
3
+ */
1
4
  export interface SelectionItem {
2
5
  id: string | number;
3
6
  }
4
- export interface TableSelectionConfig<T extends SelectionItem> {
7
+ export interface TableSelectionConfig<T extends object> {
5
8
  data?: readonly T[] | undefined;
6
9
  selected?: Set<T> | undefined;
7
10
  focused?: T | null | undefined;
@@ -14,7 +17,7 @@ export interface CloneWithConfig<T> {
14
17
  selected?: Set<T> | readonly T[] | null | undefined;
15
18
  focused?: T | null | undefined;
16
19
  }
17
- export default class Selection<T extends SelectionItem> {
20
+ export default class Selection<T extends object> {
18
21
  protected _rawData: readonly T[];
19
22
  protected _getChildren: (item: T) => readonly T[];
20
23
  protected _data: Set<T>;
@@ -1,3 +1,6 @@
1
+ /**
2
+ * @deprecated SelectionItem is deprecated. Use your own item type and provide a `getKey` function instead if there is no `id` identifier in your item type.
3
+ */
1
4
  export default class Selection {
2
5
  _rawData;
3
6
  _getChildren;
@@ -6,7 +9,14 @@ export default class Selection {
6
9
  _focused;
7
10
  _getKey;
8
11
  _isItemSelectable;
9
- constructor({ data = [], selected = new Set(), focused = null, getKey = item => item.id, getChildren = () => [], isItemSelectable = () => true, } = {}) {
12
+ constructor({ data = [], selected = new Set(), focused = null, getKey = (item) => {
13
+ // Default behavior stays backward compatible: use item's "id" if present
14
+ if ('id' in item) {
15
+ return item.id;
16
+ }
17
+ // If there's no id provided on item and no getKey supplied, fail fast with a clear message
18
+ throw new Error('Selection: getKey is required when items have no "id" property');
19
+ }, getChildren = () => [], isItemSelectable = () => true, } = {}) {
10
20
  this._rawData = data;
11
21
  this._getChildren = getChildren;
12
22
  this._data = this._buildData(data);
@@ -5,7 +5,6 @@ import { Component, PureComponent, type ReactNode, type SyntheticEvent } from 'r
5
5
  import * as React from 'react';
6
6
  import { type OnChangeMeta } from 'react-movable/lib/types';
7
7
  import { type FocusSensorAddProps, type FocusSensorProps } from '../global/focus-sensor-hoc';
8
- import { type SelectionItem } from './selection';
9
8
  import { type SelectionShortcutsAddProps, type SelectionShortcutsProps } from './selection-shortcuts-hoc';
10
9
  import { type DisableHoverAddProps, type DisableHoverProps } from './disable-hover-hoc';
11
10
  import Row from './row-with-focus-sensor';
@@ -15,7 +14,7 @@ export interface ReorderParams<T> {
15
14
  oldIndex: number;
16
15
  newIndex: number;
17
16
  }
18
- export interface TableProps<T extends SelectionItem> extends FocusSensorAddProps<HTMLTableRowElement>, SelectionShortcutsAddProps<T>, DisableHoverAddProps {
17
+ export interface TableProps<T extends object> extends FocusSensorAddProps<HTMLTableRowElement>, SelectionShortcutsAddProps<T>, DisableHoverAddProps {
19
18
  data: readonly T[];
20
19
  columns: readonly Column<T>[] | ((item: T | null) => readonly Column<T>[]);
21
20
  isItemSelectable: (item: T) => boolean;
@@ -45,6 +44,9 @@ export interface TableProps<T extends SelectionItem> extends FocusSensorAddProps
45
44
  isDisabledSelectionVisible: (item: T) => boolean;
46
45
  getCheckboxTooltip: (item: T) => string | undefined;
47
46
  className?: string | null | undefined;
47
+ wrapperClassName?: string | null | undefined;
48
+ headerClassName?: string | null | undefined;
49
+ cellClassName?: string | null | undefined;
48
50
  loaderClassName?: string | undefined;
49
51
  caption?: string | null | undefined;
50
52
  stickyHeaderOffset?: string | undefined;
@@ -55,13 +57,13 @@ export interface TableProps<T extends SelectionItem> extends FocusSensorAddProps
55
57
  /**
56
58
  * Interactive table with selection and keyboard navigation support.
57
59
  */
58
- export declare class Table<T extends SelectionItem> extends PureComponent<TableProps<T>> {
60
+ export declare class Table<T extends object> extends PureComponent<TableProps<T>> {
59
61
  static defaultProps: {
60
62
  isItemSelectable: () => boolean;
61
63
  loading: boolean;
62
64
  onSort: () => void;
63
65
  onReorder: () => void;
64
- getItemKey: (item: SelectionItem) => string | number;
66
+ getItemKey: (item: object) => string | number;
65
67
  sortKey: string;
66
68
  sortOrder: boolean;
67
69
  draggable: boolean;
@@ -100,8 +102,8 @@ export declare class Table<T extends SelectionItem> extends PureComponent<TableP
100
102
  restoreFocusWithoutScroll: () => void;
101
103
  render(): React.JSX.Element;
102
104
  }
103
- export type TableAttrs<T extends SelectionItem> = DisableHoverProps<SelectionShortcutsProps<T, FocusSensorProps<TableProps<T>, HTMLTableRowElement, typeof Table>>>;
104
- export default class TableContainer<T extends SelectionItem> extends Component<TableAttrs<T>> {
105
+ export type TableAttrs<T extends object> = DisableHoverProps<SelectionShortcutsProps<T, FocusSensorProps<TableProps<T>, HTMLTableRowElement, typeof Table>>>;
106
+ export default class TableContainer<T extends object> extends Component<TableAttrs<T>> {
105
107
  Table: React.ComponentClass<DisableHoverProps<SelectionShortcutsProps<T, FocusSensorProps<TableProps<T>, HTMLTableRowElement, typeof Table>>>, any>;
106
108
  render(): React.JSX.Element;
107
109
  }
@@ -23,7 +23,14 @@ export class Table extends PureComponent {
23
23
  loading: false,
24
24
  onSort: () => { },
25
25
  onReorder: () => { },
26
- getItemKey: (item) => item.id,
26
+ getItemKey: (item) => {
27
+ // Default behavior stays backward compatible: use item's "id" if present
28
+ if ('id' in item) {
29
+ return item.id;
30
+ }
31
+ // If there's no id provided on item and no getKey supplied, fail fast with a clear message
32
+ throw new Error('Table: getItemKey is required when items have no "id" property');
33
+ },
27
34
  sortKey: 'id',
28
35
  sortOrder: true,
29
36
  draggable: false,
@@ -121,6 +128,7 @@ export class Table extends PureComponent {
121
128
  sortOrder,
122
129
  sticky: stickyHeader,
123
130
  topStickOffset: stickyHeaderOffset,
131
+ className: this.props.headerClassName,
124
132
  };
125
133
  const selectedSize = selection.getSelected().size;
126
134
  const allSelectedSize = selection.selectAll().getSelected().size;
@@ -130,7 +138,7 @@ export class Table extends PureComponent {
130
138
  const wrapperClasses = classNames({
131
139
  [style.tableWrapper]: true,
132
140
  [style.loading]: loading,
133
- });
141
+ }, this.props.wrapperClassName);
134
142
  const classes = classNames(this.props.className, {
135
143
  [style.table]: true,
136
144
  [style.wideFirstColumn]: this.props.wideFirstColumn,
@@ -157,7 +165,7 @@ export class Table extends PureComponent {
157
165
  return null;
158
166
  }
159
167
  const { ref, ...restProps } = props;
160
- const row = (<RowComponent innerRef={ref} level={getItemLevel(value)} item={value} showFocus={selection.isFocused(value)} autofocus={selection.isFocused(value)} focused={focused && selection.isFocused(value)} selectable={selectable && isItemSelectable(value)} selected={selectable && selection.isSelected(value)} onFocus={this.onRowFocus} onSelect={this.onRowSelect} onDoubleClick={onItemDoubleClick} onClick={onItemClick} collapsible={isItemCollapsible(value)} parentCollapsible={isParentCollapsible(value)} collapsed={isItemCollapsed(value)} onCollapse={onItemCollapse} onExpand={onItemExpand} showDisabledSelection={isDisabledSelectionVisible(value)} checkboxTooltip={getCheckboxTooltip(value)} className={classNames(getItemClassName(value), { [style.draggingRow]: isDragged })} metaColumnClassName={getMetaColumnClassName(value)} draggable={draggable} alwaysShowDragHandle={alwaysShowDragHandle} dragHandleTitle={dragHandleTitle} columns={columns} data-test={getItemDataTest(value)} {...restProps} key={restProps.key ?? getItemKey(value)}/>);
168
+ const row = (<RowComponent innerRef={ref} level={getItemLevel(value)} item={value} showFocus={selection.isFocused(value)} autofocus={selection.isFocused(value)} focused={focused && selection.isFocused(value)} selectable={selectable && isItemSelectable(value)} selected={selectable && selection.isSelected(value)} onFocus={this.onRowFocus} onSelect={this.onRowSelect} onDoubleClick={onItemDoubleClick} onClick={onItemClick} collapsible={isItemCollapsible(value)} parentCollapsible={isParentCollapsible(value)} collapsed={isItemCollapsed(value)} onCollapse={onItemCollapse} onExpand={onItemExpand} showDisabledSelection={isDisabledSelectionVisible(value)} checkboxTooltip={getCheckboxTooltip(value)} className={classNames(getItemClassName(value), { [style.draggingRow]: isDragged })} metaColumnClassName={getMetaColumnClassName(value)} draggable={draggable} alwaysShowDragHandle={alwaysShowDragHandle} dragHandleTitle={dragHandleTitle} columns={columns} data-test={getItemDataTest(value)} cellClassName={this.props.cellClassName} {...restProps} key={restProps.key ?? getItemKey(value)}/>);
161
169
  return isDragged ? (<table style={{ ...props.style }} className={style.draggingTable}>
162
170
  <tbody>{row}</tbody>
163
171
  </table>) : (row);
@@ -25,6 +25,7 @@ export interface TagProps {
25
25
  children?: ReactNode;
26
26
  className?: string | null | undefined;
27
27
  containerClassName?: string | null | undefined;
28
+ removeButtonClassName?: string | null | undefined;
28
29
  rgTagIcon?: string | IconType | null | undefined;
29
30
  icon?: string | undefined;
30
31
  avatar?: string | null | undefined;
@@ -90,7 +90,7 @@ export default class Tag extends PureComponent {
90
90
  }
91
91
  renderRemoveIcon() {
92
92
  if (!this.props.readOnly && this.props.onRemove) {
93
- return (<Button title='Remove' icon={closeIcon} data-test='ring-tag-remove' className={styles.remove} onClick={this.props.onRemove} style={{ '--ring-secondary-color': this.props.textColor }} height={ControlsHeight.M}/>);
93
+ return (<Button title='Remove' icon={closeIcon} data-test='ring-tag-remove' className={classNames(styles.remove, this.props.removeButtonClassName)} onClick={this.props.onRemove} style={{ '--ring-secondary-color': this.props.textColor }} height={ControlsHeight.M}/>);
94
94
  }
95
95
  return null;
96
96
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jetbrains/ring-ui",
3
- "version": "7.0.85",
3
+ "version": "7.0.86",
4
4
  "description": "JetBrains UI library",
5
5
  "author": {
6
6
  "name": "JetBrains"
@@ -106,11 +106,11 @@
106
106
  "@rollup/plugin-json": "^6.1.0",
107
107
  "@rollup/plugin-node-resolve": "^16.0.3",
108
108
  "@rollup/plugin-replace": "^6.0.3",
109
- "@storybook/addon-a11y": "10.1.10",
110
- "@storybook/addon-docs": "^10.1.10",
111
- "@storybook/addon-themes": "^10.1.10",
109
+ "@storybook/addon-a11y": "10.1.11",
110
+ "@storybook/addon-docs": "^10.1.11",
111
+ "@storybook/addon-themes": "^10.1.11",
112
112
  "@storybook/csf": "^0.1.13",
113
- "@storybook/react-webpack5": "10.1.10",
113
+ "@storybook/react-webpack5": "10.1.11",
114
114
  "@storybook/test-runner": "^0.24.2",
115
115
  "@testing-library/dom": "^10.4.1",
116
116
  "@testing-library/react": "^16.3.1",
@@ -122,10 +122,10 @@
122
122
  "@types/react-dom": "^19.2.3",
123
123
  "@types/webpack-env": "^1.18.8",
124
124
  "@vitejs/plugin-react": "^5.1.2",
125
- "@vitest/eslint-plugin": "^1.6.1",
125
+ "@vitest/eslint-plugin": "^1.6.5",
126
126
  "acorn": "^8.15.0",
127
127
  "babel-plugin-require-context-hook": "^1.0.0",
128
- "caniuse-lite": "^1.0.30001761",
128
+ "caniuse-lite": "^1.0.30001762",
129
129
  "chai-as-promised": "^8.0.2",
130
130
  "chai-dom": "^1.12.1",
131
131
  "cheerio": "^1.1.2",
@@ -142,11 +142,11 @@
142
142
  "eslint-plugin-prettier": "^5.5.4",
143
143
  "eslint-plugin-react": "^7.37.5",
144
144
  "eslint-plugin-react-hooks": "^7.0.1",
145
- "eslint-plugin-storybook": "^10.1.10",
145
+ "eslint-plugin-storybook": "^10.1.11",
146
146
  "eslint-plugin-unicorn": "^62.0.0",
147
147
  "events": "^3.3.0",
148
148
  "glob": "^13.0.0",
149
- "globals": "^16.5.0",
149
+ "globals": "^17.0.0",
150
150
  "html-webpack-plugin": "^5.6.5",
151
151
  "http-server": "^14.1.1",
152
152
  "husky": "^9.1.7",
@@ -167,14 +167,14 @@
167
167
  "rollup": "^4.54.0",
168
168
  "rollup-plugin-clear": "^2.0.7",
169
169
  "storage-mock": "^2.1.0",
170
- "storybook": "10.1.10",
170
+ "storybook": "10.1.11",
171
171
  "stylelint": "^16.26.1",
172
172
  "stylelint-config-sass-guidelines": "^12.1.0",
173
173
  "svg-inline-loader": "^0.8.2",
174
174
  "teamcity-service-messages": "^0.1.14",
175
175
  "terser-webpack-plugin": "^5.3.16",
176
176
  "typescript": "~5.9.3",
177
- "typescript-eslint": "^8.50.0",
177
+ "typescript-eslint": "^8.51.0",
178
178
  "vitest": "^4.0.16",
179
179
  "vitest-teamcity-reporter": "^0.4.1",
180
180
  "webpack": "^5.104.1",
@@ -223,7 +223,7 @@
223
223
  "element-resize-detector": "^1.2.4",
224
224
  "fastdom": "^1.0.12",
225
225
  "file-loader": "^6.2.0",
226
- "focus-trap": "^7.7.0",
226
+ "focus-trap": "^7.7.1",
227
227
  "highlight.js": "^10.7.2",
228
228
  "just-debounce-it": "^3.2.0",
229
229
  "memoize-one": "^6.0.0",
@@ -233,7 +233,7 @@
233
233
  "postcss-font-family-system-ui": "^5.0.0",
234
234
  "postcss-loader": "^8.2.0",
235
235
  "postcss-modules-values-replace": "^4.2.2",
236
- "postcss-preset-env": "^10.5.0",
236
+ "postcss-preset-env": "^10.6.0",
237
237
  "react-compiler-runtime": "^1.0.0",
238
238
  "react-movable": "^3.4.1",
239
239
  "react-virtualized": "^9.22.6",