Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/ui/components/ConditionalBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,14 @@ export function ConditionalBuilder(props: ConditionalBuilderProps): VNode {
};

return (
<div>
<div role="group" aria-label="Conditional rule builder">
{/* IF row */}
<div class="wl-conditionalRow" style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
<span style="font-weight:700;font-size:12px;min-width:36px">IF</span>
<span style="font-weight:700;font-size:12px;min-width:36px" id="wl-cond-if-label">IF</span>

<select
class="wl-select"
aria-labelledby="wl-cond-if-label"
value={current.field}
onChange={(e) => update({ field: (e.currentTarget as HTMLSelectElement).value })}
>
Expand Down
6 changes: 4 additions & 2 deletions src/ui/components/DataDiffView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,15 @@ export function DataDiffView(props: {
};

return (
<div style="min-width:0;overflow:hidden">
<div style="min-width:0;overflow:hidden" role="region" aria-label="Data comparison results">
<div class="wl-row" style="gap:8px;margin-bottom:8px;align-items:center;flex-wrap:wrap">
<div class="wl-chipRow">
<div class="wl-chipRow" role="tablist" aria-label="Filter diff results">
{(['all', 'added', 'removed', 'changed'] as FilterTab[]).map(tab => (
<button
key={tab}
class="wl-chip"
role="tab"
aria-selected={filter === tab}
data-active={filter === tab}
onClick={() => { setFilter(tab); setPage(0); }}
style={filter === tab ? 'background:var(--wl-accent);color:white' : ''}
Expand Down
10 changes: 5 additions & 5 deletions src/ui/components/DryRunPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ export function DryRunPanel(props: {
}

return (
<div class="wl-card">
<div class="wl-card" role="region" aria-label="Dry run results" aria-live="polite">
<div class="wl-cardHeader">
<h2>Dry Run {allPass ? '✓' : ''}</h2>
<div class="wl-actions">
<button class="wl-btn" onClick={exportCsv} disabled={report.total === 0}>Export CSV</button>
<button class="wl-btn" onClick={exportJson} disabled={report.total === 0}>Export JSON</button>
<button class="wl-btn" onClick={onClose}>Dismiss</button>
<div class="wl-actions" role="toolbar" aria-label="Dry run actions">
<button class="wl-btn" onClick={exportCsv} disabled={report.total === 0} aria-label="Export dry run report as CSV">Export CSV</button>
<button class="wl-btn" onClick={exportJson} disabled={report.total === 0} aria-label="Export dry run report as JSON">Export JSON</button>
<button class="wl-btn" onClick={onClose} aria-label="Dismiss dry run panel">Dismiss</button>
</div>
</div>

Expand Down
4 changes: 2 additions & 2 deletions src/ui/components/DuplicateGroupView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ export function DuplicateGroupView(props: DuplicateGroupViewProps): VNode {
const indexes = getGroupIndexes(group);

return (
<div class="wl-dupGroup">
<div class="wl-dupGroup" role="region" aria-label={`Duplicate group ${group.masterIndex} resolution`}>
<div class="wl-tableWrap">
<table class="wl-dupGroupTable">
<table class="wl-dupGroupTable" aria-label="Field-level merge selection table">
<thead>
<tr>
<th style="padding:4px 8px;border-bottom:1px solid var(--wl-line-2);font-size:11px;text-transform:uppercase;letter-spacing:0.5px;color:var(--wl-ink-dim)">
Expand Down
4 changes: 2 additions & 2 deletions src/ui/components/FieldAnalyticsChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ export function FieldAnalyticsChart(props: FieldAnalyticsChartProps): VNode {
}

return (
<div class="wl-analyticsChart">
<div class="wl-analyticsChart" role="img" aria-label={`Field analytics chart showing ${metrics.length} fields`}>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep field metrics exposed to assistive technology

For screen-reader users, role="img" makes this entire subtree a single image whose accessible name is only “Field analytics chart showing N fields”; the descendant field names, population percentages, uniqueness rates, and distinct counts are treated as image contents rather than readable data. Since the surrounding screen does not expose those per-field metrics elsewhere, remove the image role or provide an alternative that includes every metric.

Useful? React with 👍 / 👎.

{/* Header row */}
<div class="wl-analyticsRow" style="font-weight:900;font-size:11px;text-transform:uppercase;letter-spacing:0.5px;color:var(--wl-ink-dim)">
<div class="wl-analyticsRow" style="font-weight:900;font-size:11px;text-transform:uppercase;letter-spacing:0.5px;color:var(--wl-ink-dim)" aria-hidden="true">
<span>Field</span>
<span>Population Rate</span>
<span style="text-align:right">%</span>
Expand Down
4 changes: 2 additions & 2 deletions src/ui/components/FieldDiffDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ export function FieldDiffDetail(props: FieldDiffDetailProps): VNode {
const { diff } = props;

return (
<div class="wl-diffRow" data-status={diff.status}>
<div class="wl-diffRow" data-status={diff.status} role="listitem" aria-label={`Field ${diff.name}: ${diff.status}`}>
<span class="wl-mono" style="font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title={diff.name}>
{diff.name}
</span>

<span class="wl-diffBadge" data-status={diff.status}>
<span class="wl-diffBadge" data-status={diff.status} aria-label={`Status: ${diff.status}`}>
{diff.status}
</span>

Expand Down
2 changes: 1 addition & 1 deletion src/ui/components/FieldGeneratorConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function FieldGeneratorConfig(props: FieldGeneratorConfigProps): VNode {
}

return (
<div class="wl-fieldConfigRow">
<div class="wl-fieldConfigRow" role="group" aria-label={`Generator config for ${config.fieldName}`}>
<div style="display:flex;flex-direction:column;gap:2px;min-width:0">
<span class="wl-mono" style="font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title={config.fieldName}>
{config.fieldName}
Expand Down
2 changes: 1 addition & 1 deletion src/ui/components/FieldRecommendations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function FieldRecommendations(props: FieldRecommendationsProps): VNode {
}

return (
<ul class="wl-recommendations">
<ul class="wl-recommendations" role="list" aria-label="Field quality recommendations">
{recommendations.map((rec, i) => (
<li key={i}>{rec}</li>
))}
Expand Down
7 changes: 5 additions & 2 deletions src/ui/components/MergeWizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,14 +133,17 @@ export function MergeWizard(props: MergeWizardProps): VNode {
}

return (
<div class="wl-card">
<div class="wl-card" role="region" aria-label="Merge wizard">
<div class="wl-cardHeader">
<h2>Merge Wizard</h2>
<div class="wl-actions">
<div class="wl-actions" role="tablist" aria-label="Wizard steps">
{STEPS.map((label, i) => (
<span
key={label}
class="wl-chip"
role="tab"
aria-selected={i === step}
aria-current={i === step ? 'step' : undefined}
style={i === step ? 'border-color:var(--wl-accent);font-weight:900' : ''}
>
{i + 1}. {label}
Expand Down
3 changes: 2 additions & 1 deletion src/ui/components/MigrationProgressDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,14 @@ export function MigrationProgressDashboard(props: {
const barColor = status === 'error' ? 'var(--wl-danger)' : progress.done ? 'var(--wl-success)' : 'var(--wl-accent)';

return (
<div class="wl-card">
<div class="wl-card" role="region" aria-label="Migration progress dashboard" aria-live="polite">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Limit live announcements to meaningful progress changes

While a push is processing, DataPushScreen updates nowTs every second, changing the elapsed and ETA text inside this region even when no records have completed. Applying aria-live="polite" to the whole dashboard therefore produces continuous announcements or an ever-changing announcement queue for screen-reader users; restrict the live region to a throttled status/progress message rather than the per-second dashboard.

Useful? React with 👍 / 👎.

<div class="wl-cardHeader">
<h2>Migration Progress</h2>
<div style="display:flex;align-items:center;gap:10px">
<span
class="wl-pill"
style={`color:${badge.color};border-color:${badge.color}55`}
aria-label={`Status: ${badge.label}`}
>
{badge.label}
</span>
Expand Down
2 changes: 1 addition & 1 deletion src/ui/components/OrgPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function OrgPicker(props: {
}

return (
<div class="wl-orgPicker">
<div class="wl-orgPicker" role="group" aria-label="Organization picker for data comparison">
<div style="flex:1">
<label htmlFor="compare-source-org" class="wl-muted" style="font-size:11px;font-weight:700;text-transform:uppercase;display:block;margin-bottom:4px">Source Org</label>
<select
Expand Down
4 changes: 3 additions & 1 deletion src/ui/components/QualityRuleEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ export function QualityRuleEditor(props: QualityRuleEditorProps): VNode {
}

return (
<div class="wl-card" style="display:flex;flex-direction:column;gap:12px">
<div class="wl-card" style="display:flex;flex-direction:column;gap:12px" role="region" aria-label="Quality rules editor">
<div class="wl-cardHeader">
<h2>Quality Rules</h2>
<span class="wl-muted">{rules.length} rule{rules.length !== 1 ? 's' : ''}</span>
Expand All @@ -193,6 +193,8 @@ export function QualityRuleEditor(props: QualityRuleEditorProps): VNode {
<div
key={rule.id}
class="wl-card"
role="listitem"
aria-label={`Quality rule ${index + 1}: ${rule.field || 'unconfigured'}`}
style="padding:12px;display:flex;flex-direction:column;gap:10px;border:1px solid var(--wl-line-2);border-radius:8px"
>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
Expand Down
4 changes: 2 additions & 2 deletions src/ui/components/QualityScorecard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ export function QualityScorecard(props: QualityScorecardProps): VNode {
breakdowns.sort((a, b) => a.passRate - b.passRate);

return (
<div class="wl-card" style="display:flex;flex-direction:column;gap:16px">
<div class="wl-card" style="display:flex;flex-direction:column;gap:16px" role="region" aria-label="Data quality scorecard">
<div class="wl-cardHeader">
<h2>Quality Scorecard</h2>
</div>

{/* Score badge */}
<div style="display:flex;align-items:center;justify-content:center;padding:16px 0">
<div style="display:flex;align-items:center;justify-content:center;padding:16px 0" role="img" aria-label={`Overall quality score: ${result.score} out of 100`}>
<div
style={`
width:100px;height:100px;border-radius:50%;
Expand Down
6 changes: 3 additions & 3 deletions src/ui/components/QueryExplainPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ export function QueryExplainPanel(props: {
}

return (
<div class="wl-card">
<div class="wl-card" role="region" aria-label="Query explain plan">
<div class="wl-cardHeader">
<h2>Explain Plan</h2>
<div class="wl-actions">
<button class="wl-btn wl-btnPrimary" style="font-size:11px;padding:5px 14px" onClick={runExplain} disabled={loading || !soql.trim()}>
<div class="wl-actions" role="toolbar" aria-label="Explain plan actions">
<button class="wl-btn wl-btnPrimary" style="font-size:11px;padding:5px 14px" onClick={runExplain} disabled={loading || !soql.trim()} aria-label="Analyze SOQL query execution plan">
{loading ? 'Analyzing...' : 'Analyze'}
</button>
</div>
Expand Down
8 changes: 4 additions & 4 deletions src/ui/components/QueryMetricsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,16 @@ export function QueryMetricsPanel(props: QueryMetricsPanelProps): VNode {
const displayed = viewAll ? sorted : sorted.slice(0, 5);

return (
<div class="wl-card" style="display:flex;flex-direction:column;gap:10px">
<div class="wl-card" style="display:flex;flex-direction:column;gap:10px" role="region" aria-label="Query metrics panel">
<div class="wl-cardHeader">
<h2>Query Metrics</h2>
<div class="wl-actions">
<div class="wl-actions" role="toolbar" aria-label="Query metrics actions">
{metrics.length > 5 ? (
<button class="wl-btn" onClick={() => setViewAll(!viewAll)}>
<button class="wl-btn" onClick={() => setViewAll(!viewAll)} aria-expanded={viewAll} aria-label={viewAll ? 'Show recent queries only' : `View all ${metrics.length} queries`}>
{viewAll ? 'Show Recent' : `View All (${metrics.length})`}
</button>
) : null}
<button class="wl-btn" onClick={onClear} disabled={metrics.length === 0}>
<button class="wl-btn" onClick={onClear} disabled={metrics.length === 0} aria-label="Clear query metrics">
Clear
</button>
</div>
Expand Down
6 changes: 3 additions & 3 deletions src/ui/components/RelationshipConfig.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/**
/**
* Configuration component for lookup relationship fields.
*
* Provides a textarea for entering lookup record IDs (one per line)
Expand Down Expand Up @@ -41,8 +41,7 @@ export function RelationshipConfig(props: RelationshipConfigProps): VNode {
</span>
</div>

<textarea
class="wl-textarea"
<textarea class="wl-textarea" aria-label={`Lookup record IDs for ${fieldName}`}
style="min-height:80px;font-size:12px"
placeholder={"Enter lookup record IDs (one per line):\n001xx000003abc1\n001xx000003abc2\n001xx000003abc3"}
value={lookupValues.join('\n')}
Expand All @@ -57,3 +56,4 @@ export function RelationshipConfig(props: RelationshipConfigProps): VNode {
</div>
);
}

11 changes: 4 additions & 7 deletions src/ui/components/RelationshipTree.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/**
/**
* Tree of objects with checkboxes and edge labels for the clone wizard.
*
* What this file does:
Expand Down Expand Up @@ -69,7 +69,7 @@ export function RelationshipTree(props: RelationshipTreeProps): VNode {
}, [graph.edges]);

return (
<div class="wl-relationshipTree">
<div class="wl-relationshipTree" role="tree" aria-label="Object relationship tree">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Implement the tree contract before advertising a tree

When assistive technology encounters this container, it expects owned treeitem elements and tree keyboard navigation, but every node below it remains a plain div containing a checkbox and there are no arrow-key handlers anywhere in this component. This exposes a tree with no semantic items while focus still advances using ordinary Tab behavior; either retain list/group semantics for the checkbox UI or add proper treeitems and the corresponding keyboard model.

Useful? React with 👍 / 👎.

{nodeNames.length === 0 && (
<div class="wl-muted" style="padding:8px">
No objects in the relationship graph.
Expand All @@ -85,11 +85,7 @@ export function RelationshipTree(props: RelationshipTreeProps): VNode {
{/* Node */}
<div class="wl-relNode" data-cycle={inCycle ? 'true' : undefined}>
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;flex:1">
<input
type="checkbox"
checked={selectedObjects.has(name)}
onChange={() => onToggleObject(name)}
style="accent-color:var(--wl-accent)"
<input type="checkbox" checked={selectedObjects.has(name)} onChange={() => onToggleObject(name)} style="accent-color:var(--wl-accent)" aria-label={`Select ${name} for cloning`}
/>
<span style="font-weight:700;font-size:13px">{name}</span>
</label>
Expand Down Expand Up @@ -129,3 +125,4 @@ export function RelationshipTree(props: RelationshipTreeProps): VNode {
</div>
);
}

9 changes: 5 additions & 4 deletions src/ui/components/SchemaDiffView.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/**
/**
* Scrollable list view of schema diffs with filter toolbar and summary stats.
*
* Renders a toolbar with all/added/removed/changed toggle buttons,
Expand Down Expand Up @@ -48,7 +48,7 @@ export function SchemaDiffView(props: SchemaDiffViewProps): VNode {
];

return (
<div class="wl-diffView">
<div class="wl-diffView" role="region" aria-label="Schema diff results">
{/* Summary stats */}
<div class="wl-chipRow" style="padding:8px 0">
<span class="wl-chip">
Expand All @@ -66,7 +66,7 @@ export function SchemaDiffView(props: SchemaDiffViewProps): VNode {
<button
key={f.mode}
class="wl-btn"
data-active={filter === f.mode ? 'true' : 'false'}
data-active={filter === f.mode ? 'true' : 'false'} aria-pressed={filter === f.mode}
onClick={() => setFilter(f.mode)}
style="font-size:11px;padding:5px 10px"
>
Expand All @@ -76,7 +76,7 @@ export function SchemaDiffView(props: SchemaDiffViewProps): VNode {
</div>

{/* Diff list */}
<div style="max-height:520px;overflow-y:auto">
<div style="max-height:520px;overflow-y:auto" aria-live="polite">
{visibleDiffs.length > 0 ? (
visibleDiffs.map((d) => (
<FieldDiffDetail key={d.name} diff={d} />
Expand All @@ -88,3 +88,4 @@ export function SchemaDiffView(props: SchemaDiffViewProps): VNode {
</div>
);
}

13 changes: 7 additions & 6 deletions src/ui/components/ShortcutEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/**
/**
* Keyboard shortcut editor component.
*
* Displays a table of all registered shortcuts with their default and current
Expand Down Expand Up @@ -92,10 +92,10 @@ export function ShortcutEditor(props: { sf: SfApi }): VNode {
};

return (
<div class="wl-shortcutEditor">
<div class="wl-shortcutEditor" role="region" aria-label="Keyboard shortcut editor">
<h3>Keyboard Shortcuts</h3>
{error && <div class="wl-shortcutError">{error}</div>}
<table class="wl-shortcutTable">
<table class="wl-shortcutTable" aria-label="Keyboard shortcuts">
<thead>
<tr>
<th>Description</th>
Expand All @@ -119,7 +119,7 @@ export function ShortcutEditor(props: { sf: SfApi }): VNode {
{isCapturing ? (
<input
class="wl-input wl-shortcutCapture"
placeholder="Press keys..."
placeholder="Press keys..." aria-label="Press new keyboard shortcut"
readOnly
autoFocus
onKeyDown={handleCaptureKeydown}
Expand All @@ -131,11 +131,11 @@ export function ShortcutEditor(props: { sf: SfApi }): VNode {
</td>
<td>
{isCapturing ? (
<button class="wl-btn" onClick={cancelCapture}>
<button class="wl-btn" onClick={cancelCapture} aria-label="Cancel shortcut capture">
Cancel
</button>
) : (
<button class="wl-btn" onClick={() => startCapture(def.id)}>
<button class="wl-btn" onClick={() => startCapture(def.id)} aria-label={`Edit shortcut for ${def.description}`}>
Edit
</button>
)}
Expand All @@ -148,3 +148,4 @@ export function ShortcutEditor(props: { sf: SfApi }): VNode {
</div>
);
}

Loading
Loading