@nucel/ui 0.10.0 → 0.12.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.
Files changed (30) hide show
  1. package/README.md +21 -1
  2. package/package.json +29 -5
  3. package/src/lib/components/ui/Alert.svelte +47 -0
  4. package/src/lib/components/ui/AppCard.svelte +76 -0
  5. package/src/lib/components/ui/BranchPill.svelte +19 -0
  6. package/src/lib/components/ui/CommentPill.svelte +12 -0
  7. package/src/lib/components/ui/KanbanBoard.svelte +27 -0
  8. package/src/lib/components/ui/KanbanCard.svelte +43 -0
  9. package/src/lib/components/ui/KanbanColumn.svelte +52 -0
  10. package/src/lib/components/ui/ListCard.svelte +9 -0
  11. package/src/lib/components/ui/PageHeader.svelte +25 -0
  12. package/src/lib/components/ui/PermissionChips.svelte +49 -0
  13. package/src/lib/components/ui/Section.svelte +21 -0
  14. package/src/lib/components/ui/SectionTitle.svelte +16 -0
  15. package/src/lib/components/ui/StatusPill.svelte +54 -0
  16. package/src/lib/components/ui/editor/RichEditor.svelte +580 -0
  17. package/src/lib/components/ui/editor/mention-suggestion.ts +144 -0
  18. package/src/lib/components/ui/table/Table.svelte +12 -0
  19. package/src/lib/components/ui/table/TableBody.svelte +10 -0
  20. package/src/lib/components/ui/table/TableCaption.svelte +10 -0
  21. package/src/lib/components/ui/table/TableCell.svelte +10 -0
  22. package/src/lib/components/ui/table/TableHead.svelte +10 -0
  23. package/src/lib/components/ui/table/TableHeader.svelte +10 -0
  24. package/src/lib/components/ui/table/TableRow.svelte +10 -0
  25. package/src/lib/components/ui/table/index.ts +7 -0
  26. package/src/lib/index.ts +50 -0
  27. package/src/lib/components/ColorInput.test.ts +0 -126
  28. package/src/lib/components/CopyButton.test.ts +0 -213
  29. package/src/lib/components/IconButton.test.ts +0 -139
  30. package/src/lib/utils/cn.test.ts +0 -993
@@ -1,139 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { render } from '@testing-library/svelte';
3
- import { createRawSnippet } from 'svelte';
4
- import IconButton, { iconButtonVariants } from './IconButton.svelte';
5
-
6
- /** A trivial snippet to pass as the icon child. */
7
- const icon = createRawSnippet(() => ({
8
- render: () => `<svg data-testid="icon"></svg>`,
9
- }));
10
-
11
- describe('IconButton', () => {
12
- it('renders as a <button> by default', () => {
13
- const { container } = render(IconButton, {
14
- props: { 'aria-label': 'Notifications', children: icon },
15
- });
16
- const el = container.querySelector('[data-slot="icon-button"]');
17
- expect(el).toBeInTheDocument();
18
- expect(el?.tagName).toBe('BUTTON');
19
- expect(el).toHaveAttribute('type', 'button');
20
- });
21
-
22
- it('renders as an <a> when href is set', () => {
23
- const { container } = render(IconButton, {
24
- props: { 'aria-label': 'Go home', href: '/home', children: icon },
25
- });
26
- const el = container.querySelector('[data-slot="icon-button"]');
27
- expect(el?.tagName).toBe('A');
28
- expect(el).toHaveAttribute('href', '/home');
29
- });
30
-
31
- it('exposes the required aria-label on the control', () => {
32
- const { getByLabelText } = render(IconButton, {
33
- props: { 'aria-label': 'Search', children: icon },
34
- });
35
- expect(getByLabelText('Search')).toBeInTheDocument();
36
- });
37
-
38
- it('renders the icon child', () => {
39
- const { getByTestId } = render(IconButton, {
40
- props: { 'aria-label': 'Add', children: icon },
41
- });
42
- expect(getByTestId('icon')).toBeInTheDocument();
43
- });
44
-
45
- it('applies the default variant (ghost) and default size classes', () => {
46
- const { container } = render(IconButton, {
47
- props: { 'aria-label': 'Default', children: icon },
48
- });
49
- const el = container.querySelector('[data-slot="icon-button"]')!;
50
- // ghost variant => muted-foreground; default size => size-9
51
- expect(el.className).toContain('text-muted-foreground');
52
- expect(el.className).toContain('size-9');
53
- });
54
-
55
- it('applies the tap size (44px / size-11) for mobile hit targets', () => {
56
- const { container } = render(IconButton, {
57
- props: { 'aria-label': 'Tap', size: 'tap', children: icon },
58
- });
59
- const el = container.querySelector('[data-slot="icon-button"]')!;
60
- expect(el.className).toContain('size-11');
61
- });
62
-
63
- it.each([
64
- ['sm', 'size-8'],
65
- ['default', 'size-9'],
66
- ['lg', 'size-10'],
67
- ['tap', 'size-11'],
68
- ] as const)('applies the %s size class %s', (size, expectedClass) => {
69
- const { container } = render(IconButton, {
70
- props: { 'aria-label': 'Sized', size, children: icon },
71
- });
72
- const el = container.querySelector('[data-slot="icon-button"]')!;
73
- expect(el.className).toContain(expectedClass);
74
- });
75
-
76
- it.each([
77
- ['ghost', 'text-muted-foreground'],
78
- ['outline', 'border'],
79
- ['default', 'bg-primary'],
80
- ['secondary', 'bg-secondary'],
81
- ['destructive', 'text-destructive'],
82
- ] as const)('applies the %s variant classes', (variant, expectedClass) => {
83
- const { container } = render(IconButton, {
84
- props: { 'aria-label': 'Variant', variant, children: icon },
85
- });
86
- const el = container.querySelector('[data-slot="icon-button"]')!;
87
- expect(el.className).toContain(expectedClass);
88
- });
89
-
90
- it('disables the native button when disabled', () => {
91
- const { container } = render(IconButton, {
92
- props: { 'aria-label': 'Disabled', disabled: true, children: icon },
93
- });
94
- const el = container.querySelector('button')!;
95
- expect(el).toBeDisabled();
96
- });
97
-
98
- it('disables the anchor variant via aria-disabled and drops href', () => {
99
- const { container } = render(IconButton, {
100
- props: { 'aria-label': 'Disabled link', href: '/x', disabled: true, children: icon },
101
- });
102
- const el = container.querySelector('a')!;
103
- expect(el).toHaveAttribute('aria-disabled', 'true');
104
- expect(el).not.toHaveAttribute('href');
105
- expect(el).toHaveAttribute('role', 'link');
106
- expect(el).toHaveAttribute('tabindex', '-1');
107
- });
108
-
109
- it('merges a custom class with the variant classes', () => {
110
- const { container } = render(IconButton, {
111
- props: { 'aria-label': 'Custom', class: 'my-custom-class', children: icon },
112
- });
113
- const el = container.querySelector('[data-slot="icon-button"]')!;
114
- expect(el.className).toContain('my-custom-class');
115
- expect(el.className).toContain('size-9');
116
- });
117
-
118
- it('forwards arbitrary props (e.g. data-testid) to the element', () => {
119
- const { getByTestId } = render(IconButton, {
120
- props: { 'aria-label': 'Forwarded', 'data-testid': 'fwd', children: icon },
121
- });
122
- expect(getByTestId('fwd').tagName).toBe('BUTTON');
123
- });
124
- });
125
-
126
- describe('iconButtonVariants', () => {
127
- it('returns base classes plus default variant/size', () => {
128
- const cls = iconButtonVariants();
129
- expect(cls).toContain('inline-grid');
130
- expect(cls).toContain('text-muted-foreground'); // ghost default
131
- expect(cls).toContain('size-9'); // default size
132
- });
133
-
134
- it('honours explicit variant + size args', () => {
135
- const cls = iconButtonVariants({ variant: 'destructive', size: 'tap' });
136
- expect(cls).toContain('text-destructive');
137
- expect(cls).toContain('size-11');
138
- });
139
- });