@sonic-equipment/ui 212.0.0 → 213.0.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.
@@ -0,0 +1,9 @@
1
+ import { ReactNode } from 'react';
2
+ export declare function AuthenticatedContainer({ children, onUnauthenticated, views, }: {
3
+ children: React.ReactNode;
4
+ onUnauthenticated?: () => void;
5
+ views?: {
6
+ unauthenticated?: ReactNode;
7
+ undetermined?: ReactNode;
8
+ };
9
+ }): string | number | boolean | import("react/jsx-runtime").JSX.Element | Iterable<ReactNode> | null;
@@ -0,0 +1,21 @@
1
+ "use client";
2
+ import { jsx, Fragment } from 'react/jsx-runtime';
3
+ import { useRef, useEffect } from 'react';
4
+ import { useIsAuthenticated } from '../shared/api/storefront/hooks/authentication/use-is-authenticated.js';
5
+
6
+ function AuthenticatedContainer({ children, onUnauthenticated, views, }) {
7
+ const isAuthenticated = useIsAuthenticated();
8
+ const wasAuthenticated = useRef(Boolean(isAuthenticated));
9
+ useEffect(() => {
10
+ if (wasAuthenticated.current === true && isAuthenticated === false)
11
+ onUnauthenticated?.();
12
+ wasAuthenticated.current = isAuthenticated || false;
13
+ }, [isAuthenticated, onUnauthenticated]);
14
+ if (isAuthenticated === false)
15
+ return views?.unauthenticated ?? null;
16
+ if (isAuthenticated === undefined)
17
+ return views?.undetermined ?? null;
18
+ return jsx(Fragment, { children: children });
19
+ }
20
+
21
+ export { AuthenticatedContainer };
@@ -29,7 +29,7 @@ function AccordionItem({ _pseudo = 'none', allowCollapse = true, allowToggle = t
29
29
  }), disabled: isDisabled, id: id, onClick: () => {
30
30
  if (allowToggle && allowCollapse)
31
31
  toggle();
32
- }, tabIndex: allowToggle ? 0 : -1, type: "button", children: [title, jsx("span", { className: styles.icon, children: size === 'lg' ? (jsx(GlyphsChevronsBoldDownIcon, {})) : (jsx(GlyphsChevronsSlimDownIcon, {})) })] }) }), jsx("div", { "aria-hidden": !isOpen, "aria-labelledby": id, className: styles.panel, id: panelId, inert: isOpen ? undefined : 'true', role: "region", children: children })] }));
32
+ }, tabIndex: allowToggle ? 0 : -1, type: "button", children: [title, jsx("span", { className: styles.icon, children: size === 'lg' ? (jsx(GlyphsChevronsBoldDownIcon, {})) : (jsx(GlyphsChevronsSlimDownIcon, {})) })] }) }), jsx("div", { "aria-hidden": !isOpen, "aria-labelledby": id, className: styles.panel, id: panelId, inert: isOpen ? undefined : true, role: "region", children: children })] }));
33
33
  }
34
34
 
35
35
  export { AccordionItem };
@@ -23,7 +23,7 @@ function ShowAll({ children, hasTransparency = true, initialHeight = 0, initialI
23
23
  [styles['is-open']]: isReallyOpen,
24
24
  }), style: contentFits ? undefined : { '--initial-height': `${initialHeight}px` }, children: [jsx("div", { className: styles.panel, children: jsx("div", { className: clsx(styles.content, {
25
25
  [styles['has-transparency']]: !contentFits && hasTransparency,
26
- }), children: jsx("div", { ref: ref, "aria-hidden": !isReallyOpen, inert: isReallyOpen ? undefined : 'true', children: children }) }) }), !contentFits && (jsxs(Link, { className: styles.button, color: "secondary", onClick: () => {
26
+ }), children: jsx("div", { ref: ref, "aria-hidden": !isReallyOpen, inert: isReallyOpen ? undefined : true, children: children }) }) }), !contentFits && (jsxs(Link, { className: styles.button, color: "secondary", onClick: () => {
27
27
  if (isControlled)
28
28
  return onToggle(!isControlled);
29
29
  toggle();
package/dist/exports.d.ts CHANGED
@@ -26,6 +26,7 @@ export * from './algolia/use-algolia-insights';
26
26
  export * from './algolia/use-algolia-insights-provider-global-state';
27
27
  export * from './algolia/use-algolia-instant-search-state';
28
28
  export * from './algolia/use-algolia-search';
29
+ export * from './authentication/authenticated-container';
29
30
  export * from './background-overlay/background-overlay';
30
31
  export * from './background-overlay/background-overlay-manager';
31
32
  export * from './badges/badge/badge';
@@ -14,7 +14,7 @@ function ConnectedAccountButton({ className, 'data-test-selector': dataTestSelec
14
14
  const { href } = useLocation();
15
15
  return (jsx(IconButton, { className: className, "data-authenticated": isAuthenticated ? true : false, "data-test-selector": dataTestSelector, href: isAuthenticated
16
16
  ? paths.ACCOUNT
17
- : `${paths.SIGN_IN}${href ? `?returnUrl=${encodeURIComponent(href)}` : ''}`, onClick: onClick, children: jsx(AccountIcon, { "aria-label": isAuthenticated ? t('My Sonic') : t('Sign in or create account'), isAuthenticated: isAuthenticated }) }));
17
+ : `${paths.SIGN_IN}${href ? `?returnUrl=${encodeURIComponent(href)}` : ''}`, isDisabled: isAuthenticated === undefined, onClick: onClick, children: jsx(AccountIcon, { "aria-label": isAuthenticated ? t('My Sonic') : t('Sign in or create account'), isAuthenticated: isAuthenticated }) }));
18
18
  }
19
19
 
20
20
  export { ConnectedAccountButton };
package/dist/index.js CHANGED
@@ -31,6 +31,7 @@ export { useAlgoliaInsights } from './algolia/use-algolia-insights.js';
31
31
  export { useAlgoliaInsightsGlobalState } from './algolia/use-algolia-insights-provider-global-state.js';
32
32
  export { useAlgoliaInstantSearchState, useAlgoliaInstantSearchStateIndex, useAlgoliaInstantSearchStateOnline, useAlgoliaInstantSearchStateQueryId, useAlgoliaInstantSearchStateStatus } from './algolia/use-algolia-instant-search-state.js';
33
33
  export { useAlgoliaSearch } from './algolia/use-algolia-search.js';
34
+ export { AuthenticatedContainer } from './authentication/authenticated-container.js';
34
35
  export { BackgroundOverlay } from './background-overlay/background-overlay.js';
35
36
  export { BackgroundOverlayManager } from './background-overlay/background-overlay-manager.js';
36
37
  export { Badge } from './badges/badge/badge.js';
@@ -7,7 +7,7 @@ import { useFormattedMessage } from '../../intl/use-formatted-message.js';
7
7
 
8
8
  function AccountIcon({ 'aria-label': ariaLabel, isAuthenticated, }) {
9
9
  const t = useFormattedMessage();
10
- return (jsx(IconWithBadge, { "aria-label": ariaLabel, badge: isAuthenticated ? (jsx(Badge, { "aria-label": `(${t('Signed in')})`, variant: "green" })) : (jsx(Badge, { "aria-label": `(${t('Signed out')})`, variant: "red" })), icon: jsx(SolidLoginIcon, { role: "presentation" }) }));
10
+ return (jsx(IconWithBadge, { "aria-label": ariaLabel, badge: isAuthenticated === undefined ? null : isAuthenticated ? (jsx(Badge, { "aria-label": `(${t('Signed in')})`, variant: "green" })) : (jsx(Badge, { "aria-label": `(${t('Signed out')})`, variant: "red" })), icon: jsx(SolidLoginIcon, { role: "presentation" }) }));
11
11
  }
12
12
 
13
13
  export { AccountIcon };
@@ -66,9 +66,10 @@ function OrderHistory() {
66
66
  ...options,
67
67
  [status]: t(`orderStatus.${status}`),
68
68
  }), { any: t('orderStatus.Any') });
69
- return (jsx(Fragment, { children: jsxs("section", { ref: orderHistoryRef, className: styles['order-history'], children: [jsx("div", { className: styles['search'], children: jsx(SearchField, { isDebounced: true, label: t('Search orders'), onChange: searchOrders, placeholder: t('Search orders'), showLabel: false, variant: "outline" }) }), jsx("div", { className: styles['filter'], children: jsx(SelectField, { label: t('Filter order status'), onChange: filterOrders, options: filterOptions, selectedOption: status, showLabel: false }) }), jsx("div", { "aria-label": t('Results'), "aria-live": "polite", className: styles['results'], children: jsx(DynamicLoadingOverlay, { isLoading: isFetching, children: orders && orders.length > 0 ? (jsx(DataTable, { actions: [
69
+ return (jsx(Fragment, { children: jsxs("section", { ref: orderHistoryRef, className: styles['order-history'], "data-test-selector": "order-history", children: [jsx("div", { className: styles['search'], children: jsx(SearchField, { isDebounced: true, "data-test-selector": "order-history-search", label: t('Search orders'), onChange: searchOrders, placeholder: t('Search orders'), showLabel: false, variant: "outline" }) }), jsx("div", { className: styles['filter'], children: jsx(SelectField, { "data-test-selector": "order-history-filter", label: t('Filter order status'), onChange: filterOrders, options: filterOptions, selectedOption: status, showLabel: false }) }), jsx("div", { "aria-label": t('Results'), "aria-live": "polite", className: styles['results'], children: jsx(DynamicLoadingOverlay, { isLoading: isFetching, children: orders && orders.length > 0 ? (jsx(DataTable, { actions: [
70
70
  {
71
- isPrimary: true,
71
+ 'data-test-selector': 'order-history-reorder',
72
+ isPrimary: false,
72
73
  key: 'reorder',
73
74
  label: 'Reorder',
74
75
  onAction: order => window?.alert(`Reorder ${order.id}`),
@@ -80,13 +81,13 @@ function OrderHistory() {
80
81
  },
81
82
  key: 'webOrderNumber',
82
83
  props: {
83
- cssColumn: 'minmax(10ch, min-content)',
84
+ cssColumn: 'minmax(15ch, min-content)',
84
85
  nowrap: true,
85
86
  sticky: true,
86
87
  },
87
88
  sort: { direction: 'DESC', isEnabled: true },
88
89
  value: {
89
- render: order => (jsx(Link, { hasUnderline: true, color: "secondary", href: `/my-sonic/order-history/${order.id}`, onClick: () => window?.alert(`open ${order.id}`), children: order.webOrderNumber })),
90
+ render: order => (jsx(Link, { hasUnderline: true, color: "secondary", "data-test-selector": "order-history-order-link", href: `/my-sonic/order-history/${order.id}`, onClick: () => window?.alert(`open ${order.id}`), children: order.webOrderNumber })),
90
91
  },
91
92
  },
92
93
  {
@@ -163,7 +164,9 @@ function OrderHistory() {
163
164
  name: key,
164
165
  order: direction,
165
166
  });
166
- } })) : (!isFetching && (jsx("p", { className: styles['no-results'], children: t('No orders found.') }))) }) }), orders && orders.length > 0 && (jsx("nav", { className: styles['paging'], children: jsx(Pagination, { currentPage: pagination?.currentPage || 1, onChange: page => setPage(page), totalPages: pagination?.numberOfPages || 1 }) })), jsx("div", { className: styles['sticky-bar'], role: "presentation" })] }) }));
167
+ }, row: {
168
+ 'data-test-selector': (order, _) => `tr-${order.id}`,
169
+ } })) : (!isFetching && (jsx("p", { className: styles['no-results'], children: t('No orders found.') }))) }) }), orders && orders.length > 0 && (jsx("nav", { className: styles['paging'], children: jsx(Pagination, { currentPage: pagination?.currentPage || 1, "data-test-selector": "order-history-pagination", onChange: page => setPage(page), totalPages: pagination?.numberOfPages || 1 }) })), jsx("div", { className: styles['sticky-bar'], role: "presentation" })] }) }));
167
170
  }
168
171
 
169
172
  export { OrderHistory };
@@ -49,11 +49,12 @@ function ConnectedAddressBookWidget() {
49
49
  }
50
50
  };
51
51
  return (jsx(TableCard, { stickyHeader: true, actions: [
52
- jsx(SearchField, { isDebounced: true, className: styles['search-field'], label: t('Search'), onChange: searchAddresses, placeholder: t('Search'), showLabel: false, size: "md", variant: "outline" }, "searchAddresses"),
53
- jsx(Button, { color: "secondary", href: `${paths.ACCOUNT_EDIT_BILL_TO_ADDRESS}/current${paths.ACCOUNT_EDIT_SHIP_TO_ADDRESS}/new?returnUrl=${encodeURIComponent(`${href}#${addressBookId}`)}`, size: "sm", variant: "outline", children: t('Add address') }, "addAddress"),
54
- ], className: styles['address-book-widget'], "data-test-selector": "address-book-widget", id: addressBookId, isLoading: isFetching, paging: addresses &&
52
+ jsx(SearchField, { isDebounced: true, className: styles['search-field'], "data-test-selector": "address-book-search", label: t('Search'), onChange: searchAddresses, placeholder: t('Search'), showLabel: false, size: "md", variant: "outline" }, "searchAddresses"),
53
+ jsx(Button, { color: "secondary", "data-test-selector": "address-book-add-address", href: `${paths.ACCOUNT_EDIT_BILL_TO_ADDRESS}/current${paths.ACCOUNT_EDIT_SHIP_TO_ADDRESS}/new?returnUrl=${encodeURIComponent(`${href}#${addressBookId}`)}`, size: "sm", variant: "outline", children: t('Add address') }, "addAddress"),
54
+ ], className: styles['address-book-widget'], "data-test-selector": "address-book", id: addressBookId, isLoading: isFetching, paging: addresses &&
55
55
  addresses.length > 0 && (jsx("nav", { className: styles['paging'], children: jsx(Pagination, { currentPage: pagination?.currentPage || 1, onChange: page => setPage(page), totalPages: pagination?.numberOfPages || 1 }) })), results: addresses ? addresses.length : 0, showError: error, title: "Address book", children: jsx("div", { ref: addressBookRef, "aria-label": "Results", "aria-live": "polite", className: styles['results'], children: jsx(DynamicLoadingOverlay, { isLoading: isFetching, children: jsx(DataTable, { actions: [
56
56
  {
57
+ 'data-test-selector': 'edit-address',
57
58
  href: address => `${paths.ACCOUNT_EDIT_BILL_TO_ADDRESS}/current${paths.ACCOUNT_EDIT_SHIP_TO_ADDRESS}/${address.id}?returnUrl=${encodeURIComponent(`${href}#${addressBookId}`)}`,
58
59
  key: 'edit',
59
60
  label: 'Edit',
@@ -71,7 +72,7 @@ function ConnectedAddressBookWidget() {
71
72
  },
72
73
  value: {
73
74
  render: address => {
74
- return (address.isDefault && (jsx(SolidRatingIcon, { "aria-label": `(${t('addressProperty.Default')})`, className: styles['default-icon'] })));
75
+ return (address.isDefault && (jsx(SolidRatingIcon, { "aria-label": `(${t('addressProperty.Default')})`, className: styles['default-icon'], "data-test-selector": "default-address" })));
75
76
  },
76
77
  },
77
78
  },
@@ -132,8 +133,8 @@ function ConnectedAddressBookWidget() {
132
133
  header: { label: 'addressProperty.Country' },
133
134
  key: 'country.name',
134
135
  props: {
135
- cssColumn: 'minmax(12ch, min-content)',
136
- nowrap: true,
136
+ cssColumn: 'minmax(15ch, min-content)',
137
+ truncated: 2,
137
138
  },
138
139
  sort: {
139
140
  isEnabled: true,
@@ -162,6 +163,9 @@ function ConnectedAddressBookWidget() {
162
163
  name: key,
163
164
  order: direction,
164
165
  });
166
+ }, row: {
167
+ 'data-test-selector': (address, _) => `address-${address.id}`,
168
+ selected: (address, _) => address.isDefault,
165
169
  } }) }) }) }));
166
170
  }
167
171
 
@@ -14,7 +14,7 @@ function Sidebar({ children, className }) {
14
14
  [styles['is-closed']]: !isOpen,
15
15
  [styles['is-docked']]: isDocked,
16
16
  [styles['is-not-docked']]: !isDocked,
17
- }, className), inert: isOpen ? undefined : 'true', children: [jsx(IconButton, { className: styles.close, color: "secondary", onClick: toggle, children: jsx(StrokeCloseboxIcon, {}) }), jsx("div", { className: styles['content'], children: children })] }));
17
+ }, className), inert: isOpen ? undefined : true, children: [jsx(IconButton, { className: styles.close, color: "secondary", onClick: toggle, children: jsx(StrokeCloseboxIcon, {}) }), jsx("div", { className: styles['content'], children: children })] }));
18
18
  }
19
19
 
20
20
  export { Sidebar };
package/dist/styles.css CHANGED
@@ -454,6 +454,7 @@ html {
454
454
 
455
455
  .accordion-module-9WvAH .accordion-module-lf9d-.accordion-module-kYmoe .accordion-module-KZjMo {
456
456
  block-size: 0;
457
+ visibility: hidden;
457
458
  }
458
459
 
459
460
  /* open state */
@@ -485,6 +486,7 @@ html {
485
486
  background-color: var(--color-brand-light-gray);
486
487
  font-size: var(--font-size-16);
487
488
  font-weight: var(--font-weight-normal);
489
+ transition: border-radius 0s var(--transition-duration-short);
488
490
  }
489
491
 
490
492
  .accordion-module-9WvAH.accordion-module-SAbiG .accordion-module-lf9d- .accordion-module--Rwpb svg {
@@ -499,19 +501,24 @@ html {
499
501
  }
500
502
 
501
503
  .accordion-module-9WvAH.accordion-module-SAbiG .accordion-module-lf9d- .accordion-module-KZjMo {
502
- padding-inline: 1rem;
504
+ display: grid;
505
+ padding: 0;
506
+ border: 1px solid var(--color-brand-medium-gray);
507
+ margin: 0;
508
+ border-bottom-left-radius: var(--border-radius-8);
509
+ border-bottom-right-radius: var(--border-radius-8);
510
+ grid-template-rows: var(--space-16) auto var(--space-16);
511
+ padding-inline: var(--space-16);
503
512
  }
504
513
 
514
+ .accordion-module-9WvAH.accordion-module-SAbiG .accordion-module-lf9d- .accordion-module-KZjMo::before {
515
+ content: '';
516
+ }
517
+
505
518
  .accordion-module-9WvAH.accordion-module-SAbiG .accordion-module-lf9d-.accordion-module-W0F1z .accordion-module--Rwpb {
506
519
  border-bottom-left-radius: 0;
507
520
  border-bottom-right-radius: 0;
508
- }
509
-
510
- .accordion-module-9WvAH.accordion-module-SAbiG .accordion-module-lf9d-.accordion-module-W0F1z .accordion-module-KZjMo {
511
- border: 1px solid var(--color-brand-medium-gray);
512
- border-bottom-left-radius: var(--border-radius-8);
513
- border-bottom-right-radius: var(--border-radius-8);
514
- padding-block: 1rem;
521
+ transition: border-radius 0s 0s;
515
522
  }
516
523
 
517
524
  .heading-module-pMC65 {
@@ -1766,7 +1773,7 @@ html {
1766
1773
  'label .'
1767
1774
  'field info'
1768
1775
  'error .';
1769
- grid-template-columns: auto;
1776
+ grid-template-columns: 1fr auto;
1770
1777
  grid-template-rows: repeat(3, min-content);
1771
1778
  }
1772
1779
 
@@ -8447,6 +8454,7 @@ button.swiper-pagination-bullet {
8447
8454
  background-color: var(--bgcolor-row-selected);
8448
8455
  content: '';
8449
8456
  grid-column: 1 / -1;
8457
+ margin-block-end: 1px;
8450
8458
  opacity: 1;
8451
8459
  pointer-events: none;
8452
8460
  }
@@ -2,6 +2,7 @@ import { FunctionComponent, ReactNode } from 'react';
2
2
  import { TranslationId } from '../intl/translation-id';
3
3
  import { SortDirection, SortHanlder, SortOptions, TableColumnProperties } from './elements/types';
4
4
  export interface Column<T extends object> {
5
+ 'data-test-selector'?: (column: Column<T>, item: T) => string;
5
6
  header: {
6
7
  isHidden?: boolean;
7
8
  isNarrow?: boolean;
@@ -25,6 +26,7 @@ export interface Column<T extends object> {
25
26
  };
26
27
  }
27
28
  export interface Action<T extends object> {
29
+ 'data-test-selector'?: string;
28
30
  href?: (item: T) => string;
29
31
  icon?: ReactNode;
30
32
  isEnabled?: (item: T) => boolean;
@@ -38,6 +40,11 @@ export interface DataTableProps<T extends object> {
38
40
  columns?: Column<T>[];
39
41
  data: T[];
40
42
  onSort?: SortHanlder;
43
+ row?: {
44
+ 'data-test-selector'?: (item: T, index: number) => string;
45
+ onClick?: (item: T) => void;
46
+ selected?: (item: T, index: number) => boolean;
47
+ };
41
48
  }
42
49
  export declare function SortWrapper<T extends object>({ children, column, direction, onSort, }: {
43
50
  children: ReactNode;
@@ -45,4 +52,4 @@ export declare function SortWrapper<T extends object>({ children, column, direct
45
52
  direction?: SortDirection;
46
53
  onSort: SortHanlder;
47
54
  }): import("react/jsx-runtime").JSX.Element;
48
- export declare function DataTable<T extends object>({ actions, columns: _columns, data, onSort: _onSort, }: DataTableProps<T>): import("react/jsx-runtime").JSX.Element | null;
55
+ export declare function DataTable<T extends object>({ actions, columns: _columns, data, onSort: _onSort, row: _row, }: DataTableProps<T>): import("react/jsx-runtime").JSX.Element | null;
@@ -19,7 +19,7 @@ function SortWrapper({ children, column, direction, onSort, }) {
19
19
  return jsx(Fragment, { children: children });
20
20
  return (jsx(TableSortButton, { columnName: column.header.label, direction: direction, onSort: onSort, sortKey: column.sort.columnName || column.key, children: children }));
21
21
  }
22
- function DataTable({ actions, columns: _columns, data, onSort: _onSort, }) {
22
+ function DataTable({ actions, columns: _columns, data, onSort: _onSort, row: _row, }) {
23
23
  const isXl = useIsBreakpoint('lg');
24
24
  const t = useFormattedMessage();
25
25
  const [sortInfo, setSortInfo] = useState(() => {
@@ -70,20 +70,20 @@ function DataTable({ actions, columns: _columns, data, onSort: _onSort, }) {
70
70
  ? sortInfo?.direction
71
71
  : 'NONE', onSort: onSort, children: 'render' in column.header && column.header.render
72
72
  ? column.header.render(column, data)
73
- : t(column.header.label) }))) }, column.key))) }), children: data.map((item, index) => (
73
+ : t(column.header.label) }))) }, column.key))) }), children: data.map((item, index) => (jsx(TR
74
74
  // eslint-disable-next-line @eslint-react/no-array-index-key
75
- jsx(TR, { children: columns.map(column => {
75
+ , { "data-test-selector": _row?.['data-test-selector']?.(item, index), onClick: _row?.onClick ? () => _row.onClick?.(item) : undefined, selected: _row?.selected ? _row.selected(item, index) : false, children: columns.map(column => {
76
76
  const actionKey = 'action' in column.value ? column.value.action : undefined;
77
77
  const action = actionKey
78
78
  ? actions?.find(action => action.key === actionKey)
79
79
  : undefined;
80
80
  if (actionKey && !action)
81
81
  throw new Error(`Action "${actionKey}" not found`);
82
- return 'render' in column.value ? (jsx(TD, { children: column.value.render(item, column, data) }, column.key)) : 'propertyName' in column.value ? (jsx(TD, { children: String(item[column.value.propertyName]) }, column.key)) : 'action' in column.value ? (jsx(TD, { children: action && (jsx(Link, { hasUnderline: true, color: "secondary", href: action.href?.(item), isDisabled: action.isEnabled ? !action.isEnabled(item) : false, onClick: () => action.onAction?.(item), children: t(action.label) })) }, column.key)) : null;
82
+ return 'render' in column.value ? (jsx(TD, { "data-key": column.key, children: column.value.render(item, column, data) }, column.key)) : 'propertyName' in column.value ? (jsx(TD, { "data-key": column.key, children: String(item[column.value.propertyName]) }, column.key)) : 'action' in column.value ? (jsx(TD, { "data-key": column.key, children: action && (jsx(Link, { hasUnderline: true, color: "secondary", "data-test-selector": action['data-test-selector'], href: action.href?.(item), isDisabled: action.isEnabled ? !action.isEnabled(item) : false, onClick: () => action.onAction?.(item), children: t(action.label) })) }, column.key)) : null;
83
83
  }) }, index))) }) }));
84
84
  return (jsx("div", { className: styles['data-card-list'], children: data.map((item, index) => (jsx(DataCard
85
85
  // eslint-disable-next-line @eslint-react/no-array-index-key
86
- , { actions: actions?.map(action => (jsx(Button, { color: action.isPrimary ? 'primary' : 'secondary', href: action.href?.(item), isDisabled: action.isEnabled ? !action.isEnabled(item) : false, onClick: () => action.onAction?.(item), size: "sm", variant: action.isPrimary ? undefined : 'outline', children: t(action.label) }, action.key))), data: columns
86
+ , { actions: actions?.map(action => (jsx(Button, { color: action.isPrimary ? 'primary' : 'secondary', "data-test-selector": action['data-test-selector'], href: action.href?.(item), isDisabled: action.isEnabled ? !action.isEnabled(item) : false, onClick: () => action.onAction?.(item), size: "sm", variant: action.isPrimary ? undefined : 'outline', children: t(action.label) }, action.key))), data: columns
87
87
  .map(column => 'render' in column.value
88
88
  ? {
89
89
  key: column.key,
@@ -97,7 +97,7 @@ function DataTable({ actions, columns: _columns, data, onSort: _onSort, }) {
97
97
  value: item[column.value.propertyName],
98
98
  }
99
99
  : null)
100
- .filter(Boolean) }, index))) }));
100
+ .filter(Boolean), "data-test-selector": _row?.['data-test-selector']?.(item, index) }, index))) }));
101
101
  }
102
102
 
103
103
  export { DataTable, SortWrapper };
@@ -13,7 +13,7 @@ function TableSortButton({ align = 'start', children, columnName, direction = 'N
13
13
  ${t(columnName)}
14
14
  ${isActive && `(${t('active')}, ${t(`sort.${direction}`)})`}
15
15
  `;
16
- return (jsxs("button", { "aria-label": ariaLabel, className: clsx(styles['table-sort-button'], isActive && styles['active'], styles[direction.toLowerCase()], styles[align]), onClick: () => onSort(sortKey, direction), type: "button", children: [jsx("span", { className: styles['label'], children: children }), isActive && (jsx(GlyphsChevronsBoldUpIcon, { "aria-label": t(direction), className: styles['icon'] }))] }));
16
+ return (jsxs("button", { "aria-label": ariaLabel, className: clsx(styles['table-sort-button'], isActive && styles['active'], styles[direction.toLowerCase()], styles[align]), "data-sort-direction": direction, onClick: () => onSort(sortKey, direction), type: "button", children: [jsx("span", { className: styles['label'], children: children }), isActive && (jsx(GlyphsChevronsBoldUpIcon, { "aria-label": t(direction), className: styles['icon'] }))] }));
17
17
  }
18
18
 
19
19
  export { TableSortButton };
@@ -2,5 +2,6 @@ import { TableColumnProperties } from './types';
2
2
  export interface TDProps extends TableColumnProperties {
3
3
  children: React.ReactNode;
4
4
  className?: string;
5
+ 'data-key'?: string;
5
6
  }
6
7
  export declare function TD(props: TDProps): import("react/jsx-runtime").JSX.Element;
@@ -7,7 +7,7 @@ import styles from './table.module.css.js';
7
7
 
8
8
  function TD(props) {
9
9
  const { align = 'start', children, className, colSpan, isNarrow, nowrap, rowSpan, sticky, truncated, } = useTD(props);
10
- return (jsx("td", { className: clsx(styles['td'], styles[align], nowrap && styles['nowrap'], sticky && styles['sticky'], isNarrow && styles['narrow'], className), colSpan: colSpan, rowSpan: rowSpan, children: truncated ? (jsx(Truncated, { lines: typeof truncated === 'number' ? truncated : undefined, children: children })) : (children) }));
10
+ return (jsx("td", { className: clsx(styles['td'], styles[align], nowrap && styles['nowrap'], sticky && styles['sticky'], isNarrow && styles['narrow'], className), colSpan: colSpan, "data-key": props['data-key'], rowSpan: rowSpan, children: truncated ? (jsx(Truncated, { lines: typeof truncated === 'number' ? truncated : undefined, children: children })) : (children) }));
11
11
  }
12
12
 
13
13
  export { TD };
@@ -1,2 +1,2 @@
1
1
  import { TRProps } from './types';
2
- export declare function TR({ children, className, onClick, selected }: TRProps): import("react/jsx-runtime").JSX.Element;
2
+ export declare function TR({ children, className, 'data-test-selector': dataTestSelector, onClick, selected, }: TRProps): import("react/jsx-runtime").JSX.Element;
@@ -4,8 +4,8 @@ import clsx from 'clsx';
4
4
  import { TableRowProvider } from './table-row-provider.js';
5
5
  import styles from './table.module.css.js';
6
6
 
7
- function TR({ children, className, onClick, selected }) {
8
- return (jsx(TableRowProvider, { children: jsx("tr", { "aria-selected": selected, className: clsx(styles['tr'], selected && styles['selected'], Boolean(onClick) && styles['clickable'], className), onClick: onClick, children: children }) }));
7
+ function TR({ children, className, 'data-test-selector': dataTestSelector, onClick, selected, }) {
8
+ return (jsx(TableRowProvider, { children: jsx("tr", { "aria-selected": selected, className: clsx(styles['tr'], selected && styles['selected'], Boolean(onClick) && styles['clickable'], className), "data-test-selector": dataTestSelector, onClick: onClick, children: children }) }));
9
9
  }
10
10
 
11
11
  export { TR };
@@ -32,6 +32,7 @@ export interface THProps extends TableColumnProperties {
32
32
  export interface TRProps {
33
33
  children: ReactNode;
34
34
  className?: string;
35
+ 'data-test-selector'?: string;
35
36
  onClick?: (event: React.MouseEvent<HTMLTableRowElement>) => void;
36
37
  selected?: boolean;
37
38
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sonic-equipment/ui",
3
- "version": "212.0.0",
3
+ "version": "213.0.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "engines": {
@@ -15,10 +15,7 @@
15
15
  "default": "./dist/index.js"
16
16
  }
17
17
  },
18
- "files": [
19
- "dist",
20
- "README.md"
21
- ],
18
+ "files": ["dist", "README.md"],
22
19
  "main": "dist/index.js",
23
20
  "types": "./dist/index.d.ts",
24
21
  "scripts": {