mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-07-09 16:00:59 +00:00
Converge textarea fields on shared primitive
This commit is contained in:
parent
5a16a777bd
commit
241ac9feaf
9 changed files with 134 additions and 101 deletions
|
|
@ -94,6 +94,11 @@ operator-facing alert routing behavior for live runtime alerts.
|
|||
## Shared Boundaries
|
||||
|
||||
1. `internal/proxmoxidentity/backup_identity.go` shared with `monitoring`, `storage-recovery`: Proxmox PBS backup subject identity is a shared runtime boundary for monitoring backup freshness, backup-age alert attribution, and recovery-point guest mapping.
|
||||
Alert multiline field presentation is shared with frontend-primitives:
|
||||
notification, timeline, threshold ignored-prefix, and resource threshold note
|
||||
editors must compose the shared `FormTextarea` primitive for label/id/help
|
||||
wiring and textarea chrome instead of rendering raw native `<textarea>` shells
|
||||
in alert-owned runtime components.
|
||||
|
||||
## Extension Points
|
||||
|
||||
|
|
@ -210,8 +215,7 @@ consumers. Email recipient lists, Apprise target lists, webhook payload
|
|||
templates, threshold ignored-prefix input, and incident timeline notes must
|
||||
compose `FormTextarea` for label/id/help wiring and textarea chrome instead of
|
||||
recreating raw labelled `<textarea>` shells locally. Alert resource row/mobile
|
||||
note editors are the only bounded residual until their row-editor behavior is
|
||||
migrated through the same primitive.
|
||||
note editors now follow the same primitive contract.
|
||||
Guest metric canonical state remains resource-backed and therefore node-scoped
|
||||
for Proxmox guests, so node moves must not strand active alert state on the
|
||||
previous resource ID. When a guest metric alert survives a node move, alerts
|
||||
|
|
|
|||
|
|
@ -193,6 +193,10 @@ tabs. The thresholds platform IA is platform-shaped: Proxmox, Docker,
|
|||
Kubernetes, TrueNAS, vSphere, PBS, PMG, and Systems. Frontend primitives own
|
||||
the chip, reset, "+ Filter", and route-backed shell pattern; alerts must not
|
||||
replace that with page-local search/tab chrome or legacy neutral buckets.
|
||||
Native multiline form fields are also a shared primitive boundary.
|
||||
`FormTextarea` owns label/id/help wiring, controlled value synchronization, and
|
||||
textarea chrome for alert, settings, and infrastructure runtime surfaces; those
|
||||
surfaces must not recreate raw native `<textarea>` shells locally.
|
||||
|
||||
The System Updates install guide is a shared settings primitive, not a
|
||||
deployment-lane-only panel. `UpdateInstallGuide` must render the canonical
|
||||
|
|
@ -2754,9 +2758,8 @@ label/id wiring, helper-text description merging, value synchronization, default
|
|||
textarea chrome, and compact styling hooks for multi-line text fields. Alert
|
||||
destination fields, incident notes, infrastructure merge reports, commercial
|
||||
recovery input, and agent-profile prompt/description fields must compose
|
||||
`FormTextarea`; bounded residual raw textareas are limited to editor-specific
|
||||
surfaces called out in the shared-template registry until their dedicated
|
||||
migration slices retire them.
|
||||
`FormTextarea`; alert, settings, and infrastructure runtime code must not
|
||||
recreate raw native `<textarea>` shells outside that primitive.
|
||||
Search controls are registry-backed too. `SearchField` owns simple search
|
||||
input chrome, clear affordance, keyboard forwarding, focus handling, aria
|
||||
labels, and trailing-control padding, while `SearchInput` owns resource-list
|
||||
|
|
|
|||
|
|
@ -3600,7 +3600,7 @@
|
|||
{
|
||||
"id": "form-textarea-local-alert-fields",
|
||||
"category": "form-control",
|
||||
"summary": "Alert destination and incident timeline textareas must compose FormTextarea; only the resource note editors remain as bounded residuals until their editor-specific migration.",
|
||||
"summary": "Alert textareas must compose FormTextarea so labels, help text, controlled values, and multiline field styling stay canonical.",
|
||||
"canonical": {
|
||||
"path": "src/components/shared/FormTextarea.tsx",
|
||||
"export": "FormTextarea"
|
||||
|
|
@ -3608,11 +3608,8 @@
|
|||
"scopes": ["src/components/Alerts", "src/features/alerts"],
|
||||
"extensions": [".tsx"],
|
||||
"allPatterns": ["<textarea"],
|
||||
"legacyReason": "Bounded residual. Alert resource note editors still own row/mobile-specific editing behavior; new alert textareas must use FormTextarea.",
|
||||
"allowedPaths": [
|
||||
"src/components/Alerts/AlertResourceTableMobile.tsx",
|
||||
"src/components/Alerts/AlertResourceTableRow.tsx"
|
||||
],
|
||||
"legacyReason": "Retired migration debt. Alert multiline fields belong to FormTextarea so row, mobile, destination, and timeline editors use the same labelled primitive.",
|
||||
"allowedPaths": [],
|
||||
"ignoredPaths": [
|
||||
"src/components/Alerts/EmailProviderSelect.test.tsx",
|
||||
"src/components/shared/SharedPrimitives.guardrails.test.ts"
|
||||
|
|
@ -3625,7 +3622,7 @@
|
|||
{
|
||||
"id": "form-textarea-local-settings-fields",
|
||||
"category": "form-control",
|
||||
"summary": "Settings textareas must compose FormTextarea; the SSO provider editor remains as bounded residual until its multi-field provider metadata editor is migrated.",
|
||||
"summary": "Settings textareas must compose FormTextarea so labels, help text, controlled values, and multiline field styling stay canonical.",
|
||||
"canonical": {
|
||||
"path": "src/components/shared/FormTextarea.tsx",
|
||||
"export": "FormTextarea"
|
||||
|
|
@ -3633,8 +3630,8 @@
|
|||
"scopes": ["src/components/Settings"],
|
||||
"extensions": [".tsx"],
|
||||
"allPatterns": ["<textarea"],
|
||||
"legacyReason": "Bounded residual. SSO provider metadata textareas still require a dedicated editor migration; new settings textareas must use FormTextarea.",
|
||||
"allowedPaths": ["src/components/Settings/SSOProvidersPanel.tsx"],
|
||||
"legacyReason": "Retired migration debt. Settings multiline fields belong to FormTextarea, including provider metadata and access mapping editors.",
|
||||
"allowedPaths": [],
|
||||
"ignoredPaths": [
|
||||
"src/components/Settings/__tests__/ProLicensePanel.test.tsx",
|
||||
"src/components/shared/SharedPrimitives.guardrails.test.ts"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { For, Show } from 'solid-js';
|
|||
import RotateCcw from 'lucide-solid/icons/rotate-ccw';
|
||||
|
||||
import { Card } from '@/components/shared/Card';
|
||||
import { FormTextarea } from '@/components/shared/FormTextarea';
|
||||
import { TogglePrimitive } from '@/components/shared/Toggle';
|
||||
import { AlertResourceGroupHeader } from './AlertResourceGroupHeader';
|
||||
import {
|
||||
|
|
@ -259,7 +260,12 @@ export function AlertResourceTableMobile(props: AlertResourceTableMobileProps) {
|
|||
class="p-1.5 bg-blue-50 dark:bg-blue-900 text-blue-600 rounded"
|
||||
aria-label={`Edit thresholds for ${getAlertResourceLabel(resource)}`}
|
||||
>
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg
|
||||
class="w-4 h-4"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
|
|
@ -276,7 +282,12 @@ export function AlertResourceTableMobile(props: AlertResourceTableMobileProps) {
|
|||
class="p-1.5 bg-surface-hover text-slate-600 rounded"
|
||||
aria-label="Cancel threshold edits"
|
||||
>
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg
|
||||
class="w-4 h-4"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
|
|
@ -291,7 +302,12 @@ export function AlertResourceTableMobile(props: AlertResourceTableMobileProps) {
|
|||
class="p-1.5 bg-green-50 dark:bg-green-900 text-green-600 rounded"
|
||||
aria-label={`Save threshold edits for ${getAlertResourceLabel(resource)}`}
|
||||
>
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg
|
||||
class="w-4 h-4"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
|
|
@ -321,8 +337,11 @@ export function AlertResourceTableMobile(props: AlertResourceTableMobileProps) {
|
|||
</div>
|
||||
|
||||
<Show when={isEditing()}>
|
||||
<textarea
|
||||
class="w-full text-xs p-2 rounded border border-border bg-surface-alt"
|
||||
<FormTextarea
|
||||
label="Override note"
|
||||
labelClass="sr-only"
|
||||
fieldBaseClass="w-full"
|
||||
textareaBaseClass="w-full text-xs p-2 rounded border border-border bg-surface-alt"
|
||||
rows={2}
|
||||
placeholder={getAlertResourceTableEditNotePlaceholder()}
|
||||
value={props.table.editingNote()}
|
||||
|
|
@ -368,17 +387,13 @@ export function AlertResourceTableMobile(props: AlertResourceTableMobileProps) {
|
|||
min={bounds.min}
|
||||
max={bounds.max}
|
||||
value={thresholds()?.[metric] ?? ''}
|
||||
placeholder={getAlertResourceTableMetricPlaceholder(
|
||||
isDisabled(),
|
||||
)}
|
||||
placeholder={getAlertResourceTableMetricPlaceholder(isDisabled())}
|
||||
class="w-16 text-right text-xs p-1 rounded border border-border bg-surface"
|
||||
onInput={(e) => {
|
||||
const nextValue = parseFloat(e.currentTarget.value);
|
||||
props.table.setEditingThresholds({
|
||||
...props.table.editingThresholds(),
|
||||
[metric]: Number.isNaN(nextValue)
|
||||
? undefined
|
||||
: nextValue,
|
||||
[metric]: Number.isNaN(nextValue) ? undefined : nextValue,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { For, Show } from 'solid-js';
|
||||
import RotateCcw from 'lucide-solid/icons/rotate-ccw';
|
||||
|
||||
import { FormTextarea } from '@/components/shared/FormTextarea';
|
||||
import { TogglePrimitive } from '@/components/shared/Toggle';
|
||||
import { StatusBadge } from '@/components/shared/StatusBadge';
|
||||
import { ThresholdSlider } from '@/components/Workloads/ThresholdSlider';
|
||||
|
|
@ -305,19 +306,17 @@ export function AlertResourceTableRow(props: AlertResourceTableRowProps) {
|
|||
</Show>
|
||||
</div>
|
||||
<Show when={isEditing()}>
|
||||
<div class="mt-2 w-full">
|
||||
<label class="sr-only" for={`note-${props.resource.id}`}>
|
||||
Override note
|
||||
</label>
|
||||
<textarea
|
||||
id={`note-${props.resource.id}`}
|
||||
class="w-full rounded border border-border bg-surface px-2 py-1 text-xs text-base-content focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||||
rows={2}
|
||||
placeholder={getAlertResourceTableOverrideNotePlaceholder()}
|
||||
value={props.editingNote()}
|
||||
onInput={(e) => props.setEditingNote(e.currentTarget.value)}
|
||||
/>
|
||||
</div>
|
||||
<FormTextarea
|
||||
id={`note-${props.resource.id}`}
|
||||
label="Override note"
|
||||
labelClass="sr-only"
|
||||
fieldBaseClass="mt-2 w-full"
|
||||
textareaBaseClass="w-full rounded border border-border bg-surface px-2 py-1 text-xs text-base-content focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||||
rows={2}
|
||||
placeholder={getAlertResourceTableOverrideNotePlaceholder()}
|
||||
value={props.editingNote()}
|
||||
onInput={(e) => props.setEditingNote(e.currentTarget.value)}
|
||||
/>
|
||||
</Show>
|
||||
<Show when={!isEditing() && props.resource.note}>
|
||||
<p class="mt-2 text-xs italic text-muted break-words">{props.resource.note as string}</p>
|
||||
|
|
|
|||
|
|
@ -293,9 +293,13 @@ describe('ResourceTable', () => {
|
|||
expect(alertResourceTableDesktopSource).toContain('AlertResourceGroupHeader');
|
||||
expect(alertResourceTableMobileSource).toContain('export function AlertResourceTableMobile');
|
||||
expect(alertResourceTableMobileSource).toContain('AlertResourceGroupHeader');
|
||||
expect(alertResourceTableMobileSource).toContain('FormTextarea');
|
||||
expect(alertResourceTableMobileSource).not.toContain(['<', 'textarea'].join(''));
|
||||
expect(alertResourceGroupHeaderSource).toContain('export function AlertResourceGroupHeader');
|
||||
expect(alertResourceTableRowSource).toContain('export function AlertResourceTableRow');
|
||||
expect(alertResourceTableRowSource).toContain('alertResourceSupportsMetric');
|
||||
expect(alertResourceTableRowSource).toContain('FormTextarea');
|
||||
expect(alertResourceTableRowSource).not.toContain(['<', 'textarea'].join(''));
|
||||
expect(alertResourceTableStateSource).toContain('export function useAlertResourceTableState');
|
||||
expect(alertResourceTableModelSource).toContain(
|
||||
'export function normalizeAlertResourceMetricKey',
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { Component, For, Show } from 'solid-js';
|
|||
import SettingsPanel from '@/components/shared/SettingsPanel';
|
||||
import { Toggle } from '@/components/shared/Toggle';
|
||||
import { formField, labelClass, controlClass, formHelpText } from '@/components/shared/Form';
|
||||
import { FormTextarea } from '@/components/shared/FormTextarea';
|
||||
import { Dialog } from '@/components/shared/Dialog';
|
||||
import Plus from 'lucide-solid/icons/plus';
|
||||
import Pencil from 'lucide-solid/icons/pencil';
|
||||
|
|
@ -132,14 +133,20 @@ export const SSOProvidersPanel: Component<SSOProvidersPanelProps> = (props) => {
|
|||
</div>
|
||||
<div class="min-w-0">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-medium text-base-content truncate" title={provider.name}>
|
||||
<span
|
||||
class="font-medium text-base-content truncate"
|
||||
title={provider.name}
|
||||
>
|
||||
{provider.name}
|
||||
</span>
|
||||
<span class={getSSOProviderTypeBadgeClass()}>
|
||||
{getSSOProviderTypeLabel(provider.type)}
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-xs text-muted truncate" title={getSSOProviderSummary(provider)}>
|
||||
<p
|
||||
class="text-xs text-muted truncate"
|
||||
title={getSSOProviderSummary(provider)}
|
||||
>
|
||||
{getSSOProviderSummary(provider)}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -393,16 +400,14 @@ export const SSOProvidersPanel: Component<SSOProvidersPanelProps> = (props) => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class={formField}>
|
||||
<label class={labelClass()}>IdP Certificate (PEM)</label>
|
||||
<textarea
|
||||
rows={4}
|
||||
value={form.samlIdpCertificate}
|
||||
onInput={(e) => setForm('samlIdpCertificate', e.currentTarget.value)}
|
||||
placeholder="-----BEGIN CERTIFICATE----- ... -----END CERTIFICATE-----"
|
||||
class={controlClass('font-mono text-xs')}
|
||||
/>
|
||||
</div>
|
||||
<FormTextarea
|
||||
label="IdP Certificate (PEM)"
|
||||
rows={4}
|
||||
value={form.samlIdpCertificate}
|
||||
onInput={(e) => setForm('samlIdpCertificate', e.currentTarget.value)}
|
||||
placeholder="-----BEGIN CERTIFICATE----- ... -----END CERTIFICATE-----"
|
||||
textareaBaseClass={controlClass('font-mono text-xs')}
|
||||
/>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div class={formField}>
|
||||
|
|
@ -470,7 +475,10 @@ export const SSOProvidersPanel: Component<SSOProvidersPanelProps> = (props) => {
|
|||
<Show when={testResult()?.details?.entityId}>
|
||||
<div class="flex gap-2">
|
||||
<dt class="text-muted">Entity ID:</dt>
|
||||
<dd class="text-base-content truncate" title={testResult()?.details?.entityId}>
|
||||
<dd
|
||||
class="text-base-content truncate"
|
||||
title={testResult()?.details?.entityId}
|
||||
>
|
||||
{testResult()?.details?.entityId}
|
||||
</dd>
|
||||
</div>
|
||||
|
|
@ -478,7 +486,10 @@ export const SSOProvidersPanel: Component<SSOProvidersPanelProps> = (props) => {
|
|||
<Show when={testResult()?.details?.ssoUrl}>
|
||||
<div class="flex gap-2">
|
||||
<dt class="text-muted">SSO URL:</dt>
|
||||
<dd class="text-base-content truncate" title={testResult()?.details?.ssoUrl}>
|
||||
<dd
|
||||
class="text-base-content truncate"
|
||||
title={testResult()?.details?.ssoUrl}
|
||||
>
|
||||
{testResult()?.details?.ssoUrl}
|
||||
</dd>
|
||||
</div>
|
||||
|
|
@ -486,7 +497,10 @@ export const SSOProvidersPanel: Component<SSOProvidersPanelProps> = (props) => {
|
|||
<Show when={testResult()?.details?.tokenEndpoint}>
|
||||
<div class="flex gap-2">
|
||||
<dt class="text-muted">Token Endpoint:</dt>
|
||||
<dd class="text-base-content truncate" title={testResult()?.details?.tokenEndpoint}>
|
||||
<dd
|
||||
class="text-base-content truncate"
|
||||
title={testResult()?.details?.tokenEndpoint}
|
||||
>
|
||||
{testResult()?.details?.tokenEndpoint}
|
||||
</dd>
|
||||
</div>
|
||||
|
|
@ -569,49 +583,41 @@ export const SSOProvidersPanel: Component<SSOProvidersPanelProps> = (props) => {
|
|||
</div>
|
||||
</Show>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class={formField}>
|
||||
<label class={labelClass()}>Allowed Groups</label>
|
||||
<textarea
|
||||
rows={2}
|
||||
value={form.allowedGroups}
|
||||
onInput={(e) => setForm('allowedGroups', e.currentTarget.value)}
|
||||
placeholder="admin, sso-users"
|
||||
class={controlClass('min-h-[60px]')}
|
||||
/>
|
||||
<p class={formHelpText}>Comma-separated. Empty allows all.</p>
|
||||
</div>
|
||||
<div class={formField}>
|
||||
<label class={labelClass()}>Allowed Domains</label>
|
||||
<textarea
|
||||
rows={2}
|
||||
value={form.allowedDomains}
|
||||
onInput={(e) => setForm('allowedDomains', e.currentTarget.value)}
|
||||
placeholder="example.com, corp.io"
|
||||
class={controlClass('min-h-[60px]')}
|
||||
/>
|
||||
<p class={formHelpText}>Email domains (without @)</p>
|
||||
</div>
|
||||
<div class={formField}>
|
||||
<label class={labelClass()}>Allowed Emails</label>
|
||||
<textarea
|
||||
rows={2}
|
||||
value={form.allowedEmails}
|
||||
onInput={(e) => setForm('allowedEmails', e.currentTarget.value)}
|
||||
placeholder={ALERT_EMAIL_REPLY_TO_PLACEHOLDER}
|
||||
class={controlClass('min-h-[60px]')}
|
||||
/>
|
||||
</div>
|
||||
<div class={formField}>
|
||||
<label class={labelClass()}>Group Role Mappings</label>
|
||||
<textarea
|
||||
rows={2}
|
||||
value={form.groupRoleMappings}
|
||||
onInput={(e) => setForm('groupRoleMappings', e.currentTarget.value)}
|
||||
placeholder="admins=admin, ops=operator"
|
||||
class={controlClass('min-h-[60px]')}
|
||||
/>
|
||||
<p class={formHelpText}>Format: group=roleId</p>
|
||||
</div>
|
||||
<FormTextarea
|
||||
label="Allowed Groups"
|
||||
rows={2}
|
||||
value={form.allowedGroups}
|
||||
onInput={(e) => setForm('allowedGroups', e.currentTarget.value)}
|
||||
placeholder="admin, sso-users"
|
||||
textareaBaseClass={controlClass('min-h-[60px]')}
|
||||
help="Comma-separated. Empty allows all."
|
||||
/>
|
||||
<FormTextarea
|
||||
label="Allowed Domains"
|
||||
rows={2}
|
||||
value={form.allowedDomains}
|
||||
onInput={(e) => setForm('allowedDomains', e.currentTarget.value)}
|
||||
placeholder="example.com, corp.io"
|
||||
textareaBaseClass={controlClass('min-h-[60px]')}
|
||||
help="Email domains (without @)"
|
||||
/>
|
||||
<FormTextarea
|
||||
label="Allowed Emails"
|
||||
rows={2}
|
||||
value={form.allowedEmails}
|
||||
onInput={(e) => setForm('allowedEmails', e.currentTarget.value)}
|
||||
placeholder={ALERT_EMAIL_REPLY_TO_PLACEHOLDER}
|
||||
textareaBaseClass={controlClass('min-h-[60px]')}
|
||||
/>
|
||||
<FormTextarea
|
||||
label="Group Role Mappings"
|
||||
rows={2}
|
||||
value={form.groupRoleMappings}
|
||||
onInput={(e) => setForm('groupRoleMappings', e.currentTarget.value)}
|
||||
placeholder="admins=admin, ops=operator"
|
||||
textareaBaseClass={controlClass('min-h-[60px]')}
|
||||
help="Format: group=roleId"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
|
|
|
|||
|
|
@ -523,6 +523,8 @@ describe('settings architecture guardrails', () => {
|
|||
expect(ssoProvidersPanelSource).toContain("openAddModal('saml')");
|
||||
expect(ssoProvidersPanelSource).toContain('getSSOProviderAddButtonLabel');
|
||||
expect(ssoProvidersPanelSource).toContain('Groups Claim');
|
||||
expect(ssoProvidersPanelSource).toContain('FormTextarea');
|
||||
expect(ssoProvidersPanelSource).not.toContain(['<', 'textarea'].join(''));
|
||||
expect(ssoProvidersPanelSource).toContain(
|
||||
'Claim used for OIDC allowed groups and role mappings.',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -202,6 +202,8 @@ import alertOverviewStatsCardsSource from '@/features/alerts/AlertOverviewStatsC
|
|||
import alertHistoryTableSectionSource from '@/features/alerts/AlertHistoryTableSection.tsx?raw';
|
||||
import alertHistoryTableGroupRowSource from '@/features/alerts/AlertHistoryTableGroupRow.tsx?raw';
|
||||
import alertResourceTableDesktopSource from '@/components/Alerts/AlertResourceTableDesktop.tsx?raw';
|
||||
import alertResourceTableMobileSource from '@/components/Alerts/AlertResourceTableMobile.tsx?raw';
|
||||
import alertResourceTableRowSource from '@/components/Alerts/AlertResourceTableRow.tsx?raw';
|
||||
import aiCostDashboardSource from '@/components/AI/AICostDashboard.tsx?raw';
|
||||
import resourceDetailSummarySource from '@/components/Infrastructure/ResourceDetailSummary.tsx?raw';
|
||||
import resourceDetailDrawerSource from '@/components/Infrastructure/ResourceDetailDrawer.tsx?raw';
|
||||
|
|
@ -233,6 +235,7 @@ import reportingPanelSource from '@/components/Settings/ReportingPanel.tsx?raw';
|
|||
import rolesPanelSource from '@/components/Settings/RolesPanel.tsx?raw';
|
||||
import securityAuthPanelSource from '@/components/Settings/SecurityAuthPanel.tsx?raw';
|
||||
import securityOverviewPanelSource from '@/components/Settings/SecurityOverviewPanel.tsx?raw';
|
||||
import ssoProvidersPanelSource from '@/components/Settings/SSOProvidersPanel.tsx?raw';
|
||||
import updatesSettingsPanelSource from '@/components/Settings/UpdatesSettingsPanel.tsx?raw';
|
||||
import userAssignmentsPanelSource from '@/components/Settings/UserAssignmentsPanel.tsx?raw';
|
||||
import infrastructureSourcePickerSource from '@/components/Settings/InfrastructureSourcePicker.tsx?raw';
|
||||
|
|
@ -560,15 +563,12 @@ describe('shared primitive guardrails', () => {
|
|||
expect(alertGuard?.canonical?.export).toBe('FormTextarea');
|
||||
expect(alertGuard?.allPatterns).toEqual(['<textarea']);
|
||||
expect(alertGuard?.scopes).toEqual(['src/components/Alerts', 'src/features/alerts']);
|
||||
expect(alertGuard?.allowedPaths).toEqual([
|
||||
'src/components/Alerts/AlertResourceTableMobile.tsx',
|
||||
'src/components/Alerts/AlertResourceTableRow.tsx',
|
||||
]);
|
||||
expect(alertGuard?.allowedPaths ?? []).toHaveLength(0);
|
||||
expect(settingsGuard?.canonical?.path).toBe('src/components/shared/FormTextarea.tsx');
|
||||
expect(settingsGuard?.canonical?.export).toBe('FormTextarea');
|
||||
expect(settingsGuard?.allPatterns).toEqual(['<textarea']);
|
||||
expect(settingsGuard?.scopes).toEqual(['src/components/Settings']);
|
||||
expect(settingsGuard?.allowedPaths).toEqual(['src/components/Settings/SSOProvidersPanel.tsx']);
|
||||
expect(settingsGuard?.allowedPaths ?? []).toHaveLength(0);
|
||||
expect(infrastructureGuard?.canonical?.path).toBe('src/components/shared/FormTextarea.tsx');
|
||||
expect(infrastructureGuard?.canonical?.export).toBe('FormTextarea');
|
||||
expect(infrastructureGuard?.allPatterns).toEqual(['<textarea']);
|
||||
|
|
@ -597,8 +597,11 @@ describe('shared primitive guardrails', () => {
|
|||
webhookConfigFormSource,
|
||||
reportMergeModalSource,
|
||||
agentProfilesPanelSource,
|
||||
alertResourceTableMobileSource,
|
||||
alertResourceTableRowSource,
|
||||
selfHostedCommercialRecoverySectionSource,
|
||||
suggestProfileModalSource,
|
||||
ssoProvidersPanelSource,
|
||||
alertAppriseDestinationsSectionSource,
|
||||
];
|
||||
for (const source of migratedConsumers) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue