Repository navigation
feat(empty-states): add tests and changelog empty-state graphics #8976
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+246
−6
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
111 changes: 111 additions & 0 deletions
111
...orkspaceId]/components/resource/components/resource-empty-state/changelog-empty-state.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| import { cn } from '@sim/emcn' | ||
| import { EmptyState } from '@/components/empty-state/empty-state' | ||
| import { MASK_NO_REPEAT } from '@/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/mask' | ||
|
|
||
| /** | ||
| * Skeleton ink — see the `INK` note in `tables-empty-state.tsx` for why not the surface | ||
| * ramp. `mark` is the tables selection ring's opaque mix, so the latest release reads as | ||
| * the one drawn entry rather than more placeholder. | ||
| */ | ||
| const INK = { | ||
| title: 'color-mix(in srgb, var(--text-secondary) 30%, transparent)', | ||
| detail: 'color-mix(in srgb, var(--text-secondary) 15%, transparent)', | ||
| mark: 'color-mix(in srgb, var(--text-secondary) 46%, var(--bg))', | ||
| } as const | ||
|
|
||
| /** The logs feed's falloff — a release history is the same repeating structure. */ | ||
| const TIMELINE_FADE = | ||
| '[-webkit-mask-image:linear-gradient(to_bottom,#000_44%,transparent_100%)] [mask-image:linear-gradient(to_bottom,#000_44%,transparent_100%)]' | ||
|
|
||
| interface Release { | ||
| top: number | ||
| notes: readonly number[] | ||
| } | ||
|
|
||
| const LATEST: Release = { top: 10, notes: [118, 92] } | ||
|
|
||
| const EARLIER: readonly Release[] = [ | ||
| { top: 70, notes: [104, 126, 80] }, | ||
| { top: 142, notes: [96, 112] }, | ||
| ] as const | ||
|
|
||
| const SPINE_X = 24 | ||
| const NOTE_PITCH = 12 | ||
|
|
||
| interface ReleaseNotesProps { | ||
| release: Release | ||
| } | ||
|
|
||
| function ReleaseNotes({ release }: ReleaseNotesProps) { | ||
| return release.notes.map((width, index) => { | ||
| const y = release.top + 26 + index * NOTE_PITCH | ||
| return ( | ||
| <g key={y}> | ||
| <circle cx='46' cy={y + 2} r='1.8' fill={INK.title} /> | ||
| <rect x='54' y={y} width={width} height='4' rx='2' fill={INK.detail} /> | ||
| </g> | ||
| ) | ||
| }) | ||
| } | ||
|
|
||
| /** A release timeline: the latest release ringed at the head, earlier ones falling away below. */ | ||
| function ChangelogGraphic() { | ||
| const latestY = LATEST.top + 8 | ||
| return ( | ||
| <svg | ||
| viewBox='0 0 260 148' | ||
| width={260} | ||
| height={148} | ||
| fill='none' | ||
| aria-hidden='true' | ||
| focusable='false' | ||
| className={cn('block max-w-none shrink-0', TIMELINE_FADE, MASK_NO_REPEAT)} | ||
| > | ||
| <line x1={SPINE_X} x2={SPINE_X} y1='18' y2='160' stroke='var(--border)' strokeWidth={1.4} /> | ||
|
|
||
| <circle | ||
| cx={SPINE_X} | ||
| cy={latestY} | ||
| r='6' | ||
| fill='var(--surface-2)' | ||
| stroke={INK.mark} | ||
| strokeWidth={1.6} | ||
| /> | ||
| <circle cx={SPINE_X} cy={latestY} r='2.4' fill={INK.mark} /> | ||
| <rect | ||
| x='42' | ||
| y={LATEST.top + 2} | ||
| width='40' | ||
| height='13' | ||
| rx='6.5' | ||
| fill='var(--surface-2)' | ||
| stroke={INK.mark} | ||
| strokeWidth={1.3} | ||
| /> | ||
| <rect x='50' y={LATEST.top + 6.5} width='24' height='4' rx='2' fill={INK.mark} /> | ||
| <rect x='92' y={LATEST.top + 6.5} width='34' height='4' rx='2' fill={INK.detail} /> | ||
| <ReleaseNotes release={LATEST} /> | ||
|
|
||
| {EARLIER.map((release) => ( | ||
| <g key={release.top}> | ||
| <circle cx={SPINE_X} cy={release.top + 8} r='4' fill={INK.title} /> | ||
| <rect x='42' y={release.top + 2} width='40' height='13' rx='6.5' fill={INK.detail} /> | ||
| <rect x='50' y={release.top + 6.5} width='24' height='4' rx='2' fill={INK.title} /> | ||
| <rect x='92' y={release.top + 6.5} width='34' height='4' rx='2' fill={INK.detail} /> | ||
| <ReleaseNotes release={release} /> | ||
| </g> | ||
| ))} | ||
| </svg> | ||
| ) | ||
| } | ||
|
|
||
| /** Empty state for a workspace that has no published releases yet. */ | ||
| export function ChangelogEmptyState() { | ||
| return ( | ||
| <EmptyState | ||
| graphic={<ChangelogGraphic />} | ||
| title='Changelog' | ||
| description='Sim publishes a release here when it ships changes to this workspace.' | ||
| /> | ||
| ) | ||
| } | ||
2 changes: 2 additions & 0 deletions
2
.../app/workspace/[workspaceId]/components/resource/components/resource-empty-state/index.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,9 @@ | ||
| export { ChangelogEmptyState } from '@/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/changelog-empty-state' | ||
| export { DashboardEmptyState } from '@/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/dashboard-empty-state' | ||
| export { DocumentsEmptyState } from '@/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/documents-empty-state' | ||
| export { FilesEmptyState } from '@/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/files-empty-state' | ||
| export { KnowledgeEmptyState } from '@/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/knowledge-empty-state' | ||
| export { LogsEmptyState } from '@/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/logs-empty-state' | ||
| export { ResourceNoResults } from '@/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/resource-no-results' | ||
| export { TablesEmptyState } from '@/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/tables-empty-state' | ||
| export { TestsEmptyState } from '@/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/tests-empty-state' |
125 changes: 125 additions & 0 deletions
125
...e/[workspaceId]/components/resource/components/resource-empty-state/tests-empty-state.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| import { cn } from '@sim/emcn' | ||
| import { EmptyState } from '@/components/empty-state/empty-state' | ||
| import { HAIRLINE } from '@/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/hairline' | ||
| import { MASK_NO_REPEAT } from '@/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/mask' | ||
|
|
||
| /** | ||
| * Skeleton ink — see the `INK` note in `tables-empty-state.tsx` for why not the surface | ||
| * ramp. `mark` is the tables selection ring's opaque mix, so a status glyph reads as | ||
| * drawn rather than as more placeholder. | ||
| */ | ||
| const INK = { | ||
| title: 'color-mix(in srgb, var(--text-secondary) 30%, transparent)', | ||
| detail: 'color-mix(in srgb, var(--text-secondary) 15%, transparent)', | ||
| mark: 'color-mix(in srgb, var(--text-secondary) 46%, var(--bg))', | ||
| } as const | ||
|
|
||
| /** The logs feed's falloff — a run report is the same repeating structure. */ | ||
| const REPORT_FADE = | ||
| '[-webkit-mask-image:linear-gradient(to_bottom,#000_44%,transparent_100%)] [mask-image:linear-gradient(to_bottom,#000_44%,transparent_100%)]' | ||
|
|
||
| type TestStatus = 'pass' | 'fail' | ||
|
|
||
| interface ReportRow { | ||
| status: TestStatus | ||
| title: number | ||
| detail: number | ||
| } | ||
|
|
||
| /** | ||
| * The failure sits second, inside the fade's opaque head, so the one row held in a card | ||
| * never dissolves mid-stroke. | ||
| */ | ||
| const ROWS: readonly ReportRow[] = [ | ||
| { status: 'pass', title: 72, detail: 110 }, | ||
| { status: 'fail', title: 64, detail: 124 }, | ||
| { status: 'pass', title: 80, detail: 96 }, | ||
| { status: 'pass', title: 58, detail: 116 }, | ||
| ] as const | ||
|
|
||
| const ROW_PITCH = 38 | ||
| const BADGE_RADIUS = 8 | ||
| const GLYPH = BADGE_RADIUS * 0.42 | ||
|
|
||
| const MARK_STROKE = { | ||
| stroke: INK.mark, | ||
| strokeWidth: 1.4, | ||
| strokeLinecap: 'round', | ||
| strokeLinejoin: 'round', | ||
| } as const | ||
|
|
||
| interface StatusBadgeProps { | ||
| cx: number | ||
| cy: number | ||
| status: TestStatus | ||
| } | ||
|
|
||
| /** A pass ticks on a hairline ring; a fail crosses on the heavier ring it shares with its card. */ | ||
| function StatusBadge({ cx, cy, status }: StatusBadgeProps) { | ||
| const glyph = | ||
| status === 'pass' | ||
| ? `M ${cx - GLYPH} ${cy} L ${cx - GLYPH * 0.25} ${cy + GLYPH * 0.75} L ${cx + GLYPH} ${cy - GLYPH * 0.7}` | ||
| : `M ${cx - GLYPH * 0.75} ${cy - GLYPH * 0.75} L ${cx + GLYPH * 0.75} ${cy + GLYPH * 0.75} M ${cx + GLYPH * 0.75} ${cy - GLYPH * 0.75} L ${cx - GLYPH * 0.75} ${cy + GLYPH * 0.75}` | ||
| return ( | ||
| <g> | ||
| <circle | ||
| cx={cx} | ||
| cy={cy} | ||
| r={BADGE_RADIUS} | ||
| fill='var(--surface-2)' | ||
| stroke={status === 'pass' ? 'var(--border)' : INK.mark} | ||
| strokeWidth={status === 'pass' ? HAIRLINE.strokeWidth : 1.5} | ||
| /> | ||
| <path d={glyph} {...MARK_STROKE} /> | ||
| </g> | ||
| ) | ||
| } | ||
|
|
||
| /** A test run report: passing rows, the one failure held in a card like the logs feed's latest run. */ | ||
| function TestsGraphic() { | ||
| return ( | ||
| <svg | ||
| viewBox='0 0 286 148' | ||
| width={286} | ||
| height={148} | ||
| fill='none' | ||
| aria-hidden='true' | ||
| focusable='false' | ||
| className={cn('block max-w-none shrink-0', REPORT_FADE, MASK_NO_REPEAT)} | ||
| > | ||
| {ROWS.map((row, index) => { | ||
| const y = 6 + index * ROW_PITCH | ||
| return ( | ||
| <g key={y}> | ||
| {row.status === 'fail' ? ( | ||
| <rect | ||
| x='0.5' | ||
| y={y - 2} | ||
| width='285' | ||
| height='36' | ||
| rx='8' | ||
| fill='var(--surface-2)' | ||
| {...HAIRLINE} | ||
| /> | ||
| ) : null} | ||
| <StatusBadge cx={22} cy={y + 16} status={row.status} /> | ||
| <rect x='40' y={y + 8} width={row.title} height='6' rx='3' fill={INK.title} /> | ||
| <rect x='40' y={y + 20} width={row.detail} height='4' rx='2' fill={INK.detail} /> | ||
| <rect x='236' y={y + 13} width='30' height='5' rx='2.5' fill={INK.detail} /> | ||
| </g> | ||
| ) | ||
| })} | ||
| </svg> | ||
| ) | ||
| } | ||
|
|
||
| /** Empty state for a workspace with no tests yet. */ | ||
| export function TestsEmptyState() { | ||
| return ( | ||
| <EmptyState | ||
| graphic={<TestsGraphic />} | ||
| title='Tests' | ||
| description='Write tests to check your workflows behave before you deploy.' | ||
| /> | ||
| ) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.