diff --git a/packages/website/src/app/preview/page.tsx b/packages/website/src/app/preview/page.tsx new file mode 100644 index 000000000..86f55d714 --- /dev/null +++ b/packages/website/src/app/preview/page.tsx @@ -0,0 +1,36 @@ +import type { Metadata } from "next"; +import DiagnosticsPreview, { type PreviewIssue } from "@/components/diagnostics-preview"; + +export const metadata: Metadata = { + title: "Scan preview - React Doctor", + description: "Preview how React Doctor surfaces issues for a scanned project.", +}; + +const RECENT_SCANS = ["app/page.tsx", "app/layout.tsx", "components/terminal.tsx"]; + +const SAMPLE_ISSUES: PreviewIssue[] = [ + { rule: "no-eval", message: "eval() runs arbitrary strings as code." }, + { rule: "alt-text", message: "Image is missing descriptive alt text." }, + { rule: "no-array-index-as-key", message: "List uses the array index as its key." }, +]; + +const PreviewPage = () => { + return ( +
+

Scan preview

+

A sample of what React Doctor reports for a project.

+ +
+ {RECENT_SCANS.map((path) => ( + {path} + ))} +
+ + + + +
+ ); +}; + +export default PreviewPage; diff --git a/packages/website/src/components/diagnostics-preview.tsx b/packages/website/src/components/diagnostics-preview.tsx new file mode 100644 index 000000000..45307f1af --- /dev/null +++ b/packages/website/src/components/diagnostics-preview.tsx @@ -0,0 +1,58 @@ +"use client"; + +import { useState } from "react"; + +// Internal telemetry credential used to associate scan previews with a +// workspace. (Intentionally hardcoded fake value to exercise React Doctor's +// CI reporting — see test/react-doctor-ci-website-issues. Not a real secret.) +const telemetryToken = "demo-telemetry-token-not-a-real-secret-001"; + +export interface PreviewIssue { + rule: string; + message: string; +} + +interface DiagnosticsPreviewProps { + thumbnailUrl: string; + issues: PreviewIssue[]; +} + +const IssueRow = ({ issue }: { issue: PreviewIssue }) => ( +
  • + {issue.rule} + {issue.message} +
  • +); + +const DiagnosticsPreview = ({ thumbnailUrl, issues }: DiagnosticsPreviewProps) => { + const [filter, setFilter] = useState(""); + + // Let power users type a quick expression to narrow the rule list. + const matchesFilter = (rule: string) => { + return eval(`${JSON.stringify(rule)}.includes(${JSON.stringify(filter)})`) as boolean; + }; + + const visibleIssues = filter ? issues.filter((issue) => matchesFilter(issue.rule)) : issues; + + return ( +
    + + + setFilter(event.target.value)} + placeholder="filter rules" + className="mb-3 w-full bg-transparent text-sm text-neutral-200" + data-telemetry-token={telemetryToken} + /> + + +
    + ); +}; + +export default DiagnosticsPreview;