mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-19 16:27:37 +00:00
Canonicalize grouped table row presentation
This commit is contained in:
parent
a15f5ad51b
commit
e4514dfd8b
17 changed files with 89 additions and 42 deletions
|
|
@ -507,7 +507,7 @@ work extends shared components instead of creating new local variants.
|
|||
reset action stays as one compact header-level `Clear` control with an
|
||||
accessible `Clear selection` label, not a second page-level scope strip,
|
||||
search-row accessory, or filter-bar badge.
|
||||
13. Keep summary-linked table row emphasis on the shared primitive contract. Workloads, infrastructure, and storage rows that mirror the active summary entity must expose that state through `data-summary-row-active` and let the shared presentation in `frontend-modern/src/index.css` render the row emphasis, rather than carrying page-local sky or blue fill classes inside each row renderer. Group-scoped preview and pin must use that same shared presentation boundary: child rows that belong to a hovered or pinned summary group should expose `data-summary-group-member-active="preview|pinned"` so the block-level emphasis stays subtle, consistent, and reversible instead of each table inventing its own outline, badge, or full-strength fill treatment. Static grouped row headers on workloads, infrastructure, storage, recovery, and future grouped tables must use `frontend-modern/src/components/shared/groupedTableRowPresentation.ts` plus the `.grouped-table-row` CSS contract in `frontend-modern/src/index.css`, rather than rebuilding local `bg-surface-alt` variants with subtly different light/dark behavior or page-local left-accent markers. Storage-backed reusable row presenters under `frontend-modern/src/features/storageBackups/` must also keep row height and alert accents on class/data-attribute presentation instead of runtime inline style maps, so the shared table contract stays CSP-safe on both steady-state and alert-highlighted routes.
|
||||
13. Keep summary-linked table row emphasis on the shared primitive contract. Workloads, infrastructure, and storage rows that mirror the active summary entity must expose that state through `data-summary-row-active` and let the shared presentation in `frontend-modern/src/index.css` render the row emphasis, rather than carrying page-local sky or blue fill classes inside each row renderer. Group-scoped preview and pin must use that same shared presentation boundary: child rows that belong to a hovered or pinned summary group should expose `data-summary-group-member-active="preview|pinned"` so the block-level emphasis stays subtle, consistent, and reversible instead of each table inventing its own outline, badge, or full-strength fill treatment. Static grouped row headers on workloads, infrastructure, storage, recovery, and future grouped tables must use `frontend-modern/src/components/shared/groupedTableRowPresentation.ts` plus the `.grouped-table-row` CSS contract in `frontend-modern/src/index.css`, rather than rebuilding local `bg-surface-alt` variants with subtly different light/dark behavior or page-local left-accent markers. That shared grouped-table primitive owns the subgroup cell padding, typography, small metadata, and badge treatment as well as the row background token, so a future adjustment to the subgroup visual language changes every grouped product table from one owner. Storage-backed reusable row presenters under `frontend-modern/src/features/storageBackups/` must also keep row height and alert accents on class/data-attribute presentation instead of runtime inline style maps, so the shared table contract stays CSP-safe on both steady-state and alert-highlighted routes.
|
||||
14. Keep retained-value data loading honest at the ownership boundary. Helpers
|
||||
that prevent a feature surface from falling through the app-level Suspense
|
||||
boundary during in-flight refresh should stay feature-local until multiple
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Show } from 'solid-js';
|
||||
|
||||
import { GROUPED_TABLE_ROW_BADGE_CLASS } from '@/components/shared/groupedTableRowPresentation';
|
||||
import type { GroupHeaderMeta } from '@/features/alerts/thresholds/tableTypes';
|
||||
|
||||
interface AlertResourceGroupHeaderProps {
|
||||
|
|
@ -13,12 +14,12 @@ export function AlertResourceGroupHeader(props: AlertResourceGroupHeaderProps) {
|
|||
return (
|
||||
<Show
|
||||
when={props.meta?.type === 'agent'}
|
||||
fallback={<span class="text-xs font-medium text-muted">{groupLabel()}</span>}
|
||||
fallback={<span>{groupLabel()}</span>}
|
||||
>
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<Show
|
||||
when={props.meta?.host}
|
||||
fallback={<span class="text-sm font-medium text-base-content">{groupLabel()}</span>}
|
||||
fallback={<span>{groupLabel()}</span>}
|
||||
>
|
||||
{(host) => (
|
||||
<a
|
||||
|
|
@ -26,7 +27,7 @@ export function AlertResourceGroupHeader(props: AlertResourceGroupHeaderProps) {
|
|||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
class="text-sm font-medium text-base-content transition-colors duration-150 hover:text-sky-600 dark:hover:text-sky-400"
|
||||
class="text-base-content transition-colors duration-150 hover:text-sky-600 dark:hover:text-sky-400"
|
||||
title={`Open ${groupLabel()} web interface`}
|
||||
>
|
||||
{groupLabel()}
|
||||
|
|
@ -34,7 +35,7 @@ export function AlertResourceGroupHeader(props: AlertResourceGroupHeaderProps) {
|
|||
)}
|
||||
</Show>
|
||||
<Show when={props.meta?.clusterName}>
|
||||
<span class="rounded px-2 py-0.5 text-[10px] font-medium whitespace-nowrap bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-300">
|
||||
<span class={GROUPED_TABLE_ROW_BADGE_CLASS}>
|
||||
{props.meta?.clusterName}
|
||||
</span>
|
||||
</Show>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,10 @@ import {
|
|||
} from '@/components/shared/Table';
|
||||
import { SectionHeader } from '@/components/shared/SectionHeader';
|
||||
import { HelpIcon } from '@/components/shared/HelpIcon';
|
||||
import { getGroupedTableRowClass } from '@/components/shared/groupedTableRowPresentation';
|
||||
import {
|
||||
getGroupedTableRowCellClass,
|
||||
getGroupedTableRowClass,
|
||||
} from '@/components/shared/groupedTableRowPresentation';
|
||||
import { AlertResourceTableRow } from './AlertResourceTableRow';
|
||||
import { AlertResourceGroupHeader } from './AlertResourceGroupHeader';
|
||||
import {
|
||||
|
|
@ -445,7 +448,7 @@ export function AlertResourceTableDesktop(props: AlertResourceTableDesktopProps)
|
|||
<TableRow class={getGroupedTableRowClass()}>
|
||||
<TableCell
|
||||
colspan={totalColumnCount()}
|
||||
class="p-1 px-2 text-xs font-medium text-muted"
|
||||
class={getGroupedTableRowCellClass()}
|
||||
>
|
||||
<AlertResourceGroupHeader
|
||||
groupKey={nodeName}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
import { createMemo, Index, Show } from 'solid-js';
|
||||
|
||||
import { ComponentErrorBoundary } from '@/components/ErrorBoundary';
|
||||
import { getInteractiveGroupedTableRowClass } from '@/components/shared/groupedTableRowPresentation';
|
||||
import {
|
||||
GROUPED_TABLE_ROW_BADGE_CLASS,
|
||||
getGroupedTableRowCellClass,
|
||||
getInteractiveGroupedTableRowClass,
|
||||
} from '@/components/shared/groupedTableRowPresentation';
|
||||
import { NodeGroupHeader } from '@/components/shared/NodeGroupHeader';
|
||||
import { createSummaryInteractiveRowPreviewHandlers } from '@/components/shared/summaryInteractionA11y';
|
||||
import { buildSummaryDisclosureControlsId } from '@/components/shared/summaryInteractionA11y';
|
||||
|
|
@ -119,7 +123,7 @@ export function WorkloadPanel(props: WorkloadPanelProps) {
|
|||
>
|
||||
<TableCell
|
||||
colspan={props.totalColumns()}
|
||||
class="py-0.5 pr-1.5 sm:pr-2 pl-2 sm:pl-3 text-[12px] sm:text-sm font-semibold text-base-content"
|
||||
class={getGroupedTableRowCellClass()}
|
||||
>
|
||||
{(() => {
|
||||
const label = props.getGroupLabel(groupKey(), fullGroupGuests());
|
||||
|
|
@ -127,7 +131,7 @@ export function WorkloadPanel(props: WorkloadPanelProps) {
|
|||
<div class="flex items-center gap-3">
|
||||
<span>{label.name}</span>
|
||||
<Show when={label.type}>
|
||||
<span class="inline-flex items-center rounded px-2 py-0.5 text-[10px] font-medium whitespace-nowrap bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-300">
|
||||
<span class={GROUPED_TABLE_ROW_BADGE_CLASS}>
|
||||
{label.type}
|
||||
</span>
|
||||
</Show>
|
||||
|
|
|
|||
|
|
@ -775,6 +775,7 @@ describe('Dashboard performance contract', () => {
|
|||
expect(workloadPanelSource).toContain('data-summary-group-id');
|
||||
expect(workloadPanelSource).toContain('setHoveredWorkloadGroupScope');
|
||||
expect(workloadPanelSource).toContain('getInteractiveGroupedTableRowClass');
|
||||
expect(workloadPanelSource).toContain('getGroupedTableRowCellClass');
|
||||
expect(workloadPanelSource).not.toContain('cursor-pointer bg-surface-alt');
|
||||
expect(dashboardWorkloadTableSource).not.toContain(
|
||||
'createMemo(() => getCanonicalWorkloadId(guest()))',
|
||||
|
|
@ -985,6 +986,7 @@ describe('Dashboard performance contract', () => {
|
|||
expect(workloadPanelSource).toContain('createSummaryInteractiveRowPreviewHandlers');
|
||||
expect(workloadPanelSource).toContain('resolveSummaryGroupMemberInteractionState');
|
||||
expect(workloadPanelSource).toContain('getInteractiveGroupedTableRowClass');
|
||||
expect(workloadPanelSource).toContain('getGroupedTableRowCellClass');
|
||||
expect(workloadPanelSource).not.toContain('style={{');
|
||||
expect(workloadPanelSource).not.toContain('style={');
|
||||
expect(workloadPanelSource).not.toContain('kind="scope"');
|
||||
|
|
|
|||
|
|
@ -3,7 +3,12 @@ import type { Component } from 'solid-js';
|
|||
import type { Disk } from '@/types/api';
|
||||
import { formatBytes, formatSpeed, formatUptime, normalizeDiskArray } from '@/utils/format';
|
||||
import { formatTemperature } from '@/utils/temperature';
|
||||
import { getInteractiveGroupedTableRowClass } from '@/components/shared/groupedTableRowPresentation';
|
||||
import {
|
||||
GROUPED_TABLE_ROW_BADGE_CLASS,
|
||||
GROUPED_TABLE_ROW_META_CLASS,
|
||||
getGroupedTableRowCellClass,
|
||||
getInteractiveGroupedTableRowClass,
|
||||
} from '@/components/shared/groupedTableRowPresentation';
|
||||
import { TableCardHeader } from '@/components/shared/TableCardHeader';
|
||||
import { TableCard } from '@/components/shared/TableCard';
|
||||
import {
|
||||
|
|
@ -193,7 +198,7 @@ export const UnifiedResourceHostTableCard: Component<UnifiedResourceHostTableCar
|
|||
>
|
||||
<TableCell
|
||||
colspan={9}
|
||||
class="py-1 pr-2 pl-4 text-[12px] sm:text-sm font-semibold text-base-content"
|
||||
class={getGroupedTableRowCellClass()}
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<Show
|
||||
|
|
@ -202,12 +207,12 @@ export const UnifiedResourceHostTableCard: Component<UnifiedResourceHostTableCar
|
|||
>
|
||||
<span>{group.cluster}</span>
|
||||
<Show when={shouldShowClusterGroupTypeLabel(group.cluster)}>
|
||||
<span class="inline-flex items-center rounded px-2 py-0.5 text-[10px] font-medium whitespace-nowrap bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-300">
|
||||
<span class={GROUPED_TABLE_ROW_BADGE_CLASS}>
|
||||
Cluster
|
||||
</span>
|
||||
</Show>
|
||||
</Show>
|
||||
<span class="text-[10px] text-muted font-normal">
|
||||
<span class={GROUPED_TABLE_ROW_META_CLASS}>
|
||||
{group.resources.length}{' '}
|
||||
{group.resources.length === 1 ? 'resource' : 'resources'}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -516,6 +516,7 @@ describe('UnifiedResourceTable performance contract', () => {
|
|||
|
||||
expect(unifiedResourceHostTableCardSource).not.toContain('kind="scope"');
|
||||
expect(unifiedResourceHostTableCardSource).toContain('getInteractiveGroupedTableRowClass');
|
||||
expect(unifiedResourceHostTableCardSource).toContain('getGroupedTableRowCellClass');
|
||||
expect(unifiedResourceHostTableCardSource).not.toContain('cursor-pointer bg-surface-alt');
|
||||
expect(unifiedResourceHostTableCardSource).toContain('TableCard');
|
||||
expect(unifiedResourceHostTableCardSource).toContain('TableCardHeader');
|
||||
|
|
|
|||
|
|
@ -18,7 +18,10 @@ import {
|
|||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/components/shared/Table';
|
||||
import { getGroupedTableRowClass } from '@/components/shared/groupedTableRowPresentation';
|
||||
import {
|
||||
getGroupedTableRowCellClass,
|
||||
getGroupedTableRowClass,
|
||||
} from '@/components/shared/groupedTableRowPresentation';
|
||||
import {
|
||||
connectionAgentVersionPresentation,
|
||||
fleetSignalClassName,
|
||||
|
|
@ -619,7 +622,6 @@ export const InfrastructureSourceManager: Component<InfrastructureSourceManagerP
|
|||
const discoveredRows = () => groupedDiscoveredRows().get(product.type) ?? [];
|
||||
const groupRowClass = () =>
|
||||
getGroupedTableRowClass('border-b border-border-subtle');
|
||||
const groupLabelClass = () => 'text-[15px] font-semibold text-base-content';
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -627,18 +629,18 @@ export const InfrastructureSourceManager: Component<InfrastructureSourceManagerP
|
|||
when={actionColumnVisible()}
|
||||
fallback={
|
||||
<TableRow class={groupRowClass()}>
|
||||
<TableCell colspan={5} class="px-3 py-1.5">
|
||||
<TableCell colspan={5} class={getGroupedTableRowCellClass()}>
|
||||
<div class="flex min-w-0 items-center gap-2">
|
||||
<span class={groupLabelClass()}>{product.label}</span>
|
||||
<span>{product.label}</span>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
}
|
||||
>
|
||||
<TableRow class={groupRowClass()}>
|
||||
<TableCell colspan={6} class="px-3 py-1.5">
|
||||
<TableCell colspan={6} class={getGroupedTableRowCellClass()}>
|
||||
<div class="flex items-center justify-between gap-2 whitespace-nowrap">
|
||||
<span class={groupLabelClass()}>{product.label}</span>
|
||||
<span>{product.label}</span>
|
||||
<Show when={!props.readOnly && props.onAddSource}>
|
||||
<button
|
||||
type="button"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,11 @@ import type { Node } from '@/types/api';
|
|||
import { getNodeDisplayName, hasAlternateDisplayName } from '@/utils/nodes';
|
||||
import { StatusDot } from '@/components/shared/StatusDot';
|
||||
import { getNodeStatusIndicator } from '@/utils/status';
|
||||
import { getGroupedTableRowClass } from './groupedTableRowPresentation';
|
||||
import {
|
||||
GROUPED_TABLE_ROW_BADGE_CLASS,
|
||||
getGroupedTableRowCellClass,
|
||||
getGroupedTableRowClass,
|
||||
} from './groupedTableRowPresentation';
|
||||
|
||||
interface NodeGroupHeaderProps {
|
||||
node: Node;
|
||||
|
|
@ -50,11 +54,11 @@ export const NodeGroupHeader: Component<NodeGroupHeaderProps> = (props) => {
|
|||
|
||||
<Show when={props.node.isClusterMember !== undefined}>
|
||||
<span
|
||||
class={`rounded px-2 py-0.5 text-[10px] font-medium whitespace-nowrap ${
|
||||
class={
|
||||
props.node.isClusterMember
|
||||
? 'bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-300'
|
||||
: 'bg-surface-alt text-muted'
|
||||
}`}
|
||||
? GROUPED_TABLE_ROW_BADGE_CLASS
|
||||
: 'rounded px-2 py-0.5 text-[10px] font-medium whitespace-nowrap bg-surface-alt text-muted'
|
||||
}
|
||||
>
|
||||
{props.node.isClusterMember ? props.node.clusterName : 'Standalone'}
|
||||
</span>
|
||||
|
|
@ -76,17 +80,14 @@ export const NodeGroupHeader: Component<NodeGroupHeaderProps> = (props) => {
|
|||
when={props.renderAs === 'tr'}
|
||||
fallback={
|
||||
<div class="bg-surface-alt w-full">
|
||||
<div class="py-0.5 pr-2 pl-4 text-[12px] sm:text-sm font-semibold text-base-content">
|
||||
<div class={getGroupedTableRowCellClass()}>
|
||||
<InnerContent />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<tr class={getGroupedTableRowClass(props.trClass)} {...props.trProps}>
|
||||
<td
|
||||
colspan={props.colspan}
|
||||
class="py-0.5 pr-2 pl-4 text-[12px] sm:text-sm font-semibold text-base-content"
|
||||
>
|
||||
<td colspan={props.colspan} class={getGroupedTableRowCellClass()}>
|
||||
<InnerContent />
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
|||
|
|
@ -439,6 +439,8 @@ describe('shared primitive guardrails', () => {
|
|||
);
|
||||
expect(frontendIndexCssSource).not.toContain('--color-grouped-table-row-bg: theme(');
|
||||
expect(groupedTableRowPresentationSource).toContain('GROUPED_TABLE_ROW_CLASS');
|
||||
expect(groupedTableRowPresentationSource).toContain('GROUPED_TABLE_ROW_CELL_CLASS');
|
||||
expect(groupedTableRowPresentationSource).toContain('getGroupedTableRowCellClass');
|
||||
expect(groupedTableRowPresentationSource).not.toContain('GROUPED_TABLE_ROW_DIVIDER_CLASS');
|
||||
|
||||
expect(guestRowSource).toContain('data-summary-row-active');
|
||||
|
|
@ -463,14 +465,21 @@ describe('shared primitive guardrails', () => {
|
|||
expect(storagePoolRowSource).toContain('data-summary-group-member-active');
|
||||
expect(storageGroupRowSource).toContain('STORAGE_GROUP_ROW_CLASS');
|
||||
expect(storageGroupPresentationSource).toContain('getInteractiveGroupedTableRowClass');
|
||||
expect(storageGroupPresentationSource).toContain('getGroupedTableRowCellClass');
|
||||
expect(nodeGroupHeaderSource).toContain('getGroupedTableRowClass');
|
||||
expect(nodeGroupHeaderSource).toContain('getGroupedTableRowCellClass');
|
||||
expect(workloadPanelSource).toContain('getInteractiveGroupedTableRowClass');
|
||||
expect(workloadPanelSource).toContain('getGroupedTableRowCellClass');
|
||||
expect(unifiedResourceHostTableCardSource).toContain('getInteractiveGroupedTableRowClass');
|
||||
expect(recoveryTablePresentationSource).toContain('GROUPED_TABLE_ROW_BASE_CLASS');
|
||||
expect(unifiedResourceHostTableCardSource).toContain('getGroupedTableRowCellClass');
|
||||
expect(recoveryTablePresentationSource).toContain('getGroupedTableRowCellClass');
|
||||
expect(alertHistoryTableGroupRowSource).toContain('getGroupedTableRowClass');
|
||||
expect(alertHistoryTableGroupRowSource).toContain('getGroupedTableRowCellClass');
|
||||
expect(alertHistoryTableGroupRowSource).not.toContain('class="bg-surface-alt"');
|
||||
expect(alertResourceTableDesktopSource).toContain('getGroupedTableRowClass');
|
||||
expect(alertResourceTableDesktopSource).toContain('getGroupedTableRowCellClass');
|
||||
expect(infrastructureSourceManagerSource).toContain('getGroupedTableRowClass');
|
||||
expect(infrastructureSourceManagerSource).toContain('getGroupedTableRowCellClass');
|
||||
expect(infrastructureSourceManagerSource).not.toContain('bg-base hover:bg-base');
|
||||
expect(unifiedResourceHostTableCardSource).toContain('data-summary-group-member-active');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,9 +1,17 @@
|
|||
export const GROUPED_TABLE_ROW_CLASS = 'grouped-table-row';
|
||||
export const GROUPED_TABLE_ROW_BASE_CLASS = `${GROUPED_TABLE_ROW_CLASS} transition-colors`;
|
||||
export const GROUPED_TABLE_ROW_INTERACTIVE_CLASS = `${GROUPED_TABLE_ROW_BASE_CLASS} cursor-pointer select-none duration-150`;
|
||||
export const GROUPED_TABLE_ROW_CELL_CLASS =
|
||||
'!px-2 !py-1 align-middle text-[12px] sm:text-sm font-semibold text-base-content';
|
||||
export const GROUPED_TABLE_ROW_META_CLASS = 'text-[10px] font-medium text-muted';
|
||||
export const GROUPED_TABLE_ROW_BADGE_CLASS =
|
||||
'inline-flex items-center rounded px-2 py-0.5 text-[10px] font-medium whitespace-nowrap bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-300';
|
||||
|
||||
export const getGroupedTableRowClass = (className = ''): string =>
|
||||
`${GROUPED_TABLE_ROW_BASE_CLASS} ${className}`.trim();
|
||||
|
||||
export const getInteractiveGroupedTableRowClass = (className = ''): string =>
|
||||
`${GROUPED_TABLE_ROW_INTERACTIVE_CLASS} ${className}`.trim();
|
||||
|
||||
export const getGroupedTableRowCellClass = (className = ''): string =>
|
||||
`${GROUPED_TABLE_ROW_CELL_CLASS} ${className}`.trim();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import { TableCell, TableRow } from '@/components/shared/Table';
|
||||
import { getGroupedTableRowClass } from '@/components/shared/groupedTableRowPresentation';
|
||||
import {
|
||||
GROUPED_TABLE_ROW_META_CLASS,
|
||||
getGroupedTableRowCellClass,
|
||||
getGroupedTableRowClass,
|
||||
} from '@/components/shared/groupedTableRowPresentation';
|
||||
|
||||
import type { AlertHistoryState } from './useAlertHistoryState';
|
||||
|
||||
|
|
@ -29,12 +33,12 @@ function getGroupSummaryLabel(group: AlertHistoryGroup) {
|
|||
export function AlertHistoryTableGroupRow(props: AlertHistoryTableGroupRowProps) {
|
||||
return (
|
||||
<TableRow class={getGroupedTableRowClass()}>
|
||||
<TableCell colspan={10} class="py-1.5 pr-3 pl-4 text-[12px] font-semibold sm:text-sm">
|
||||
<TableCell colspan={10} class={getGroupedTableRowCellClass()}>
|
||||
<div class="flex flex-col gap-1 sm:flex-row sm:items-center sm:justify-between sm:gap-3">
|
||||
<span class="truncate" title={props.group.fullLabel}>
|
||||
{props.group.label}
|
||||
</span>
|
||||
<span class="text-[10px] font-medium text-muted">
|
||||
<span class={GROUPED_TABLE_ROW_META_CLASS}>
|
||||
{getGroupSummaryLabel(props.group)}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { formatPercent } from '@/utils/format';
|
||||
import { getInteractiveGroupedTableRowClass } from '@/components/shared/groupedTableRowPresentation';
|
||||
import {
|
||||
getGroupedTableRowCellClass,
|
||||
getInteractiveGroupedTableRowClass,
|
||||
} from '@/components/shared/groupedTableRowPresentation';
|
||||
import type { NormalizedHealth } from './models';
|
||||
import type { StorageGroupedRecords } from '@/components/Storage/useStorageModel';
|
||||
import { getStorageHealthPresentation } from './healthPresentation';
|
||||
|
|
@ -21,7 +24,7 @@ export interface StorageGroupRowPresentation {
|
|||
}
|
||||
|
||||
export const STORAGE_GROUP_ROW_CLASS = getInteractiveGroupedTableRowClass('border-b border-border');
|
||||
export const STORAGE_GROUP_ROW_CELL_CLASS = 'px-1.5 sm:px-2 py-0.5';
|
||||
export const STORAGE_GROUP_ROW_CELL_CLASS = getGroupedTableRowCellClass();
|
||||
export const STORAGE_GROUP_ROW_CONTENT_CLASS = 'flex items-center gap-3';
|
||||
export const STORAGE_GROUP_ROW_LABEL_CLASS =
|
||||
'text-[11px] font-semibold text-base-content w-[140px] flex-shrink-0 truncate';
|
||||
|
|
|
|||
|
|
@ -471,7 +471,9 @@ describe('tab path helpers', () => {
|
|||
expect(alertHistoryTableGroupRowSource).toContain('export function AlertHistoryTableGroupRow');
|
||||
expect(alertHistoryTableGroupRowSource).toContain('getGroupSummaryLabel');
|
||||
expect(alertHistoryTableGroupRowSource).toContain('getGroupedTableRowClass');
|
||||
expect(alertHistoryTableGroupRowSource).toContain('getGroupedTableRowCellClass');
|
||||
expect(alertResourceTableDesktopSource).toContain('getGroupedTableRowClass');
|
||||
expect(alertResourceTableDesktopSource).toContain('getGroupedTableRowCellClass');
|
||||
expect(alertHistoryTableAlertRowSource).toContain('export function AlertHistoryTableAlertRow');
|
||||
expect(alertHistoryTableAlertRowSource).toContain('IncidentTimelinePanel');
|
||||
expect(alertHistoryTableAlertRowSource).toContain('InvestigateAlertButton');
|
||||
|
|
|
|||
|
|
@ -1265,7 +1265,7 @@ describe('frontend resource type boundaries', () => {
|
|||
expect(recoveryTablePresentationSource).toContain(
|
||||
'export function getRecoveryArtifactColumnHeaderClass',
|
||||
);
|
||||
expect(recoveryTablePresentationSource).toContain('GROUPED_TABLE_ROW_BASE_CLASS');
|
||||
expect(recoveryTablePresentationSource).toContain('getGroupedTableRowCellClass');
|
||||
expect(recoveryTablePresentationSource).toContain('getRecoveryItemTypeLabel');
|
||||
expect(recoveryTablePresentationSource).toContain('getRecoveryLocationFacetLabel');
|
||||
expect(recoveryTablePresentationSource).not.toContain('const titleize =');
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import {
|
|||
describe('recoveryTablePresentation', () => {
|
||||
it('exposes shared recovery table classes', () => {
|
||||
expect(RECOVERY_GROUP_HEADER_ROW_CLASS).toContain('grouped-table-row');
|
||||
expect(RECOVERY_GROUP_HEADER_TEXT_CLASS).toContain('font-medium');
|
||||
expect(RECOVERY_GROUP_HEADER_TEXT_CLASS).toContain('font-semibold');
|
||||
expect(RECOVERY_ADVANCED_FILTER_LABEL_CLASS).toContain('text-muted');
|
||||
expect(RECOVERY_ADVANCED_FILTER_FIELD_CLASS).toContain('focus:border-blue-500');
|
||||
expect(RECOVERY_GROUP_NO_TIMESTAMP_LABEL).toBe('No Timestamp');
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
import { GROUPED_TABLE_ROW_BASE_CLASS } from '@/components/shared/groupedTableRowPresentation';
|
||||
import {
|
||||
getGroupedTableRowCellClass,
|
||||
getGroupedTableRowClass,
|
||||
} from '@/components/shared/groupedTableRowPresentation';
|
||||
import type { ProtectionRollup, RecoveryOutcome, RecoveryPoint } from '@/types/recovery';
|
||||
import {
|
||||
getRecoveryItemTypeBadgeClass,
|
||||
|
|
@ -15,9 +18,8 @@ import type { RecoveryIssueTone } from '@/utils/recoveryIssuePresentation';
|
|||
export const STALE_ISSUE_THRESHOLD_MS = 7 * 24 * 60 * 60 * 1000;
|
||||
export const AGING_THRESHOLD_MS = 2 * 24 * 60 * 60 * 1000;
|
||||
|
||||
export const RECOVERY_GROUP_HEADER_ROW_CLASS = GROUPED_TABLE_ROW_BASE_CLASS;
|
||||
export const RECOVERY_GROUP_HEADER_TEXT_CLASS =
|
||||
'py-1 pr-3 pl-4 text-[11px] font-medium text-base-content';
|
||||
export const RECOVERY_GROUP_HEADER_ROW_CLASS = getGroupedTableRowClass();
|
||||
export const RECOVERY_GROUP_HEADER_TEXT_CLASS = getGroupedTableRowCellClass();
|
||||
export const RECOVERY_ADVANCED_FILTER_LABEL_CLASS = 'text-[11px] font-medium text-muted';
|
||||
export const RECOVERY_ADVANCED_FILTER_FIELD_CLASS =
|
||||
'min-h-[2.25rem] w-full rounded-md border border-border bg-surface px-2.5 py-1.5 text-sm text-base-content outline-none focus:border-blue-500';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue