mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-20 01:01:20 +00:00
Canonicalize shared all-filter labels
This commit is contained in:
parent
bfd6b42f15
commit
2e473b2ba1
23 changed files with 139 additions and 31 deletions
|
|
@ -228,6 +228,9 @@ Alert history filter defaults such as the all-time period option must likewise
|
|||
come from the alert overview/history presentation helper and the shared
|
||||
filter-option label primitive rather than hard-coded title-case strings in the
|
||||
history filter card.
|
||||
Alert configuration select options share that same rule: all-channel
|
||||
escalation labels must come from `alertConfigPresentation.ts` plus the shared
|
||||
filter-option primitive, not a schedule-page-local `All Channels` string.
|
||||
Thresholds empty states that hand operators to Infrastructure settings must use
|
||||
`frontend-modern/src/utils/infrastructureSettingsPresentation.ts` for the
|
||||
canonical `Settings → Infrastructure` label instead of hard-coding generic
|
||||
|
|
|
|||
|
|
@ -1677,6 +1677,14 @@ fetch orchestration. The shell must not re-accumulate localStorage or API
|
|||
runtime logic inline. Audit-log filter option labels must come from
|
||||
`frontend-modern/src/utils/auditLogPresentation.ts` and the shared filter-option
|
||||
label primitive instead of hard-coded title-case strings in the settings shell.
|
||||
That shared filter-option primitive is also the canonical owner for default
|
||||
`All <scope>` option wording wherever a product surface exposes filter selects
|
||||
or segmented filter choices. Workloads/dashboard filters, storage source
|
||||
filters, recovery history and platform/type filters, Kubernetes namespace
|
||||
drawers, resource-change timeline filters, and alert configuration options must
|
||||
call `frontend-modern/src/components/shared/filterOptionPresentation.ts` through
|
||||
their nearest presentation/model owner instead of hard-coding page-local `All
|
||||
...` labels.
|
||||
|
||||
The audit webhook settings surface now follows that same owner split.
|
||||
`frontend-modern/src/components/Settings/AuditWebhookPanel.tsx` stays the
|
||||
|
|
|
|||
|
|
@ -208,9 +208,12 @@ regression protection.
|
|||
header row exposes names such as Uptime, Image, Context, and Node instead
|
||||
of collapsing to only the visible text columns.
|
||||
Pod-mode workload filters must use the workload-owned
|
||||
`K8s Cluster` / `All K8s clusters` labels for Kubernetes context selection,
|
||||
`K8s cluster` / `All K8s clusters` labels for Kubernetes context selection,
|
||||
rather than a generic `Cluster` label that can be confused with Proxmox
|
||||
cluster terminology on adjacent infrastructure surfaces.
|
||||
cluster terminology on adjacent infrastructure surfaces, and all workload
|
||||
default filter-option labels must flow through the shared all-option
|
||||
presentation helper via the workload filter config model instead of the
|
||||
hot-path shell hard-coding local `All ...` strings.
|
||||
26. Keep long-range workload chart capping time-proportional across `frontend-modern/src/components/Workloads/WorkloadsSummary.tsx`, `frontend-modern/src/api/charts.ts`, and `internal/api/router.go`: when the workload hot path caps mixed-cadence history for top cards, it must bucket by time window rather than raw point index so 7-day and 30-day workload cards stay visually even without relaxing the protected payload budget.
|
||||
27. Keep summary hover/focus and sticky-card behavior on shared hot paths: infrastructure, workloads, and storage summary shells must reuse one page/group/entity scope model plus `frontend-modern/src/components/shared/StickySummarySection.tsx` inside the app scroll shell instead of per-page scroll listeners or per-card hover derivations, so row scrubbing highlights all cards, workload group headers, infrastructure cluster headers, and storage pool-group headers scope the summary coherently, pinned group focus remains route-backed and reversible, and the hot path does not multiply render or scroll work. That hot path stays row-first rather than adding fallback chrome: the on-screen row or group header is the scoped state, and any explicit reset belongs to one compact shared table-header action plus the shared `Escape` path, not to page-level scope strips, search-row widgets, or filter-bar badges. Background whitespace clearing may exist as a convenience, but the hot path must not depend on brittle dead-space hit testing as the only reversible control. The same hot path must therefore keep one page-level reset owner for filters plus pinned summary selections, and it must keep chart-backed summary-card geometry explicit and stable so hover rerenders, synchronized readouts, or idle header metadata cannot feed layout loops that grow or shrink the top cards over time. Recovery’s summary rail is not part of this interactive hot path; it may share summary-card framing, but it must remain non-interactive until a separately governed model says otherwise.
|
||||
The input path for that hot summary contract must stay shared too:
|
||||
|
|
@ -805,6 +808,10 @@ The dashboard-owned filter-config assembly now lives in
|
|||
filter runtime changes must extend through those owners instead of
|
||||
reintroducing dashboard-local state, reset drift, or inline config assembly
|
||||
into the shell.
|
||||
Workload type option labels are part of that filter-config model ownership:
|
||||
`DashboardFilter.tsx` must render the exported workload type option catalog
|
||||
instead of embedding its own `System containers` / `App containers` wording in
|
||||
the shell.
|
||||
The dashboard threshold slider now follows that same pattern: the shell stays
|
||||
in `frontend-modern/src/components/Dashboard/ThresholdSlider.tsx`, while
|
||||
metric-type text and fill presentation live in
|
||||
|
|
|
|||
|
|
@ -869,6 +869,10 @@ controls, and source selectors are also canonical presentation contracts:
|
|||
storage surfaces must consume `frontend-modern/src/components/Storage/storagePageState.ts`
|
||||
and `frontend-modern/src/utils/storageSources.ts` rather than re-declaring
|
||||
page-local title casing or alternate all-option labels.
|
||||
Recovery all-history, all-item-type, and all-platform defaults follow the same
|
||||
shared filter-option contract through
|
||||
`frontend-modern/src/utils/recoveryTablePresentation.ts`, so recovery history
|
||||
and protected-item tables do not invent separate default-filter wording.
|
||||
Physical-disk role and group filter defaults plus disk-type display labels
|
||||
must likewise come from `frontend-modern/src/features/storageBackups/diskPresentation.ts`;
|
||||
storage pages must not reintroduce local `All Roles`, `All Groups`, or
|
||||
|
|
|
|||
|
|
@ -1192,6 +1192,11 @@ timeline reads as the primary inspection surface instead of opening on a form.
|
|||
When that reveal is open, the controls still stack vertically instead of using
|
||||
a paired filter grid, so the filter state reads as one subordinate control
|
||||
column instead of a competing secondary layout.
|
||||
Timeline filter default options such as all kinds, all sources, and all
|
||||
adapters must use the shared frontend all-option presentation helper through
|
||||
`ResourceDetailDrawerOverviewTab.tsx`, not drawer-local hard-coded strings, so
|
||||
future wording changes stay aligned with workload, storage, recovery, and alert
|
||||
filters.
|
||||
When filters are active, the filtered facet bundle drives both the summary
|
||||
chips and the event log, so the header and the list stay aligned instead of
|
||||
showing stale unfiltered counts above filtered results.
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { PageControls } from '@/components/shared/PageControls';
|
|||
import { SearchInput } from '@/components/shared/SearchInput';
|
||||
import { STORAGE_KEYS } from '@/utils/localStorage';
|
||||
import type { DashboardFilterProps } from './dashboardFilterModel';
|
||||
import { DASHBOARD_WORKLOAD_TYPE_OPTIONS } from './dashboardWorkloadFilterConfigModel';
|
||||
import { useDashboardFilterState } from './useDashboardFilterState';
|
||||
|
||||
export const DashboardFilter: Component<DashboardFilterProps> = (props) => {
|
||||
|
|
@ -153,11 +154,9 @@ export const DashboardFilter: Component<DashboardFilterProps> = (props) => {
|
|||
}
|
||||
selectClass="min-w-[7rem]"
|
||||
>
|
||||
<option value="all">All</option>
|
||||
<option value="vm">VMs</option>
|
||||
<option value="system-container">System Containers</option>
|
||||
<option value="app-container">App Containers</option>
|
||||
<option value="pod">Pods</option>
|
||||
<For each={DASHBOARD_WORKLOAD_TYPE_OPTIONS}>
|
||||
{(option) => <option value={option.value}>{option.label}</option>}
|
||||
</For>
|
||||
</LabeledFilterSelect>
|
||||
|
||||
<LabeledFilterSelect
|
||||
|
|
|
|||
|
|
@ -370,7 +370,7 @@ describe('Dashboard pod workloads integration', () => {
|
|||
|
||||
await waitFor(() => {
|
||||
expect(lastHostFilter).toBeDefined();
|
||||
expect(lastHostFilter?.label).toBe('K8s Cluster');
|
||||
expect(lastHostFilter?.label).toBe('K8s cluster');
|
||||
});
|
||||
const hostFilter = requireLastHostFilter();
|
||||
hostFilter.onChange('cluster-a');
|
||||
|
|
|
|||
|
|
@ -676,10 +676,13 @@ describe('Dashboard performance contract', () => {
|
|||
'export const buildDashboardHostFilterConfig',
|
||||
);
|
||||
expect(dashboardWorkloadFilterConfigModelSource).toContain(
|
||||
"DASHBOARD_KUBERNETES_CONTEXT_FILTER_LABEL = 'K8s Cluster'",
|
||||
"DASHBOARD_KUBERNETES_CONTEXT_FILTER_LABEL = 'K8s cluster'",
|
||||
);
|
||||
expect(dashboardWorkloadFilterConfigModelSource).toContain(
|
||||
"DASHBOARD_KUBERNETES_CONTEXT_ALL_OPTION_LABEL = 'All K8s clusters'",
|
||||
"getAllFilterOptionLabel('K8s clusters')",
|
||||
);
|
||||
expect(dashboardWorkloadFilterConfigModelSource).toContain(
|
||||
'DASHBOARD_WORKLOAD_TYPE_OPTIONS',
|
||||
);
|
||||
expect(dashboardWorkloadFilterConfigModelSource).toContain(
|
||||
'export const buildDashboardNamespaceFilterConfig',
|
||||
|
|
|
|||
|
|
@ -86,7 +86,9 @@ describe('DashboardFilter', () => {
|
|||
|
||||
const options = typeSelect.querySelectorAll('option');
|
||||
const values = Array.from(options).map((o) => o.value);
|
||||
const labels = Array.from(options).map((o) => o.textContent);
|
||||
expect(values).toEqual(['all', 'vm', 'system-container', 'app-container', 'pod']);
|
||||
expect(labels).toEqual(['All', 'VMs', 'System containers', 'App containers', 'Pods']);
|
||||
});
|
||||
|
||||
it('renders the Status filter select with all options', () => {
|
||||
|
|
|
|||
|
|
@ -5,8 +5,13 @@ import {
|
|||
buildDashboardHostFilterConfig,
|
||||
buildDashboardNamespaceFilterConfig,
|
||||
buildDashboardPlatformFilterConfig,
|
||||
DASHBOARD_CONTAINER_RUNTIME_ALL_OPTION_LABEL,
|
||||
DASHBOARD_KUBERNETES_CONTEXT_ALL_OPTION_LABEL,
|
||||
DASHBOARD_KUBERNETES_CONTEXT_FILTER_LABEL,
|
||||
DASHBOARD_NAMESPACE_ALL_OPTION_LABEL,
|
||||
DASHBOARD_NODE_ALL_OPTION_LABEL,
|
||||
DASHBOARD_PLATFORM_ALL_OPTION_LABEL,
|
||||
DASHBOARD_WORKLOAD_TYPE_OPTIONS,
|
||||
} from '../dashboardWorkloadFilterConfigModel';
|
||||
|
||||
describe('dashboardWorkloadFilterConfigModel', () => {
|
||||
|
|
@ -25,6 +30,11 @@ describe('dashboardWorkloadFilterConfigModel', () => {
|
|||
id: 'workloads-container-runtime-filter',
|
||||
label: 'Runtime',
|
||||
value: 'docker',
|
||||
options: [
|
||||
{ value: '', label: DASHBOARD_CONTAINER_RUNTIME_ALL_OPTION_LABEL },
|
||||
{ value: 'containerd', label: 'containerd' },
|
||||
{ value: 'docker', label: 'docker' },
|
||||
],
|
||||
});
|
||||
|
||||
expect(
|
||||
|
|
@ -79,6 +89,10 @@ describe('dashboardWorkloadFilterConfigModel', () => {
|
|||
id: 'workloads-node-filter',
|
||||
label: 'Node',
|
||||
value: 'cluster-a-node-a',
|
||||
options: [
|
||||
{ value: '', label: DASHBOARD_NODE_ALL_OPTION_LABEL },
|
||||
{ value: 'cluster-a-node-a', label: 'node-a' },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -97,6 +111,11 @@ describe('dashboardWorkloadFilterConfigModel', () => {
|
|||
id: 'workloads-k8s-namespace-filter',
|
||||
label: 'Namespace',
|
||||
value: 'default',
|
||||
options: [
|
||||
{ value: '', label: DASHBOARD_NAMESPACE_ALL_OPTION_LABEL },
|
||||
{ value: 'default', label: 'default' },
|
||||
{ value: 'kube-system', label: 'kube-system' },
|
||||
],
|
||||
});
|
||||
|
||||
expect(
|
||||
|
|
@ -128,10 +147,20 @@ describe('dashboardWorkloadFilterConfigModel', () => {
|
|||
label: 'Platform',
|
||||
value: 'truenas',
|
||||
options: [
|
||||
{ value: '', label: 'All platforms' },
|
||||
{ value: '', label: DASHBOARD_PLATFORM_ALL_OPTION_LABEL },
|
||||
{ value: 'docker', label: 'Docker' },
|
||||
{ value: 'truenas', label: 'TrueNAS' },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps workload type filter labels in the presentation model', () => {
|
||||
expect(DASHBOARD_WORKLOAD_TYPE_OPTIONS).toEqual([
|
||||
{ value: 'all', label: 'All' },
|
||||
{ value: 'vm', label: 'VMs' },
|
||||
{ value: 'system-container', label: 'System containers' },
|
||||
{ value: 'app-container', label: 'App containers' },
|
||||
{ value: 'pod', label: 'Pods' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,9 +1,23 @@
|
|||
import type { ViewMode } from '@/types/workloads';
|
||||
import { getAllFilterOptionLabel } from '@/components/shared/filterOptionPresentation';
|
||||
import type { DashboardToolbarFilterConfig } from './dashboardFilterModel';
|
||||
import type { DashboardWorkloadNodeOption } from './dashboardWorkloadRouteModel';
|
||||
|
||||
export const DASHBOARD_KUBERNETES_CONTEXT_FILTER_LABEL = 'K8s Cluster';
|
||||
export const DASHBOARD_KUBERNETES_CONTEXT_ALL_OPTION_LABEL = 'All K8s clusters';
|
||||
export const DASHBOARD_KUBERNETES_CONTEXT_FILTER_LABEL = 'K8s cluster';
|
||||
export const DASHBOARD_KUBERNETES_CONTEXT_ALL_OPTION_LABEL =
|
||||
getAllFilterOptionLabel('K8s clusters');
|
||||
export const DASHBOARD_CONTAINER_RUNTIME_ALL_OPTION_LABEL =
|
||||
getAllFilterOptionLabel('runtimes');
|
||||
export const DASHBOARD_PLATFORM_ALL_OPTION_LABEL = getAllFilterOptionLabel('platforms');
|
||||
export const DASHBOARD_NODE_ALL_OPTION_LABEL = getAllFilterOptionLabel('nodes');
|
||||
export const DASHBOARD_NAMESPACE_ALL_OPTION_LABEL = getAllFilterOptionLabel('namespaces');
|
||||
export const DASHBOARD_WORKLOAD_TYPE_OPTIONS: Array<{ value: ViewMode; label: string }> = [
|
||||
{ value: 'all', label: 'All' },
|
||||
{ value: 'vm', label: 'VMs' },
|
||||
{ value: 'system-container', label: 'System containers' },
|
||||
{ value: 'app-container', label: 'App containers' },
|
||||
{ value: 'pod', label: 'Pods' },
|
||||
];
|
||||
|
||||
interface DashboardContainerRuntimeFilterConfigOptions {
|
||||
isWorkloadsRoute: boolean;
|
||||
|
|
@ -29,7 +43,7 @@ export const buildDashboardContainerRuntimeFilterConfig = ({
|
|||
label: 'Runtime',
|
||||
value: containerRuntime,
|
||||
options: [
|
||||
{ value: '', label: 'All runtimes' },
|
||||
{ value: '', label: DASHBOARD_CONTAINER_RUNTIME_ALL_OPTION_LABEL },
|
||||
...runtimeOptions.map((value) => ({ value, label: value })),
|
||||
],
|
||||
onChange,
|
||||
|
|
@ -57,7 +71,7 @@ export const buildDashboardPlatformFilterConfig = ({
|
|||
id: 'workloads-platform-filter',
|
||||
label: 'Platform',
|
||||
value: selectedPlatform ?? '',
|
||||
options: [{ value: '', label: 'All platforms' }, ...platformOptions],
|
||||
options: [{ value: '', label: DASHBOARD_PLATFORM_ALL_OPTION_LABEL }, ...platformOptions],
|
||||
onChange,
|
||||
};
|
||||
};
|
||||
|
|
@ -102,7 +116,7 @@ export const buildDashboardHostFilterConfig = ({
|
|||
id: 'workloads-node-filter',
|
||||
label: 'Node',
|
||||
value: selectedNode ?? '',
|
||||
options: [{ value: '', label: 'All nodes' }, ...workloadNodeOptions],
|
||||
options: [{ value: '', label: DASHBOARD_NODE_ALL_OPTION_LABEL }, ...workloadNodeOptions],
|
||||
onChange: onNodeChange,
|
||||
};
|
||||
};
|
||||
|
|
@ -131,7 +145,7 @@ export const buildDashboardNamespaceFilterConfig = ({
|
|||
label: 'Namespace',
|
||||
value: selectedNamespace ?? '',
|
||||
options: [
|
||||
{ value: '', label: 'All namespaces' },
|
||||
{ value: '', label: DASHBOARD_NAMESPACE_ALL_OPTION_LABEL },
|
||||
...namespaceOptions.map((value) => ({ value, label: value })),
|
||||
],
|
||||
onChange,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import { TemperaturesCard } from '@/components/shared/cards/TemperaturesCard';
|
|||
import { RaidCard } from '@/components/shared/cards/RaidCard';
|
||||
import { DiscoveryTab } from '@/components/Discovery/DiscoveryTab';
|
||||
import { WebInterfaceUrlField } from '@/components/shared/WebInterfaceUrlField';
|
||||
import { getAllFilterOptionLabel } from '@/components/shared/filterOptionPresentation';
|
||||
import { getServiceHealthPresentation } from '@/utils/serviceHealthPresentation';
|
||||
import {
|
||||
getResourceRoutingScopeLabel,
|
||||
|
|
@ -69,7 +70,7 @@ const vmwareRowToneClass = (tone?: 'default' | 'accent' | 'warning'): string =>
|
|||
};
|
||||
|
||||
const timelineKindOptions: Array<{ label: string; value: ResourceChangeKind | '' }> = [
|
||||
{ label: 'All kinds', value: '' },
|
||||
{ label: getAllFilterOptionLabel('kinds'), value: '' },
|
||||
...RESOURCE_CHANGE_KIND_ORDER.map((kind) => ({
|
||||
label: getResourceChangeKindPresentation(kind).label,
|
||||
value: kind,
|
||||
|
|
@ -77,7 +78,7 @@ const timelineKindOptions: Array<{ label: string; value: ResourceChangeKind | ''
|
|||
];
|
||||
|
||||
const timelineSourceTypeOptions: Array<{ label: string; value: ResourceChangeSourceType | '' }> = [
|
||||
{ label: 'All sources', value: '' },
|
||||
{ label: getAllFilterOptionLabel('sources'), value: '' },
|
||||
...RESOURCE_CHANGE_SOURCE_TYPE_ORDER.map((sourceType) => ({
|
||||
label: getResourceChangeSourceTypePresentation(sourceType).label,
|
||||
value: sourceType,
|
||||
|
|
@ -88,7 +89,7 @@ const timelineSourceAdapterOptions: Array<{
|
|||
label: string;
|
||||
value: ResourceChangeSourceAdapter | '';
|
||||
}> = [
|
||||
{ label: 'All adapters', value: '' },
|
||||
{ label: getAllFilterOptionLabel('adapters'), value: '' },
|
||||
...RESOURCE_CHANGE_SOURCE_ADAPTER_ORDER.map((sourceAdapter) => ({
|
||||
label: getResourceChangeSourceAdapterPresentation(sourceAdapter).label,
|
||||
value: sourceAdapter,
|
||||
|
|
|
|||
|
|
@ -122,6 +122,10 @@ describe('ResourceDetailDrawer change history section', () => {
|
|||
expect(discoveryTabSource).not.toContain('triggerDiscovery(');
|
||||
expect(discoveryTabSource).not.toContain('updateDiscoveryNotes(');
|
||||
expect(resourceDetailDrawerOverviewSource).toContain("from '@/components/shared/TagBadges'");
|
||||
expect(resourceDetailDrawerOverviewSource).toContain('getAllFilterOptionLabel');
|
||||
expect(resourceDetailDrawerOverviewSource).not.toContain("'All kinds'");
|
||||
expect(resourceDetailDrawerOverviewSource).not.toContain("'All sources'");
|
||||
expect(resourceDetailDrawerOverviewSource).not.toContain("'All adapters'");
|
||||
expect(resourceDetailDrawerOverviewSource).not.toContain(
|
||||
"from '@/components/Dashboard/TagBadges'",
|
||||
);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import {
|
|||
import { normalizeRecoveryModeQueryValue } from '@/utils/recoveryRecordPresentation';
|
||||
import {
|
||||
getRecoveryArtifactColumnLabel,
|
||||
getRecoveryAllHistoryLabel,
|
||||
getRecoveryAllItemTypesLabel,
|
||||
getRecoveryAllPlatformsLabel,
|
||||
getRecoveryHistorySearchPlaceholder,
|
||||
|
|
@ -250,7 +251,7 @@ export const RecoveryHistorySection: Component<RecoveryHistorySectionProps> = (p
|
|||
}}
|
||||
class={RECOVERY_ADVANCED_FILTER_FIELD_CLASS}
|
||||
>
|
||||
<option value="all">All history</option>
|
||||
<option value="all">{getRecoveryAllHistoryLabel()}</option>
|
||||
<option value="workload">Workloads only</option>
|
||||
</select>
|
||||
</label>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import type { Accessor } from 'solid-js';
|
||||
import { getAllFilterOptionLabel } from '@/components/shared/filterOptionPresentation';
|
||||
import type { StorageHealthFilter, StorageRecord } from '@/features/storageBackups/models';
|
||||
import {
|
||||
DEFAULT_PHYSICAL_DISK_FACET_FILTER,
|
||||
|
|
@ -196,7 +197,9 @@ export const hasActiveStorageFilters = (state: StorageFilterActivityState): bool
|
|||
DEFAULT_STORAGE_DISK_GROUP_FILTER;
|
||||
|
||||
export const getStorageNodeFilterLabel = (view: StorageView): string =>
|
||||
view === 'disks' ? 'All disk hosts' : 'All nodes';
|
||||
view === 'disks'
|
||||
? getAllFilterOptionLabel('disk hosts')
|
||||
: getAllFilterOptionLabel('nodes');
|
||||
|
||||
export const readStorageRouteValue = (value: string | undefined, defaultValue: string): string => {
|
||||
const normalized = (value || '').trim();
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ describe('FilterHeader', () => {
|
|||
}
|
||||
: {
|
||||
id: 'workloads-k8s-context-filter',
|
||||
label: 'K8s Cluster',
|
||||
label: 'K8s cluster',
|
||||
options: [{ value: '', label: 'All K8s clusters' }],
|
||||
};
|
||||
|
||||
|
|
@ -142,7 +142,7 @@ describe('FilterHeader', () => {
|
|||
screen.getByRole('button', { name: 'Show pods' }).click();
|
||||
|
||||
await waitFor(() =>
|
||||
expect(screen.getByLabelText('K8s Cluster')).toBe(screen.getByTestId('dynamic-filter')),
|
||||
expect(screen.getByLabelText('K8s cluster')).toBe(screen.getByTestId('dynamic-filter')),
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ describe('alertConfigPresentation', () => {
|
|||
);
|
||||
expect(ALERT_CONFIG_ESCALATION_NOTIFY_EMAIL).toBe('Email');
|
||||
expect(ALERT_CONFIG_ESCALATION_NOTIFY_WEBHOOKS).toBe('Webhooks');
|
||||
expect(ALERT_CONFIG_ESCALATION_NOTIFY_ALL).toBe('All Channels');
|
||||
expect(ALERT_CONFIG_ESCALATION_NOTIFY_ALL).toBe('All channels');
|
||||
expect(ALERT_CONFIG_SUMMARY_TITLE).toBe('Configuration summary');
|
||||
expect(ALERT_CONFIG_SUMMARY_DESCRIPTION).toBe(
|
||||
'Preview of the active schedule settings.',
|
||||
|
|
@ -193,7 +193,7 @@ describe('alertConfigPresentation', () => {
|
|||
);
|
||||
expect(getAlertConfigEscalationNotifyLabel('email')).toBe('Email');
|
||||
expect(getAlertConfigEscalationNotifyLabel('webhook')).toBe('Webhooks');
|
||||
expect(getAlertConfigEscalationNotifyLabel('all')).toBe('All Channels');
|
||||
expect(getAlertConfigEscalationNotifyLabel('all')).toBe('All channels');
|
||||
expect(getAlertConfigSummaryRecoveryEnabled()).toBe(
|
||||
ALERT_CONFIG_SUMMARY_RECOVERY,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -697,6 +697,7 @@ describe('frontend resource type boundaries', () => {
|
|||
expect(sourcePlatformsSource).toContain('titleCaseDelimitedLabel');
|
||||
expect(sourcePlatformsSource).not.toContain('const titleize =');
|
||||
expect(storageSourcesSource).toContain('titleCaseDelimitedLabel');
|
||||
expect(storageSourcesSource).toContain('getAllFilterOptionLabel');
|
||||
expect(storageSourcesSource).not.toContain('const titleCaseLabel =');
|
||||
expect(workloadsSource).toContain('export const normalizeWorkloadViewModeParam');
|
||||
expect(orgScopeSource).toContain("export const DEFAULT_ORG_SCOPE = 'default'");
|
||||
|
|
@ -804,6 +805,10 @@ describe('frontend resource type boundaries', () => {
|
|||
expect(dashboardWorkloadFilterConfigModelSource).toContain(
|
||||
'export const buildDashboardHostFilterConfig',
|
||||
);
|
||||
expect(dashboardWorkloadFilterConfigModelSource).toContain('getAllFilterOptionLabel');
|
||||
expect(dashboardWorkloadFilterConfigModelSource).toContain(
|
||||
'DASHBOARD_WORKLOAD_TYPE_OPTIONS',
|
||||
);
|
||||
expect(dashboardWorkloadFilterConfigModelSource).toContain(
|
||||
'export const buildDashboardNamespaceFilterConfig',
|
||||
);
|
||||
|
|
@ -989,6 +994,7 @@ describe('frontend resource type boundaries', () => {
|
|||
expect(guestRowSource).not.toContain('buildGuestId');
|
||||
expect(tagBadgesSource).toContain("from '@/components/shared/Tooltip'");
|
||||
expect(resourceDetailDrawerOverviewSource).toContain("from '@/components/shared/TagBadges'");
|
||||
expect(resourceDetailDrawerOverviewSource).toContain('getAllFilterOptionLabel');
|
||||
expect(resourceDetailDrawerOverviewSource).not.toContain(
|
||||
"from '@/components/Dashboard/TagBadges'",
|
||||
);
|
||||
|
|
@ -1111,6 +1117,8 @@ describe('frontend resource type boundaries', () => {
|
|||
expect(recoveryHistorySectionSource).toContain('useRecoveryHistorySectionState');
|
||||
expect(recoveryHistorySectionSource).toContain('RecoveryHistoryTable');
|
||||
expect(recoveryHistorySectionSource).toContain('TableCard');
|
||||
expect(recoveryHistorySectionSource).toContain('getRecoveryAllHistoryLabel');
|
||||
expect(recoveryTablePresentationSource).toContain('getAllFilterOptionLabel');
|
||||
expect(recoveryHistorySectionSource).not.toContain(
|
||||
'overflow-hidden border-border-subtle bg-surface',
|
||||
);
|
||||
|
|
@ -2428,6 +2436,7 @@ describe('frontend resource type boundaries', () => {
|
|||
expect(storagePageStateSource).toContain('export const countActiveStorageFilters');
|
||||
expect(storagePageStateSource).toContain('export const hasActiveStorageFilters');
|
||||
expect(storagePageStateSource).toContain('export const getStorageNodeFilterLabel');
|
||||
expect(storagePageStateSource).toContain('getAllFilterOptionLabel');
|
||||
expect(storagePageStateSource).toContain('export const readStorageRouteValue');
|
||||
expect(storagePageStateSource).toContain('export const writeStorageRouteValue');
|
||||
expect(storagePageStateSource).toContain('export const coerceSelectedStorageNodeId');
|
||||
|
|
@ -2470,6 +2479,7 @@ describe('frontend resource type boundaries', () => {
|
|||
expect(k8sDeploymentsDrawerSource).toContain('getK8sDeploymentsDrawerPresentation');
|
||||
expect(k8sDeploymentsDrawerSource).toContain('getK8sDeploymentsEmptyState');
|
||||
expect(k8sDeploymentsDrawerSource).toContain('getK8sDeploymentsLoadingState');
|
||||
expect(k8sDeploymentPresentationSource).toContain('getAllFilterOptionLabel');
|
||||
expect(k8sDeploymentsDrawerSource).not.toContain('const statusTone =');
|
||||
expect(k8sDeploymentsDrawerSource).not.toContain('Desired state controllers (not Pods)');
|
||||
expect(k8sDeploymentsDrawerSource).not.toContain('Search deployments...');
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {
|
|||
getRecoveryArtifactColumnHeaderClass,
|
||||
getRecoveryArtifactRowClass,
|
||||
getRecoveryArtifactTableMinWidth,
|
||||
getRecoveryAllHistoryLabel,
|
||||
getRecoveryAllItemTypesLabel,
|
||||
getRecoveryAllPlatformsLabel,
|
||||
getRecoveryAnyItemLabel,
|
||||
|
|
@ -21,6 +22,7 @@ import {
|
|||
isRecoveryRollupStale,
|
||||
RECOVERY_ADVANCED_FILTER_FIELD_CLASS,
|
||||
RECOVERY_ADVANCED_FILTER_LABEL_CLASS,
|
||||
RECOVERY_ALL_HISTORY_LABEL,
|
||||
RECOVERY_ALL_ITEM_TYPES_LABEL,
|
||||
RECOVERY_ALL_PLATFORMS_LABEL,
|
||||
RECOVERY_ANY_ITEM_LABEL,
|
||||
|
|
@ -43,6 +45,7 @@ describe('recoveryTablePresentation', () => {
|
|||
expect(RECOVERY_PROTECTED_SEARCH_PLACEHOLDER).toBe('Search protected items...');
|
||||
expect(RECOVERY_HISTORY_SEARCH_PLACEHOLDER).toBe('Search recovery history...');
|
||||
expect(RECOVERY_SEARCH_HISTORY_EMPTY_MESSAGE).toBe('Recent searches appear here.');
|
||||
expect(RECOVERY_ALL_HISTORY_LABEL).toBe('All history');
|
||||
expect(RECOVERY_ALL_ITEM_TYPES_LABEL).toBe('All item types');
|
||||
expect(RECOVERY_ALL_PLATFORMS_LABEL).toBe('All platforms');
|
||||
expect(RECOVERY_ANY_ITEM_LABEL).toBe('Any item');
|
||||
|
|
@ -50,6 +53,7 @@ describe('recoveryTablePresentation', () => {
|
|||
expect(getRecoveryProtectedSearchPlaceholder()).toBe('Search protected items...');
|
||||
expect(getRecoveryHistorySearchPlaceholder()).toBe('Search recovery history...');
|
||||
expect(getRecoverySearchHistoryEmptyMessage()).toBe('Recent searches appear here.');
|
||||
expect(getRecoveryAllHistoryLabel()).toBe('All history');
|
||||
expect(getRecoveryAllItemTypesLabel()).toBe('All item types');
|
||||
expect(getRecoveryAllPlatformsLabel()).toBe('All platforms');
|
||||
expect(getRecoveryAnyItemLabel()).toBe('Any item');
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { getAllFilterOptionLabel } from '@/components/shared/filterOptionPresentation';
|
||||
|
||||
export const ALERT_CONFIG_UNSAVED_CHANGES = 'You have unsaved changes';
|
||||
export const ALERT_CONFIG_SAVE_CHANGES = 'Save Changes';
|
||||
export const ALERT_CONFIG_RESET_DEFAULTS = 'Reset to defaults';
|
||||
|
|
@ -69,7 +71,7 @@ export const ALERT_CONFIG_ESCALATION_NOTIFY_LABEL = 'Notify';
|
|||
export const ALERT_CONFIG_ESCALATION_MINUTES_SUFFIX = 'min';
|
||||
export const ALERT_CONFIG_ESCALATION_NOTIFY_EMAIL = 'Email';
|
||||
export const ALERT_CONFIG_ESCALATION_NOTIFY_WEBHOOKS = 'Webhooks';
|
||||
export const ALERT_CONFIG_ESCALATION_NOTIFY_ALL = 'All Channels';
|
||||
export const ALERT_CONFIG_ESCALATION_NOTIFY_ALL = getAllFilterOptionLabel('channels');
|
||||
export const ALERT_CONFIG_ESCALATION_REMOVE_TITLE = 'Remove escalation level';
|
||||
export const ALERT_CONFIG_ESCALATION_ADD_LABEL = 'Add Escalation Level';
|
||||
export const ALERT_CONFIG_SUMMARY_TITLE = 'Configuration summary';
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import { getAllFilterOptionLabel } from '@/components/shared/filterOptionPresentation';
|
||||
|
||||
export const K8S_DEPLOYMENTS_DRAWER_TITLE = 'Deployments';
|
||||
export const K8S_DEPLOYMENTS_DRAWER_DESCRIPTION = 'Desired state controllers (not Pods)';
|
||||
export const K8S_DEPLOYMENTS_SEARCH_PLACEHOLDER = 'Search deployments...';
|
||||
export const K8S_DEPLOYMENTS_NAMESPACE_FILTER_LABEL = 'Namespace';
|
||||
export const K8S_DEPLOYMENTS_ALL_NAMESPACES_LABEL = 'All namespaces';
|
||||
export const K8S_DEPLOYMENTS_ALL_NAMESPACES_LABEL = getAllFilterOptionLabel('namespaces');
|
||||
export const K8S_DEPLOYMENTS_OPEN_PODS_LABEL = 'Open Pods';
|
||||
export const K8S_DEPLOYMENTS_VIEW_PODS_LABEL = 'View Pods';
|
||||
export const K8S_DEPLOYMENTS_COLUMN_DEPLOYMENT_LABEL = 'Deployment';
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import {
|
|||
getGroupedTableRowCellClass,
|
||||
getGroupedTableRowClass,
|
||||
} from '@/components/shared/groupedTableRowPresentation';
|
||||
import { getAllFilterOptionLabel } from '@/components/shared/filterOptionPresentation';
|
||||
import type { ProtectionRollup, RecoveryOutcome, RecoveryPoint } from '@/types/recovery';
|
||||
import {
|
||||
getRecoveryItemTypeBadgeClass,
|
||||
|
|
@ -27,8 +28,9 @@ export const RECOVERY_GROUP_NO_TIMESTAMP_LABEL = 'No Timestamp';
|
|||
export const RECOVERY_PROTECTED_SEARCH_PLACEHOLDER = 'Search protected items...';
|
||||
export const RECOVERY_HISTORY_SEARCH_PLACEHOLDER = 'Search recovery history...';
|
||||
export const RECOVERY_SEARCH_HISTORY_EMPTY_MESSAGE = 'Recent searches appear here.';
|
||||
export const RECOVERY_ALL_ITEM_TYPES_LABEL = 'All item types';
|
||||
export const RECOVERY_ALL_PLATFORMS_LABEL = 'All platforms';
|
||||
export const RECOVERY_ALL_HISTORY_LABEL = getAllFilterOptionLabel('history');
|
||||
export const RECOVERY_ALL_ITEM_TYPES_LABEL = getAllFilterOptionLabel('item types');
|
||||
export const RECOVERY_ALL_PLATFORMS_LABEL = getAllFilterOptionLabel('platforms');
|
||||
export const RECOVERY_ANY_ITEM_LABEL = 'Any item';
|
||||
export const RECOVERY_ARTIFACT_METADATA_TEXT_CLASS = 'text-[11px] font-medium text-base-content/80';
|
||||
export const RECOVERY_ARTIFACT_COLUMN_LABELS: Record<string, string> = {
|
||||
|
|
@ -99,6 +101,10 @@ export function getRecoveryAllItemTypesLabel(): string {
|
|||
return RECOVERY_ALL_ITEM_TYPES_LABEL;
|
||||
}
|
||||
|
||||
export function getRecoveryAllHistoryLabel(): string {
|
||||
return RECOVERY_ALL_HISTORY_LABEL;
|
||||
}
|
||||
|
||||
export function getRecoveryAllPlatformsLabel(): string {
|
||||
return RECOVERY_ALL_PLATFORMS_LABEL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import type { Storage } from '@/types/api';
|
||||
import { getAllFilterOptionLabel } from '@/components/shared/filterOptionPresentation';
|
||||
import { getSourcePlatformLabel, normalizeSourcePlatformKey } from '@/utils/sourcePlatforms';
|
||||
import { titleCaseDelimitedLabel } from '@/utils/textPresentation';
|
||||
|
||||
|
|
@ -19,7 +20,7 @@ export interface StorageSourceOption {
|
|||
|
||||
const ALL_STORAGE_SOURCE_OPTION: StorageSourceOption = {
|
||||
key: 'all',
|
||||
label: 'All sources',
|
||||
label: getAllFilterOptionLabel('sources'),
|
||||
tone: 'slate',
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue