@reshape-biotech/design-system 2.4.1 → 2.5.1

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 (80) hide show
  1. package/README.md +3 -0
  2. package/dist/app.css +2 -2
  3. package/dist/components/graphs/chart/Chart.svelte +5 -1
  4. package/dist/components/graphs/matrix/Matrix.svelte +2 -2
  5. package/dist/components/icons/AnalysisIcon.svelte +1 -2
  6. package/dist/components/icons/custom/Halo.svelte +3 -2
  7. package/dist/components/icons/custom/Halo.svelte.d.ts +1 -1
  8. package/dist/components/icons/custom/LightBottomHighIcon.svelte +26 -0
  9. package/dist/components/icons/custom/LightBottomHighIcon.svelte.d.ts +8 -0
  10. package/dist/components/icons/custom/LightBottomLowIcon.svelte +23 -0
  11. package/dist/components/icons/custom/LightBottomLowIcon.svelte.d.ts +8 -0
  12. package/dist/components/icons/custom/LightBottomMediumIcon.svelte +26 -0
  13. package/dist/components/icons/custom/LightBottomMediumIcon.svelte.d.ts +8 -0
  14. package/dist/components/icons/custom/LightTopHighIcon.svelte +26 -0
  15. package/dist/components/icons/custom/LightTopHighIcon.svelte.d.ts +8 -0
  16. package/dist/components/icons/custom/LightTopLowIcon.svelte +26 -0
  17. package/dist/components/icons/custom/LightTopLowIcon.svelte.d.ts +8 -0
  18. package/dist/components/icons/custom/LightTopMediumIcon.svelte +26 -0
  19. package/dist/components/icons/custom/LightTopMediumIcon.svelte.d.ts +8 -0
  20. package/dist/components/icons/custom/Well.svelte +3 -2
  21. package/dist/components/icons/custom/Well.svelte.d.ts +1 -1
  22. package/dist/components/icons/custom/index.d.ts +9 -0
  23. package/dist/components/icons/custom/index.js +8 -0
  24. package/dist/components/icons/index.d.ts +8 -0
  25. package/dist/components/icons/index.js +2 -1
  26. package/dist/components/legend/Legend.stories.svelte +108 -0
  27. package/dist/components/{button/Button.test.svelte.d.ts → legend/Legend.stories.svelte.d.ts} +4 -4
  28. package/dist/components/legend/components/legend-item.svelte +28 -0
  29. package/dist/components/legend/components/legend-item.svelte.d.ts +9 -0
  30. package/dist/components/legend/components/legend-root.svelte +24 -0
  31. package/dist/components/legend/components/legend-root.svelte.d.ts +8 -0
  32. package/dist/components/legend/index.d.ts +11 -0
  33. package/dist/components/legend/index.js +2 -0
  34. package/dist/components/segmented-control-buttons/ControlButton.svelte +3 -1
  35. package/dist/components/select/components/MultiSelectTrigger.svelte +5 -4
  36. package/dist/components/select/components/SelectContent.svelte +1 -1
  37. package/dist/components/sjsf-wrappers/SjsfNumberInputWrapper.svelte.d.ts +1 -1
  38. package/dist/components/sjsf-wrappers/SjsfSelectWidgetWrapper.svelte.d.ts +1 -1
  39. package/dist/components/sjsf-wrappers/SjsfTextInputWrapper.svelte.d.ts +1 -1
  40. package/dist/components/tag/Tag.svelte +1 -1
  41. package/dist/components/tooltip/Tooltip.svelte +3 -0
  42. package/dist/components/tooltip/Tooltip.svelte.d.ts +1 -0
  43. package/dist/index.d.ts +1 -0
  44. package/dist/index.js +1 -0
  45. package/dist/notifications.d.ts +2 -5
  46. package/dist/tailwind.preset.js +1 -1
  47. package/package.json +195 -189
  48. package/dist/components/activity/Activity.test.d.ts +0 -1
  49. package/dist/components/activity/Activity.test.js +0 -89
  50. package/dist/components/avatar/Avatar.test.d.ts +0 -1
  51. package/dist/components/avatar/Avatar.test.js +0 -55
  52. package/dist/components/button/Button.test.d.ts +0 -1
  53. package/dist/components/button/Button.test.js +0 -117
  54. package/dist/components/button/Button.test.svelte +0 -5
  55. package/dist/components/checkbox/Checkbox.test.d.ts +0 -1
  56. package/dist/components/checkbox/Checkbox.test.js +0 -51
  57. package/dist/components/graphs/matrix/Matrix.test.d.ts +0 -1
  58. package/dist/components/graphs/matrix/Matrix.test.js +0 -256
  59. package/dist/components/graphs/matrix/Matrix.test.svelte +0 -49
  60. package/dist/components/graphs/matrix/Matrix.test.svelte.d.ts +0 -4
  61. package/dist/components/input/Input.test.d.ts +0 -1
  62. package/dist/components/input/Input.test.js +0 -35
  63. package/dist/components/label/Label.test.d.ts +0 -1
  64. package/dist/components/label/Label.test.js +0 -29
  65. package/dist/components/manual-cfu-counter/test/ManualCFUCounter.test.d.ts +0 -1
  66. package/dist/components/manual-cfu-counter/test/ManualCFUCounter.test.js +0 -319
  67. package/dist/components/multi-cfu-counter/test/MultiCFUCounter.test.d.ts +0 -1
  68. package/dist/components/multi-cfu-counter/test/MultiCFUCounter.test.js +0 -320
  69. package/dist/components/stat-card/StatCard.test.d.ts +0 -1
  70. package/dist/components/stat-card/StatCard.test.js +0 -54
  71. package/dist/components/table/Table.test.d.ts +0 -1
  72. package/dist/components/table/Table.test.js +0 -97
  73. package/dist/components/table/Table.test.svelte +0 -37
  74. package/dist/components/table/Table.test.svelte.d.ts +0 -12
  75. package/dist/components/textarea/Textarea.test.d.ts +0 -1
  76. package/dist/components/textarea/Textarea.test.js +0 -90
  77. package/dist/components/toggle-icon-button/ToggleIconButton.test.d.ts +0 -1
  78. package/dist/components/toggle-icon-button/ToggleIconButton.test.js +0 -100
  79. package/dist/components/tooltip/Tooltip.test.d.ts +0 -1
  80. package/dist/components/tooltip/Tooltip.test.js +0 -81
@@ -1,54 +0,0 @@
1
- import { render, fireEvent, screen } from '@testing-library/svelte';
2
- import { tick } from 'svelte';
3
- import StatCard from './StatCard.svelte';
4
- import '@testing-library/jest-dom';
5
- import { describe, it, expect, vi } from 'vitest';
6
- describe('StatCard', () => {
7
- it('should focus the input field when the pencil edit icon is clicked', async () => {
8
- render(StatCard, {
9
- props: {
10
- title: 'Test Stat',
11
- value: 'Initial Value',
12
- editable: true,
13
- onsubmit: vi.fn(),
14
- },
15
- });
16
- const pencilButton = screen.getByRole('button');
17
- await fireEvent.click(pencilButton);
18
- // Wait for Svelte's reactivity and the $effect with tick() to apply focus
19
- await tick();
20
- const inputField = screen.getByRole('textbox');
21
- expect(inputField).toHaveFocus();
22
- });
23
- it('should focus the input field when the card body is clicked', async () => {
24
- render(StatCard, {
25
- props: {
26
- title: 'Clickable Stat',
27
- value: 'Initial Value',
28
- editable: true,
29
- onsubmit: vi.fn(),
30
- },
31
- });
32
- const cardBody = screen.getByTestId('stat-card-body');
33
- await fireEvent.click(cardBody);
34
- await tick(); // Wait for Svelte's reactivity
35
- const inputField = screen.getByRole('textbox');
36
- expect(inputField).toHaveFocus();
37
- });
38
- it('should not focus the input field when the card body is clicked, out of editable state', async () => {
39
- render(StatCard, {
40
- props: {
41
- title: 'Clickable Stat',
42
- value: 'Initial Value',
43
- editable: false,
44
- onsubmit: vi.fn(),
45
- },
46
- });
47
- const cardBody = screen.getByTestId('stat-card-body');
48
- await fireEvent.click(cardBody);
49
- await tick(); // Wait for Svelte's reactivity
50
- // When not editable, the input field should not be rendered
51
- const inputField = screen.queryByRole('textbox');
52
- expect(inputField).toBeNull();
53
- });
54
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,97 +0,0 @@
1
- import { render, screen } from '@testing-library/svelte';
2
- import Table from './Table.test.svelte';
3
- const users = [
4
- {
5
- name: 'John Doe',
6
- age: 25,
7
- role: 'admin',
8
- },
9
- {
10
- name: 'Jane Smith',
11
- age: 32,
12
- role: 'member',
13
- },
14
- {
15
- name: 'Michael Johnson',
16
- age: 41,
17
- role: 'deactivated',
18
- },
19
- {
20
- name: 'Emily Brown',
21
- age: 28,
22
- role: 'member',
23
- },
24
- {
25
- name: 'David Lee',
26
- age: 37,
27
- role: 'admin',
28
- },
29
- {
30
- name: 'Sarah Wilson',
31
- age: 29,
32
- role: 'member',
33
- },
34
- {
35
- name: 'Robert Taylor',
36
- age: 45,
37
- role: 'deactivated',
38
- },
39
- {
40
- name: 'Lisa Anderson',
41
- age: 33,
42
- role: 'member',
43
- },
44
- {
45
- name: 'James Martinez',
46
- age: 39,
47
- role: 'admin',
48
- },
49
- {
50
- name: 'Jennifer Garcia',
51
- age: 31,
52
- role: 'deactivated',
53
- },
54
- ];
55
- describe('Table Component', () => {
56
- it('renders the table with correct headers', () => {
57
- render(Table, { props: { users } });
58
- expect(screen.getByText('Name')).toBeInTheDocument();
59
- expect(screen.getByText('Age')).toBeInTheDocument();
60
- expect(screen.getByText('Role')).toBeInTheDocument();
61
- });
62
- it('renders table rows with user data', () => {
63
- render(Table, { props: { users } });
64
- users.forEach((user) => {
65
- expect(screen.getByTestId(`user-${user.name}`)).toBeInTheDocument();
66
- expect(screen.getByTestId(`user-${user.name}-age`)).toBeInTheDocument();
67
- expect(screen.getByTestId(`user-${user.name}-role`)).toBeInTheDocument();
68
- });
69
- });
70
- it('applies disabled class to deactivated users', () => {
71
- render(Table, { props: { users } });
72
- const deactivatedUser = screen.getByText('Michael Johnson');
73
- expect(deactivatedUser.closest('tr')).toHaveClass('disabled');
74
- });
75
- it('renders phone icon button for admin users', () => {
76
- render(Table, { props: { users, userRole: 'admin' } });
77
- const phoneIcon = screen.getAllByTestId('phone-icon');
78
- expect(phoneIcon).toHaveLength(users.length);
79
- });
80
- it('does not render phone icon button for non-admin users', () => {
81
- render(Table, { props: { users, userRole: 'member' } });
82
- const phoneIcon = screen.queryAllByTestId('phone-icon');
83
- expect(phoneIcon).toHaveLength(0);
84
- });
85
- it('applies disabled styles to td elements when disabled', () => {
86
- render(Table, { props: { users, userRole: 'member' } });
87
- const deactivatedUser = screen.getByText('Michael Johnson');
88
- const tr = deactivatedUser.closest('tr');
89
- if (tr) {
90
- expect(tr.classList.contains('disabled')).toBe(true);
91
- expect(tr.classList.contains('[&.disabled]:text-tertiary')).toBe(true);
92
- }
93
- else {
94
- throw new Error('Tr element not found');
95
- }
96
- });
97
- });
@@ -1,37 +0,0 @@
1
- <script lang="ts">
2
- import Table from './Table.svelte';
3
- import IconButton from '../icon-button/IconButton.svelte';
4
- import { Icon } from '../icons';
5
-
6
- interface Props {
7
- users?: { name: string; age: number; role: string }[];
8
- userRole?: 'admin' | 'member' | 'deactivated';
9
- }
10
-
11
- let { users = [], userRole = 'admin' }: Props = $props();
12
- </script>
13
-
14
- <Table>
15
- {#snippet children({ THead, TBody, Tr, Th, Td })}
16
- <THead>
17
- <Tr>
18
- <Th>Name</Th>
19
- <Th>Age</Th>
20
- <Th class="w-[100px]">Role</Th>
21
- <Th class="w-6"></Th>
22
- </Tr>
23
- </THead>
24
- <TBody>
25
- {#each users as user}
26
- <Tr disabled={user.role === 'deactivated'}>
27
- <Td dataTestId={`user-${user.name}`}>{user.name}</Td>
28
- <Td dataTestId={`user-${user.name}-age`}>{user.age}</Td>
29
- <Td dataTestId={`user-${user.name}-role`}>{user.role}</Td>
30
- {#if 'admin' === userRole}
31
- <Td dataTestId="phone-icon"><IconButton><Icon iconName="Phone" /></IconButton></Td>
32
- {/if}
33
- </Tr>
34
- {/each}
35
- </TBody>
36
- {/snippet}
37
- </Table>
@@ -1,12 +0,0 @@
1
- import Table from './Table.svelte';
2
- interface Props {
3
- users?: {
4
- name: string;
5
- age: number;
6
- role: string;
7
- }[];
8
- userRole?: 'admin' | 'member' | 'deactivated';
9
- }
10
- declare const Table: import("svelte").Component<Props, {}, "">;
11
- type Table = ReturnType<typeof Table>;
12
- export default Table;
@@ -1 +0,0 @@
1
- export {};
@@ -1,90 +0,0 @@
1
- import { render, screen, fireEvent } from '@testing-library/svelte';
2
- import Textarea from './Textarea.svelte';
3
- import { describe, it, expect, vi } from 'vitest';
4
- describe('Textarea component', () => {
5
- it('renders with default props', () => {
6
- render(Textarea, { props: { value: '' } });
7
- const textarea = screen.getByRole('textbox');
8
- expect(textarea).toBeInTheDocument();
9
- expect(textarea).not.toHaveClass('!border-error'); // Default valid state
10
- });
11
- it('binds the value correctly', async () => {
12
- const { component } = render(Textarea, { props: { value: 'Initial' } });
13
- const textarea = screen.getByRole('textbox');
14
- expect(textarea.value).toBe('Initial');
15
- // Simulate user typing
16
- await fireEvent.input(textarea, { target: { value: 'Changed' } });
17
- expect(textarea.value).toBe('Changed'); // Check the DOM element's value directly
18
- });
19
- it('applies placeholder, name, required, readonly, maxlength attributes', () => {
20
- render(Textarea, {
21
- props: {
22
- value: '',
23
- placeholder: 'Enter text',
24
- name: 'myTextarea',
25
- required: true,
26
- readonly: true,
27
- maxlength: 50,
28
- },
29
- });
30
- const textarea = screen.getByRole('textbox');
31
- expect(textarea).toHaveAttribute('placeholder', 'Enter text');
32
- expect(textarea).toHaveAttribute('name', 'myTextarea');
33
- expect(textarea).toBeRequired();
34
- expect(textarea).toHaveAttribute('readonly');
35
- expect(textarea).toHaveAttribute('maxlength', '50');
36
- });
37
- it('calls onInput, onBlur, onKeydown event handlers', async () => {
38
- const handleInput = vi.fn();
39
- const handleBlur = vi.fn();
40
- const handleKeydown = vi.fn();
41
- render(Textarea, {
42
- props: {
43
- value: '',
44
- oninput: handleInput,
45
- onblur: handleBlur,
46
- onkeydown: handleKeydown,
47
- },
48
- });
49
- const textarea = screen.getByRole('textbox');
50
- await fireEvent.input(textarea, { target: { value: 'test' } });
51
- expect(handleInput).toHaveBeenCalledTimes(1);
52
- await fireEvent.blur(textarea);
53
- expect(handleBlur).toHaveBeenCalledTimes(1);
54
- await fireEvent.keyDown(textarea, { key: 'Enter' });
55
- expect(handleKeydown).toHaveBeenCalledTimes(1);
56
- });
57
- it('auto-resizes correctly on input', async () => {
58
- // Mock scrollHeight to simulate content height
59
- const mockScrollHeight = 200;
60
- Object.defineProperty(HTMLElement.prototype, 'scrollHeight', {
61
- configurable: true,
62
- get() {
63
- return mockScrollHeight;
64
- },
65
- });
66
- render(Textarea, { props: { value: 'Short', size: 'dynamic' } });
67
- const textarea = screen.getByRole('textbox');
68
- // Simulate typing more content
69
- await fireEvent.input(textarea, {
70
- target: {
71
- value: 'This is a much longer line of text that should definitely exceed the initial height and cause a resize.',
72
- },
73
- });
74
- // Verify height was set to scrollHeight
75
- expect(textarea.style.height).toBe(`${mockScrollHeight}px`);
76
- });
77
- it('calls focus function when exported focus() is called', () => {
78
- const { component } = render(Textarea, { props: { value: '' } });
79
- const textarea = screen.getByRole('textbox');
80
- vi.spyOn(textarea, 'focus');
81
- component.focus();
82
- expect(textarea.focus).toHaveBeenCalled();
83
- });
84
- it('applies custom class', () => {
85
- const customClass = 'my-custom-textarea';
86
- render(Textarea, { props: { value: '', class: customClass } });
87
- const textarea = screen.getByRole('textbox');
88
- expect(textarea).toHaveClass(customClass);
89
- });
90
- });
@@ -1,100 +0,0 @@
1
- import { render, fireEvent } from '@testing-library/svelte';
2
- import { describe, it, expect, vi } from 'vitest';
3
- import ToggleIconButton from './ToggleIconButton.svelte';
4
- describe('ToggleIconButton component', () => {
5
- it('renders correctly', () => {
6
- const { getByRole } = render(ToggleIconButton);
7
- expect(getByRole('button')).toBeTruthy();
8
- });
9
- it('applies the correct variant class for secondary (default)', () => {
10
- const { getByRole } = render(ToggleIconButton);
11
- const button = getByRole('button');
12
- expect(button.classList.toString()).toContain('bg-neutral text-icon-primary');
13
- });
14
- it('applies the correct variant class for primary-inverse', () => {
15
- const { getByRole } = render(ToggleIconButton, {
16
- props: {
17
- variant: 'primary-inverse',
18
- },
19
- });
20
- const button = getByRole('button');
21
- expect(button.classList.toString()).toContain('bg-base-inverse text-icon-primary-inverse');
22
- });
23
- it('applies the active state class when isActive is true for secondary', () => {
24
- const { getByRole, container } = render(ToggleIconButton, {
25
- props: {
26
- variant: 'secondary',
27
- isActive: true,
28
- },
29
- });
30
- const activeElement = container.querySelector('div');
31
- expect(activeElement?.classList.toString()).toContain('bg-neutral-hover text-icon-primary');
32
- });
33
- it('applies the active state class when isActive is true for primary-inverse', () => {
34
- const { getByRole, container } = render(ToggleIconButton, {
35
- props: {
36
- variant: 'primary-inverse',
37
- isActive: true,
38
- },
39
- });
40
- const activeElement = container.querySelector('div');
41
- expect(activeElement?.classList.toString()).toContain('bg-accent-inverse text-icon-primary-inverse');
42
- });
43
- it('applies the size class', () => {
44
- const { getByRole } = render(ToggleIconButton, {
45
- props: {
46
- size: 'lg',
47
- },
48
- });
49
- const button = getByRole('button');
50
- expect(button.classList.toString()).toContain('h-12 w-12');
51
- });
52
- it('applies the rounded-full class when rounded is true', () => {
53
- const { getByRole } = render(ToggleIconButton, {
54
- props: {
55
- rounded: true,
56
- },
57
- });
58
- const button = getByRole('button');
59
- expect(button.classList.contains('rounded-full')).toBe(true);
60
- });
61
- it('applies the rounded-md class when rounded is false', () => {
62
- const { getByRole } = render(ToggleIconButton, {
63
- props: {
64
- rounded: false,
65
- },
66
- });
67
- const button = getByRole('button');
68
- expect(button.classList.contains('rounded-md')).toBe(true);
69
- });
70
- it('calls onclick handler when clicked', async () => {
71
- const handleClick = vi.fn();
72
- const { getByRole } = render(ToggleIconButton, {
73
- props: {
74
- onclick: handleClick,
75
- },
76
- });
77
- const button = getByRole('button');
78
- await fireEvent.click(button);
79
- expect(handleClick).toHaveBeenCalledTimes(1);
80
- });
81
- it('is disabled when disabled prop is true', () => {
82
- const { getByRole } = render(ToggleIconButton, {
83
- props: {
84
- disabled: true,
85
- },
86
- });
87
- const button = getByRole('button');
88
- expect(button.hasAttribute('disabled')).toBe(true);
89
- expect(button.classList.contains('disabled:bg-neutral')).toBe(true);
90
- });
91
- it('shows spinner when loading is true', () => {
92
- const { container } = render(ToggleIconButton, {
93
- props: {
94
- loading: true,
95
- },
96
- });
97
- // Spinner should be present
98
- expect(container.innerHTML).toContain('spinner');
99
- });
100
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,81 +0,0 @@
1
- import { render } from '@testing-library/svelte';
2
- import { describe, it, expect } from 'vitest';
3
- import TooltipTest from './TooltipTest.svelte';
4
- // We test via a wrapper component because testing Svelte 5 snippets directly is complex
5
- describe('Tooltip component', () => {
6
- it('renders trigger content when forced open', () => {
7
- const { getByText } = render(TooltipTest, {
8
- props: {
9
- forceOpen: true,
10
- triggerText: 'Hover me',
11
- contentText: 'Tooltip content',
12
- },
13
- });
14
- expect(getByText('Hover me')).toBeInTheDocument();
15
- expect(getByText('Tooltip content')).toBeInTheDocument();
16
- });
17
- it('applies custom class when forced open', () => {
18
- render(TooltipTest, {
19
- props: {
20
- forceOpen: true,
21
- class: 'custom-tooltip',
22
- triggerText: 'Hover me',
23
- contentText: 'Tooltip content',
24
- },
25
- });
26
- // Tooltip content is portaled to document.body, so we search the entire document
27
- const tooltipContent = document.querySelector('.custom-tooltip');
28
- expect(tooltipContent).toBeInTheDocument();
29
- });
30
- it('applies design system tokens correctly when forced open', () => {
31
- render(TooltipTest, {
32
- props: {
33
- forceOpen: true,
34
- triggerText: 'Hover me',
35
- contentText: 'Tooltip content',
36
- },
37
- });
38
- // Tooltip content is portaled to document.body
39
- const tooltipContent = document.querySelector('.bg-base-inverse');
40
- expect(tooltipContent).toBeInTheDocument();
41
- const textElement = document.querySelector('.text-primary-inverse');
42
- expect(textElement).toBeInTheDocument();
43
- });
44
- it('renders with arrow by default when forced open', () => {
45
- render(TooltipTest, {
46
- props: {
47
- forceOpen: true,
48
- triggerText: 'Hover me',
49
- contentText: 'Tooltip content',
50
- },
51
- });
52
- // Arrow is part of the portaled content - bits-ui renders it as an SVG
53
- const arrow = document.querySelector('svg[data-arrow]') || document.querySelector('[data-arrow]');
54
- expect(arrow).toBeInTheDocument();
55
- });
56
- it('hides arrow when showArrow is false', () => {
57
- render(TooltipTest, {
58
- props: {
59
- forceOpen: true,
60
- showArrow: false,
61
- triggerText: 'Hover me',
62
- contentText: 'Tooltip content',
63
- },
64
- });
65
- const arrow = document.querySelector('svg[data-arrow]') || document.querySelector('[data-arrow]');
66
- expect(arrow).toBeNull();
67
- });
68
- it('applies correct placement when forced open', () => {
69
- render(TooltipTest, {
70
- props: {
71
- forceOpen: true,
72
- placement: 'bottom',
73
- triggerText: 'Hover me',
74
- contentText: 'Tooltip content',
75
- },
76
- });
77
- // Check data attribute on portaled content
78
- const tooltipContent = document.querySelector('[data-side="bottom"]');
79
- expect(tooltipContent).toBeInTheDocument();
80
- });
81
- });