@nucel/ui 0.2.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +8 -32
- package/src/lib/components/BottomSheet.svelte +96 -0
- package/src/lib/components/Breadcrumbs.svelte +57 -0
- package/src/lib/components/Checkbox.svelte +64 -0
- package/src/lib/components/CodeBlock.svelte +264 -0
- package/src/lib/components/CodeEditor.svelte +175 -0
- package/src/lib/components/ColorInput.svelte +41 -0
- package/src/lib/components/ColorInput.test.ts +126 -0
- package/src/lib/components/Combobox.svelte +103 -0
- package/src/lib/components/CommandPalette.svelte +135 -0
- package/src/lib/components/CopyButton.svelte +95 -0
- package/src/lib/components/CopyButton.test.ts +213 -0
- package/src/lib/components/DataTable.svelte +202 -0
- package/src/lib/components/DateRangePicker.svelte +185 -0
- package/src/lib/components/DiffEditor.svelte +174 -0
- package/src/lib/components/Drawer.svelte +69 -0
- package/src/lib/components/Fab.svelte +59 -0
- package/src/lib/components/Form.svelte +38 -0
- package/src/lib/components/FormField.svelte +51 -0
- package/src/lib/components/IconButton.svelte +86 -0
- package/src/lib/components/IconButton.test.ts +139 -0
- package/src/lib/components/InlineCode.svelte +28 -0
- package/src/lib/components/Pagination.svelte +65 -0
- package/src/lib/components/Radio.svelte +60 -0
- package/src/lib/components/RadioGroup.svelte +26 -0
- package/src/lib/components/SearchInput.svelte +77 -0
- package/src/lib/components/Skeleton.svelte +76 -0
- package/src/lib/components/StatCard.svelte +97 -0
- package/src/lib/components/ThemeProvider.svelte +157 -0
- package/src/lib/components/ThemeToggle.svelte +68 -0
- package/src/lib/components/ThreeWayMerge.svelte +185 -0
- package/src/lib/components/ui/MarkdownRenderer.svelte +126 -8
- package/src/lib/components/ui/Sparkline.svelte +1 -1
- package/src/lib/components/ui/StatusBadge.svelte +6 -3
- package/src/lib/components/ui/StatusDot.svelte +3 -3
- package/src/lib/index.ts +120 -45
- package/src/lib/utils/detectLanguage.ts +187 -0
- package/src/lib/utils/monaco-workers.d.ts +32 -0
- package/src/lib/utils/monacoLoader.ts +167 -0
- package/src/lib/utils/shikiHighlighter.ts +78 -0
- package/src/styles.css +100 -32
- package/src/lib/components/ui/Alert.svelte +0 -47
- package/src/lib/components/ui/Alert.test.ts +0 -206
- package/src/lib/components/ui/AppCard.svelte +0 -76
- package/src/lib/components/ui/AppShell.svelte +0 -14
- package/src/lib/components/ui/AppSidebar.svelte +0 -45
- package/src/lib/components/ui/BranchPill.svelte +0 -19
- package/src/lib/components/ui/BranchPill.test.ts +0 -121
- package/src/lib/components/ui/CommentPill.svelte +0 -12
- package/src/lib/components/ui/CostDisplay.svelte +0 -26
- package/src/lib/components/ui/CostDisplay.test.ts +0 -1115
- package/src/lib/components/ui/FormField.svelte +0 -34
- package/src/lib/components/ui/FormField.test.ts +0 -41
- package/src/lib/components/ui/ListCard.svelte +0 -9
- package/src/lib/components/ui/NavItem.svelte +0 -42
- package/src/lib/components/ui/NavSection.svelte +0 -17
- package/src/lib/components/ui/PageHeader.svelte +0 -25
- package/src/lib/components/ui/PageHeader.test.ts +0 -72
- package/src/lib/components/ui/PermissionChips.svelte +0 -49
- package/src/lib/components/ui/ProgressRing.test.ts +0 -239
- package/src/lib/components/ui/Section.svelte +0 -21
- package/src/lib/components/ui/Section.test.ts +0 -44
- package/src/lib/components/ui/SectionTitle.svelte +0 -16
- package/src/lib/components/ui/StatCard.svelte +0 -19
- package/src/lib/components/ui/StatusBadge.test.ts +0 -150
- package/src/lib/components/ui/StatusPill.svelte +0 -54
- package/src/lib/components/ui/StatusPill.test.ts +0 -125
- package/src/lib/components/ui/editor/RichEditor.svelte +0 -580
- package/src/lib/components/ui/editor/mention-suggestion.ts +0 -144
- package/src/lib/components/ui/table/Table.svelte +0 -12
- package/src/lib/components/ui/table/Table.test.ts +0 -317
- package/src/lib/components/ui/table/TableBody.svelte +0 -10
- package/src/lib/components/ui/table/TableCaption.svelte +0 -10
- package/src/lib/components/ui/table/TableCell.svelte +0 -10
- package/src/lib/components/ui/table/TableHead.svelte +0 -10
- package/src/lib/components/ui/table/TableHeader.svelte +0 -10
- package/src/lib/components/ui/table/TableRow.svelte +0 -10
- package/src/lib/components/ui/table/index.ts +0 -7
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { render, screen } from "@testing-library/svelte";
|
|
3
|
-
import StatusBadge from "./StatusBadge.svelte";
|
|
4
|
-
|
|
5
|
-
describe("StatusBadge — label rendering", () => {
|
|
6
|
-
it("renders 'open' as the label for status=open", () => {
|
|
7
|
-
render(StatusBadge, { props: { status: "open" } });
|
|
8
|
-
expect(screen.getByText("open")).toBeInTheDocument();
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
it("renders 'closed' as the label for status=closed", () => {
|
|
12
|
-
render(StatusBadge, { props: { status: "closed" } });
|
|
13
|
-
expect(screen.getByText("closed")).toBeInTheDocument();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it("renders 'running' as the label for status=running", () => {
|
|
17
|
-
render(StatusBadge, { props: { status: "running" } });
|
|
18
|
-
expect(screen.getByText("running")).toBeInTheDocument();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it("renders 'succeeded' as the label for status=succeeded", () => {
|
|
22
|
-
render(StatusBadge, { props: { status: "succeeded" } });
|
|
23
|
-
expect(screen.getByText("succeeded")).toBeInTheDocument();
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("renders 'failed' as the label for status=failed", () => {
|
|
27
|
-
render(StatusBadge, { props: { status: "failed" } });
|
|
28
|
-
expect(screen.getByText("failed")).toBeInTheDocument();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("renders 'merged' as the label for status=merged", () => {
|
|
32
|
-
render(StatusBadge, { props: { status: "merged" } });
|
|
33
|
-
expect(screen.getByText("merged")).toBeInTheDocument();
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("renders custom label when label prop is provided", () => {
|
|
37
|
-
render(StatusBadge, { props: { status: "open", label: "In Progress" } });
|
|
38
|
-
expect(screen.getByText("In Progress")).toBeInTheDocument();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("custom label overrides the default status label", () => {
|
|
42
|
-
render(StatusBadge, { props: { status: "open", label: "My Label" } });
|
|
43
|
-
expect(screen.queryByText("open")).not.toBeInTheDocument();
|
|
44
|
-
expect(screen.getByText("My Label")).toBeInTheDocument();
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("renders without crashing for any known status", () => {
|
|
48
|
-
const statuses = ["open", "closed", "running", "succeeded", "failed", "merged"];
|
|
49
|
-
for (const status of statuses) {
|
|
50
|
-
expect(() => render(StatusBadge, { props: { status } })).not.toThrow();
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
describe("StatusBadge — color classes", () => {
|
|
56
|
-
it("applies success color classes for status=open", () => {
|
|
57
|
-
const { container } = render(StatusBadge, { props: { status: "open" } });
|
|
58
|
-
const badge = container.firstElementChild;
|
|
59
|
-
expect(badge?.className).toContain("text-success");
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("applies purple color classes for status=merged", () => {
|
|
63
|
-
const { container } = render(StatusBadge, { props: { status: "merged" } });
|
|
64
|
-
const badge = container.firstElementChild;
|
|
65
|
-
expect(badge?.className).toContain("text-purple-400");
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it("applies blue color classes for status=running", () => {
|
|
69
|
-
const { container } = render(StatusBadge, { props: { status: "running" } });
|
|
70
|
-
const badge = container.firstElementChild;
|
|
71
|
-
expect(badge?.className).toContain("text-blue-400");
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it("applies success color classes for status=succeeded", () => {
|
|
75
|
-
const { container } = render(StatusBadge, { props: { status: "succeeded" } });
|
|
76
|
-
const badge = container.firstElementChild;
|
|
77
|
-
expect(badge?.className).toContain("text-success");
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it("applies destructive color classes for status=failed", () => {
|
|
81
|
-
const { container } = render(StatusBadge, { props: { status: "failed" } });
|
|
82
|
-
const badge = container.firstElementChild;
|
|
83
|
-
expect(badge?.className).toContain("text-destructive");
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it("uses secondary variant for status=closed", () => {
|
|
87
|
-
// closed maps to secondary variant (no extraClass)
|
|
88
|
-
const { container } = render(StatusBadge, { props: { status: "closed" } });
|
|
89
|
-
expect(container.firstElementChild).not.toBeNull();
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
describe("StatusBadge — structure", () => {
|
|
94
|
-
it("renders an inline-flex element", () => {
|
|
95
|
-
const { container } = render(StatusBadge, { props: { status: "open" } });
|
|
96
|
-
const badge = container.firstElementChild;
|
|
97
|
-
expect(badge?.className).toContain("inline-flex");
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it("renders items-center class", () => {
|
|
101
|
-
const { container } = render(StatusBadge, { props: { status: "open" } });
|
|
102
|
-
const badge = container.firstElementChild;
|
|
103
|
-
expect(badge?.className).toContain("items-center");
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it("contains a span with the label text", () => {
|
|
107
|
-
const { container } = render(StatusBadge, { props: { status: "open" } });
|
|
108
|
-
const span = container.querySelector("span");
|
|
109
|
-
expect(span).not.toBeNull();
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it("renders without crashing for unknown status", () => {
|
|
113
|
-
expect(() => render(StatusBadge, { props: { status: "unknown" } })).not.toThrow();
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it("renders custom label for any status", () => {
|
|
117
|
-
render(StatusBadge, { props: { status: "failed", label: "Broken" } });
|
|
118
|
-
expect(screen.getByText("Broken")).toBeInTheDocument();
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it("background color class is present for status=open", () => {
|
|
122
|
-
const { container } = render(StatusBadge, { props: { status: "open" } });
|
|
123
|
-
const badge = container.firstElementChild;
|
|
124
|
-
expect(badge?.className).toContain("bg-success/10");
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it("background color class is present for status=merged", () => {
|
|
128
|
-
const { container } = render(StatusBadge, { props: { status: "merged" } });
|
|
129
|
-
const badge = container.firstElementChild;
|
|
130
|
-
expect(badge?.className).toContain("bg-purple-500/10");
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it("background color class is present for status=running", () => {
|
|
134
|
-
const { container } = render(StatusBadge, { props: { status: "running" } });
|
|
135
|
-
const badge = container.firstElementChild;
|
|
136
|
-
expect(badge?.className).toContain("bg-blue-500/10");
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it("background color class is present for status=failed", () => {
|
|
140
|
-
const { container } = render(StatusBadge, { props: { status: "failed" } });
|
|
141
|
-
const badge = container.firstElementChild;
|
|
142
|
-
expect(badge?.className).toContain("bg-destructive/10");
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
it("background color class is present for status=succeeded", () => {
|
|
146
|
-
const { container } = render(StatusBadge, { props: { status: "succeeded" } });
|
|
147
|
-
const badge = container.firstElementChild;
|
|
148
|
-
expect(badge?.className).toContain("bg-success/10");
|
|
149
|
-
});
|
|
150
|
-
});
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { Check, X, Loader2, CircleSlash, Clock, AlertTriangle } from '@lucide/svelte';
|
|
3
|
-
|
|
4
|
-
type Status = 'success' | 'failure' | 'running' | 'pending' | 'cancelled' | 'warning';
|
|
5
|
-
|
|
6
|
-
let {
|
|
7
|
-
status,
|
|
8
|
-
label,
|
|
9
|
-
size = 'sm',
|
|
10
|
-
}: {
|
|
11
|
-
status: Status;
|
|
12
|
-
label?: string;
|
|
13
|
-
size?: 'xs' | 'sm';
|
|
14
|
-
} = $props();
|
|
15
|
-
|
|
16
|
-
const defaults: Record<Status, string> = {
|
|
17
|
-
success: 'passed',
|
|
18
|
-
failure: 'failed',
|
|
19
|
-
running: 'running',
|
|
20
|
-
pending: 'pending',
|
|
21
|
-
cancelled: 'cancelled',
|
|
22
|
-
warning: 'warning',
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const styles: Record<Status, string> = {
|
|
26
|
-
success: 'border-green-500/30 bg-green-500/10 text-green-500',
|
|
27
|
-
failure: 'border-destructive/30 bg-destructive/10 text-destructive',
|
|
28
|
-
running: 'border-blue-500/30 bg-blue-500/10 text-blue-400',
|
|
29
|
-
pending: 'border-yellow-500/30 bg-yellow-500/10 text-yellow-500',
|
|
30
|
-
cancelled: 'border-border/60 bg-muted text-muted-foreground',
|
|
31
|
-
warning: 'border-amber-500/30 bg-amber-500/10 text-amber-500',
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
let text = $derived(label ?? defaults[status]);
|
|
35
|
-
let sizeCls = $derived(size === 'xs' ? 'text-[10px] px-1.5 py-0.5' : 'text-[11px] px-2 py-0.5');
|
|
36
|
-
let iconSize = $derived(size === 'xs' ? 'h-2.5 w-2.5' : 'h-3 w-3');
|
|
37
|
-
</script>
|
|
38
|
-
|
|
39
|
-
<span class="inline-flex items-center gap-1 rounded-full border font-medium {sizeCls} {styles[status]}">
|
|
40
|
-
{#if status === 'success'}
|
|
41
|
-
<Check class={iconSize} />
|
|
42
|
-
{:else if status === 'failure'}
|
|
43
|
-
<X class={iconSize} />
|
|
44
|
-
{:else if status === 'running'}
|
|
45
|
-
<Loader2 class="{iconSize} animate-spin" />
|
|
46
|
-
{:else if status === 'cancelled'}
|
|
47
|
-
<CircleSlash class={iconSize} />
|
|
48
|
-
{:else if status === 'warning'}
|
|
49
|
-
<AlertTriangle class={iconSize} />
|
|
50
|
-
{:else}
|
|
51
|
-
<Clock class={iconSize} />
|
|
52
|
-
{/if}
|
|
53
|
-
{text}
|
|
54
|
-
</span>
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { render, screen } from "@testing-library/svelte";
|
|
3
|
-
import StatusPill from "./StatusPill.svelte";
|
|
4
|
-
|
|
5
|
-
describe("StatusPill", () => {
|
|
6
|
-
it("renders default label 'passed' for status=success", () => {
|
|
7
|
-
render(StatusPill, { props: { status: "success" } });
|
|
8
|
-
expect(screen.getByText("passed")).toBeInTheDocument();
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
it("renders default label 'failed' for status=failure", () => {
|
|
12
|
-
render(StatusPill, { props: { status: "failure" } });
|
|
13
|
-
expect(screen.getByText("failed")).toBeInTheDocument();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it("renders default label 'running' for status=running", () => {
|
|
17
|
-
render(StatusPill, { props: { status: "running" } });
|
|
18
|
-
expect(screen.getByText("running")).toBeInTheDocument();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it("renders default label 'pending' for status=pending", () => {
|
|
22
|
-
render(StatusPill, { props: { status: "pending" } });
|
|
23
|
-
expect(screen.getByText("pending")).toBeInTheDocument();
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("renders default label 'cancelled' for status=cancelled", () => {
|
|
27
|
-
render(StatusPill, { props: { status: "cancelled" } });
|
|
28
|
-
expect(screen.getByText("cancelled")).toBeInTheDocument();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("renders default label 'warning' for status=warning", () => {
|
|
32
|
-
render(StatusPill, { props: { status: "warning" } });
|
|
33
|
-
expect(screen.getByText("warning")).toBeInTheDocument();
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("renders custom label when label prop is provided", () => {
|
|
37
|
-
render(StatusPill, { props: { status: "success", label: "Deployed" } });
|
|
38
|
-
expect(screen.getByText("Deployed")).toBeInTheDocument();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("custom label overrides the default label", () => {
|
|
42
|
-
render(StatusPill, { props: { status: "success", label: "Custom" } });
|
|
43
|
-
expect(screen.queryByText("passed")).not.toBeInTheDocument();
|
|
44
|
-
expect(screen.getByText("Custom")).toBeInTheDocument();
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("size='xs' applies text-[10px] class", () => {
|
|
48
|
-
const { container } = render(StatusPill, { props: { status: "success", size: "xs" } });
|
|
49
|
-
const span = container.querySelector("span");
|
|
50
|
-
expect(span).not.toBeNull();
|
|
51
|
-
expect(span!.className).toContain("text-[10px]");
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it("size='xs' applies px-1.5 class", () => {
|
|
55
|
-
const { container } = render(StatusPill, { props: { status: "success", size: "xs" } });
|
|
56
|
-
const span = container.querySelector("span");
|
|
57
|
-
expect(span!.className).toContain("px-1.5");
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it("size='sm' (default) applies text-[11px] class", () => {
|
|
61
|
-
const { container } = render(StatusPill, { props: { status: "success" } });
|
|
62
|
-
const span = container.querySelector("span");
|
|
63
|
-
expect(span!.className).toContain("text-[11px]");
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("size='sm' applies px-2 class", () => {
|
|
67
|
-
const { container } = render(StatusPill, { props: { status: "success", size: "sm" } });
|
|
68
|
-
const span = container.querySelector("span");
|
|
69
|
-
expect(span!.className).toContain("px-2");
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it("size='xs' does not apply text-[11px] class", () => {
|
|
73
|
-
const { container } = render(StatusPill, { props: { status: "running", size: "xs" } });
|
|
74
|
-
const span = container.querySelector("span");
|
|
75
|
-
expect(span!.className).not.toContain("text-[11px]");
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it("root element is a span", () => {
|
|
79
|
-
const { container } = render(StatusPill, { props: { status: "success" } });
|
|
80
|
-
expect(container.firstElementChild?.tagName.toLowerCase()).toBe("span");
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it("applies green color classes for success status", () => {
|
|
84
|
-
const { container } = render(StatusPill, { props: { status: "success" } });
|
|
85
|
-
const span = container.querySelector("span");
|
|
86
|
-
expect(span!.className).toContain("text-green-500");
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it("applies destructive color classes for failure status", () => {
|
|
90
|
-
const { container } = render(StatusPill, { props: { status: "failure" } });
|
|
91
|
-
const span = container.querySelector("span");
|
|
92
|
-
expect(span!.className).toContain("text-destructive");
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it("applies blue color classes for running status", () => {
|
|
96
|
-
const { container } = render(StatusPill, { props: { status: "running" } });
|
|
97
|
-
const span = container.querySelector("span");
|
|
98
|
-
expect(span!.className).toContain("text-blue-400");
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("applies yellow color classes for pending status", () => {
|
|
102
|
-
const { container } = render(StatusPill, { props: { status: "pending" } });
|
|
103
|
-
const span = container.querySelector("span");
|
|
104
|
-
expect(span!.className).toContain("text-yellow-500");
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("applies amber color classes for warning status", () => {
|
|
108
|
-
const { container } = render(StatusPill, { props: { status: "warning" } });
|
|
109
|
-
const span = container.querySelector("span");
|
|
110
|
-
expect(span!.className).toContain("text-amber-500");
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it("applies muted color classes for cancelled status", () => {
|
|
114
|
-
const { container } = render(StatusPill, { props: { status: "cancelled" } });
|
|
115
|
-
const span = container.querySelector("span");
|
|
116
|
-
expect(span!.className).toContain("text-muted-foreground");
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it("renders without crashing for all statuses", () => {
|
|
120
|
-
const statuses = ["success", "failure", "running", "pending", "cancelled", "warning"] as const;
|
|
121
|
-
for (const status of statuses) {
|
|
122
|
-
expect(() => render(StatusPill, { props: { status } })).not.toThrow();
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
});
|