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
1,836 changes: 868 additions & 968 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@
"@tanstack/react-query": "^5.90.11",
"@types/node": "^25.0.2",
"@vitejs/plugin-react": "^6.0.1",
"@vizzly-testing/observatory": "^0.3.3",
"autoprefixer": "^10.4.21",
"babel-plugin-react-compiler": "^1.0.0",
"babel-plugin-transform-remove-console": "^6.9.4",
Expand Down
8 changes: 4 additions & 4 deletions src/reporter/src/components/comparison/comparison-viewer.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { useCallback, useMemo, useState } from 'react';
import { VIEW_MODES } from '../../utils/constants.js';
import { withImageVersion } from '../../utils/image-url.js';
import {
OnionSkinMode,
OverlayMode,
ToggleView,
} from '@vizzly-testing/observatory';
import { useCallback, useMemo, useState } from 'react';
import { VIEW_MODES } from '../../utils/constants.js';
import { withImageVersion } from '../../utils/image-url.js';
} from '../design-system/index.js';

/**
* Comparison Viewer for inline card display
Expand Down
8 changes: 4 additions & 4 deletions src/reporter/src/components/comparison/fullscreen-viewer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import {
ListBulletIcon,
MapPinIcon,
} from '@heroicons/react/24/outline';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { VIEW_MODES } from '../../utils/constants.js';
import { withImageVersion } from '../../utils/image-url.js';
import {
ApprovalButtonGroup,
Badge,
Expand All @@ -42,10 +45,7 @@ import {
VariantBreadcrumb,
ViewModeToggle,
ZoomControls,
} from '@vizzly-testing/observatory';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { VIEW_MODES } from '../../utils/constants.js';
import { withImageVersion } from '../../utils/image-url.js';
} from '../design-system/index.js';
import { ScreenshotDisplay } from './screenshot-display.jsx';

/**
Expand Down
6 changes: 3 additions & 3 deletions src/reporter/src/components/comparison/screenshot-display.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { withImageVersion } from '../../utils/image-url.js';
import {
HotSpotOverlay,
OnionSkinMode,
OverlayMode,
ToggleMode,
} from '@vizzly-testing/observatory';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { withImageVersion } from '../../utils/image-url.js';
} from '../design-system/index.js';

/**
* Unified Screenshot Display Component - matches Observatory architecture
Expand Down
6 changes: 3 additions & 3 deletions src/reporter/src/components/dashboard/dashboard-filters.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import {
DeviceTabletIcon,
XMarkIcon,
} from '@heroicons/react/24/outline';
import { useCallback, useMemo, useRef, useState } from 'react';
import { FILTER_TYPES, SORT_TYPES } from '../../utils/constants.js';
import {
BrowserIcon,
FilterPill,
SearchInput,
} from '@vizzly-testing/observatory';
import { useCallback, useMemo, useRef, useState } from 'react';
import { FILTER_TYPES, SORT_TYPES } from '../../utils/constants.js';
} from '../design-system/index.js';

/**
* Get device icon based on viewport width
Expand Down
125 changes: 125 additions & 0 deletions src/reporter/src/components/design-system/active-filter-chip.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { XMarkIcon } from '@heroicons/react/24/outline';

/**
* ActiveFilterChip - Shows an active filter with dismiss button
* Used to display currently applied filters that can be removed
*/

let warningChipClass =
'bg-[var(--accent-warning-muted)] text-[var(--accent-warning)] border-[color-mix(in_srgb,var(--accent-warning)_24%,transparent)]';
let infoChipClass =
'bg-[var(--accent-info-muted)] text-[var(--accent-info)] border-[color-mix(in_srgb,var(--accent-info)_24%,transparent)]';
let successChipClass =
'bg-[var(--accent-success-muted)] text-[var(--accent-success)] border-[color-mix(in_srgb,var(--accent-success)_24%,transparent)]';
let dangerChipClass =
'bg-[var(--accent-danger-muted)] text-[var(--accent-danger)] border-[color-mix(in_srgb,var(--accent-danger)_24%,transparent)]';
let mediaChipClass =
'bg-[var(--accent-media-muted)] text-[var(--accent-media)] border-[color-mix(in_srgb,var(--accent-media)_24%,transparent)]';
let neutralChipClass =
'bg-white/5 text-[var(--text-secondary)] border-[var(--vz-border-subtle)]';

let colorClasses = {
brand:
'bg-[var(--accent-brand-muted)] text-[var(--accent-brand)] border-[color-mix(in_srgb,var(--accent-brand)_24%,transparent)]',
amber: warningChipClass,
blue: infoChipClass,
cyan: infoChipClass,
emerald: successChipClass,
gray: neutralChipClass,
green: successChipClass,
info: infoChipClass,
media: mediaChipClass,
neutral: neutralChipClass,
orange: warningChipClass,
purple: mediaChipClass,
red: dangerChipClass,
};

export function ActiveFilterChip({
label,
color = 'neutral',
onRemove,
icon: Icon,
className = '',
}) {
return (
<span
className={`
inline-flex items-center gap-1 px-2 py-0.5 border rounded text-xs
whitespace-nowrap flex-shrink-0
${colorClasses[color] || colorClasses.neutral}
${className}
`}
>
{Icon && <Icon className="w-3 h-3" />}
<span className="max-w-[150px] truncate">{label}</span>
{onRemove && (
<button
type="button"
onClick={onRemove}
className="ml-0.5 hover:text-[var(--text-primary)] transition-colors"
>
<XMarkIcon className="w-3 h-3" />
</button>
)}
</span>
);
}

/**
* ActiveFilterBar - Container for showing active filters with count summary
*/
export function ActiveFilterBar({
filteredCount,
totalCount,
children,
onClearAll,
className = '',
}) {
let hasFilters =
children && (Array.isArray(children) ? children.length > 0 : true);

if (!hasFilters) return null;

return (
<div
className={`
flex items-center gap-2 px-3 sm:px-4 py-2 bg-black/20 border-b border-[var(--vz-border-subtle)]
overflow-x-auto scrollbar-none
${className}
`}
>
{(filteredCount !== undefined || totalCount !== undefined) && (
<>
<span className="text-xs text-[var(--text-muted)] whitespace-nowrap">
<span className="font-medium text-[var(--text-primary)]">
{filteredCount}
</span>
{totalCount !== undefined && (
<span className="hidden sm:inline"> of {totalCount}</span>
)}
</span>
<div className="w-px h-4 bg-[var(--vz-border-subtle)] flex-shrink-0" />
</>
)}

<div className="flex items-center gap-1.5 flex-nowrap sm:flex-wrap">
{children}
</div>

{onClearAll && (
<>
<div className="flex-1" />
<button
type="button"
onClick={onClearAll}
className="inline-flex items-center gap-1 px-2 py-1 text-xs text-[var(--text-tertiary)] hover:text-[var(--text-primary)] transition-colors whitespace-nowrap"
>
<XMarkIcon className="w-3.5 h-3.5" />
Clear all
</button>
</>
)}
</div>
);
}
100 changes: 100 additions & 0 deletions src/reporter/src/components/design-system/alert.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/**
* Alert Component
* Observatory Design System
*
* For feedback messages and notifications
* Variants: success, warning, danger, info
*/

import {
CheckCircleIcon,
ExclamationTriangleIcon,
InformationCircleIcon,
XCircleIcon,
XMarkIcon,
} from '@heroicons/react/24/outline';

export function Alert({
variant = 'info',
title,
children,
dismissible = false,
onDismiss,
icon: CustomIcon,
className = '',
...props
}) {
let variants = {
success: {
container:
'bg-[var(--accent-success-muted)] border-[color-mix(in_srgb,var(--accent-success)_24%,transparent)]',
icon: 'text-[var(--accent-success)]',
title: 'text-[var(--accent-success)]',
text: 'text-[var(--text-secondary)]',
DefaultIcon: CheckCircleIcon,
},
warning: {
container:
'bg-[var(--accent-warning-muted)] border-[color-mix(in_srgb,var(--accent-warning)_24%,transparent)]',
icon: 'text-[var(--accent-warning)]',
title: 'text-[var(--accent-warning)]',
text: 'text-[var(--text-secondary)]',
DefaultIcon: ExclamationTriangleIcon,
},
danger: {
container:
'bg-[var(--accent-danger-muted)] border-[color-mix(in_srgb,var(--accent-danger)_24%,transparent)]',
icon: 'text-[var(--accent-danger)]',
title: 'text-[var(--accent-danger)]',
text: 'text-[var(--text-secondary)]',
DefaultIcon: XCircleIcon,
},
info: {
container:
'bg-[var(--accent-info-muted)] border-[color-mix(in_srgb,var(--accent-info)_24%,transparent)]',
icon: 'text-[var(--accent-info)]',
title: 'text-[var(--accent-info)]',
text: 'text-[var(--text-secondary)]',
DefaultIcon: InformationCircleIcon,
},
};

let {
container,
icon,
title: titleColor,
text,
DefaultIcon,
} = variants[variant];
let Icon = CustomIcon || DefaultIcon;

return (
<div
className={`flex gap-3 p-4 rounded-lg border ${container} ${className}`}
{...props}
>
<div className={`flex-shrink-0 ${icon}`}>
<Icon className="w-5 h-5" />
</div>
<div className="flex-1 min-w-0">
{title && (
<h4 className={`text-sm font-medium ${titleColor}`}>{title}</h4>
)}
{children && (
<div className={`text-sm ${text} ${title ? 'mt-1' : ''}`}>
{children}
</div>
)}
</div>
{dismissible && (
<button
type="button"
onClick={onDismiss}
className={`flex-shrink-0 ${icon} hover:opacity-70 transition-opacity`}
>
<XMarkIcon className="w-5 h-5" />
</button>
)}
</div>
);
}
Loading