@elizaos/client 1.5.5-alpha.10
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/LICENSE +21 -0
- package/README.md +350 -0
- package/dist/assets/empty-module-CLMscLYw.js +1 -0
- package/dist/assets/main-BBZ_3lkn.css +5999 -0
- package/dist/assets/main-C5zNUkXH.js +7 -0
- package/dist/assets/main-Dz64ENQg.js +614 -0
- package/dist/assets/react-vendor-DM5m98rr.js +545 -0
- package/dist/assets/ui-vendor-BQCqNqg0.js +1 -0
- package/dist/elizaos-avatar.png +0 -0
- package/dist/elizaos-icon.png +0 -0
- package/dist/elizaos-logo-light.png +0 -0
- package/dist/elizaos.webp +0 -0
- package/dist/favicon.ico +0 -0
- package/dist/images/agents/agent1.png +0 -0
- package/dist/images/agents/agent2.png +0 -0
- package/dist/images/agents/agent3.png +0 -0
- package/dist/images/agents/agent4.png +0 -0
- package/dist/images/agents/agent5.png +0 -0
- package/dist/index.html +14 -0
- package/index.html +24 -0
- package/package.json +159 -0
- package/postcss.config.js +3 -0
- package/public/elizaos-avatar.png +0 -0
- package/public/elizaos-icon.png +0 -0
- package/public/elizaos-logo-light.png +0 -0
- package/public/elizaos.webp +0 -0
- package/public/favicon.ico +0 -0
- package/public/images/agents/agent1.png +0 -0
- package/public/images/agents/agent2.png +0 -0
- package/public/images/agents/agent3.png +0 -0
- package/public/images/agents/agent4.png +0 -0
- package/public/images/agents/agent5.png +0 -0
- package/src/App.tsx +222 -0
- package/src/components/AgentDetailsPanel.tsx +147 -0
- package/src/components/ChatInputArea.tsx +196 -0
- package/src/components/ChatMessageListComponent.tsx +139 -0
- package/src/components/actionTool.tsx +186 -0
- package/src/components/add-agent-card.tsx +77 -0
- package/src/components/agent-action-viewer.tsx +816 -0
- package/src/components/agent-avatar-stack.tsx +121 -0
- package/src/components/agent-card.cy.tsx +259 -0
- package/src/components/agent-card.tsx +177 -0
- package/src/components/agent-creator.tsx +142 -0
- package/src/components/agent-log-viewer.tsx +645 -0
- package/src/components/agent-memory-edit-overlay.tsx +461 -0
- package/src/components/agent-memory-viewer.tsx +504 -0
- package/src/components/agent-settings.tsx +270 -0
- package/src/components/agent-sidebar.tsx +178 -0
- package/src/components/api-key-dialog.tsx +113 -0
- package/src/components/app-sidebar.tsx +685 -0
- package/src/components/array-input.tsx +116 -0
- package/src/components/audio-recorder.tsx +292 -0
- package/src/components/avatar-panel.tsx +141 -0
- package/src/components/character-form.tsx +1138 -0
- package/src/components/chat.tsx +1813 -0
- package/src/components/combobox.tsx +187 -0
- package/src/components/confirmation-dialog.tsx +59 -0
- package/src/components/connection-error-banner.tsx +101 -0
- package/src/components/connection-status.cy.tsx +73 -0
- package/src/components/connection-status.tsx +155 -0
- package/src/components/copy-button.tsx +35 -0
- package/src/components/delete-button.tsx +24 -0
- package/src/components/env-settings.tsx +261 -0
- package/src/components/group-card.tsx +160 -0
- package/src/components/group-panel.tsx +543 -0
- package/src/components/input-copy.tsx +21 -0
- package/src/components/logs-page.tsx +41 -0
- package/src/components/media-content.tsx +385 -0
- package/src/components/memory-graph.tsx +170 -0
- package/src/components/missing-secrets-dialog.tsx +72 -0
- package/src/components/onboarding-tour.tsx +247 -0
- package/src/components/page-title.tsx +8 -0
- package/src/components/plugins-panel.tsx +383 -0
- package/src/components/profile-card.tsx +66 -0
- package/src/components/profile-overlay.tsx +283 -0
- package/src/components/retry-button.tsx +28 -0
- package/src/components/secret-panel.tsx +1505 -0
- package/src/components/server-management.tsx +264 -0
- package/src/components/split-button.tsx +148 -0
- package/src/components/stop-agent-button.tsx +99 -0
- package/src/components/ui/alert-dialog.cy.tsx +333 -0
- package/src/components/ui/alert-dialog.tsx +115 -0
- package/src/components/ui/alert.tsx +49 -0
- package/src/components/ui/avatar.cy.tsx +180 -0
- package/src/components/ui/avatar.tsx +57 -0
- package/src/components/ui/badge.cy.tsx +146 -0
- package/src/components/ui/badge.tsx +43 -0
- package/src/components/ui/button.cy.tsx +177 -0
- package/src/components/ui/button.tsx +56 -0
- package/src/components/ui/card.cy.tsx +160 -0
- package/src/components/ui/card.tsx +73 -0
- package/src/components/ui/chat/animated-markdown.tsx +59 -0
- package/src/components/ui/chat/chat-bubble.tsx +178 -0
- package/src/components/ui/chat/chat-container.tsx +51 -0
- package/src/components/ui/chat/chat-input.cy.tsx +169 -0
- package/src/components/ui/chat/chat-input.tsx +47 -0
- package/src/components/ui/chat/chat-message-list.tsx +61 -0
- package/src/components/ui/chat/chat-tts-button.tsx +199 -0
- package/src/components/ui/chat/code-block.tsx +79 -0
- package/src/components/ui/chat/expandable-chat.tsx +131 -0
- package/src/components/ui/chat/hooks/useAutoScroll.ts +86 -0
- package/src/components/ui/chat/markdown.tsx +209 -0
- package/src/components/ui/chat/message-loading.tsx +48 -0
- package/src/components/ui/checkbox.cy.tsx +170 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/collapsible.cy.tsx +283 -0
- package/src/components/ui/collapsible.tsx +9 -0
- package/src/components/ui/command.cy.tsx +313 -0
- package/src/components/ui/command.tsx +143 -0
- package/src/components/ui/dialog.cy.tsx +279 -0
- package/src/components/ui/dialog.tsx +104 -0
- package/src/components/ui/dropdown-menu.cy.tsx +273 -0
- package/src/components/ui/dropdown-menu.tsx +281 -0
- package/src/components/ui/input.cy.tsx +82 -0
- package/src/components/ui/input.tsx +27 -0
- package/src/components/ui/label.cy.tsx +157 -0
- package/src/components/ui/label.tsx +19 -0
- package/src/components/ui/resizable.tsx +42 -0
- package/src/components/ui/scroll-area.cy.tsx +242 -0
- package/src/components/ui/scroll-area.tsx +46 -0
- package/src/components/ui/select.cy.tsx +277 -0
- package/src/components/ui/select.tsx +155 -0
- package/src/components/ui/separator.cy.tsx +145 -0
- package/src/components/ui/separator.tsx +29 -0
- package/src/components/ui/sheet.cy.tsx +324 -0
- package/src/components/ui/sheet.tsx +119 -0
- package/src/components/ui/sidebar.tsx +734 -0
- package/src/components/ui/skeleton.cy.tsx +149 -0
- package/src/components/ui/skeleton.tsx +17 -0
- package/src/components/ui/split-button.cy.tsx +274 -0
- package/src/components/ui/split-button.tsx +112 -0
- package/src/components/ui/switch.tsx +28 -0
- package/src/components/ui/tabs.cy.tsx +271 -0
- package/src/components/ui/tabs.tsx +53 -0
- package/src/components/ui/textarea.cy.tsx +136 -0
- package/src/components/ui/textarea.tsx +26 -0
- package/src/components/ui/toast.cy.tsx +209 -0
- package/src/components/ui/toast.tsx +126 -0
- package/src/components/ui/toaster.tsx +29 -0
- package/src/components/ui/tooltip.cy.tsx +244 -0
- package/src/components/ui/tooltip.tsx +30 -0
- package/src/config/agent-templates.ts +349 -0
- package/src/config/voice-models.ts +181 -0
- package/src/constants.ts +23 -0
- package/src/context/AuthContext.tsx +44 -0
- package/src/context/ConnectionContext.tsx +194 -0
- package/src/entry.tsx +9 -0
- package/src/hooks/__tests__/use-agent-tab-state.test.ts +137 -0
- package/src/hooks/__tests__/use-agent-update.test.tsx +250 -0
- package/src/hooks/__tests__/use-character-convert.test.ts +102 -0
- package/src/hooks/__tests__/use-panel-width-state.test.ts +243 -0
- package/src/hooks/__tests__/use-sidebar-state.test.ts +117 -0
- package/src/hooks/use-agent-management.ts +130 -0
- package/src/hooks/use-agent-tab-state.ts +74 -0
- package/src/hooks/use-agent-update.ts +469 -0
- package/src/hooks/use-character-convert.ts +138 -0
- package/src/hooks/use-confirmation.ts +55 -0
- package/src/hooks/use-delete-agent.ts +123 -0
- package/src/hooks/use-dm-channels.ts +198 -0
- package/src/hooks/use-elevenlabs-voices.ts +83 -0
- package/src/hooks/use-file-upload.ts +224 -0
- package/src/hooks/use-mobile.tsx +19 -0
- package/src/hooks/use-onboarding.tsx +49 -0
- package/src/hooks/use-panel-width-state.ts +147 -0
- package/src/hooks/use-partial-update.ts +288 -0
- package/src/hooks/use-plugin-details.ts +462 -0
- package/src/hooks/use-plugins.ts +119 -0
- package/src/hooks/use-query-hooks.ts +1263 -0
- package/src/hooks/use-server-agents.ts +62 -0
- package/src/hooks/use-server-version.tsx +47 -0
- package/src/hooks/use-sidebar-state.ts +50 -0
- package/src/hooks/use-socket-chat.ts +264 -0
- package/src/hooks/use-toast.ts +260 -0
- package/src/hooks/use-version.tsx +64 -0
- package/src/index.css +146 -0
- package/src/lib/api-client-config.ts +53 -0
- package/src/lib/api-type-mappers.ts +196 -0
- package/src/lib/export-utils.ts +123 -0
- package/src/lib/logger.ts +19 -0
- package/src/lib/media-utils.ts +170 -0
- package/src/lib/pca.test.ts +17 -0
- package/src/lib/pca.ts +52 -0
- package/src/lib/socketio-manager.ts +664 -0
- package/src/lib/utils.ts +168 -0
- package/src/main.tsx +16 -0
- package/src/mocks/empty-module.ts +12 -0
- package/src/mocks/node-module.ts +57 -0
- package/src/polyfills.ts +37 -0
- package/src/routes/agent-detail.tsx +30 -0
- package/src/routes/agent-list.tsx +27 -0
- package/src/routes/agent-settings.tsx +48 -0
- package/src/routes/character-detail.tsx +52 -0
- package/src/routes/character-form.tsx +79 -0
- package/src/routes/character-list.tsx +38 -0
- package/src/routes/chat.tsx +128 -0
- package/src/routes/createAgent.tsx +13 -0
- package/src/routes/group-new.tsx +50 -0
- package/src/routes/group.tsx +29 -0
- package/src/routes/home.tsx +218 -0
- package/src/routes/not-found.tsx +71 -0
- package/src/test/setup.ts +154 -0
- package/src/types/crypto-browserify.d.ts +4 -0
- package/src/types/index.ts +13 -0
- package/src/types/rooms.ts +8 -0
- package/src/types.ts +84 -0
- package/src/vite-env.d.ts +40 -0
- package/tailwind.config.ts +90 -0
- package/tsconfig.json +10 -0
- package/vite.config.ts +102 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// @hidden
|
|
2
|
+
export default function MessageLoading() {
|
|
3
|
+
return (
|
|
4
|
+
<svg
|
|
5
|
+
role="img"
|
|
6
|
+
aria-label="Loading animation"
|
|
7
|
+
width="24"
|
|
8
|
+
height="24"
|
|
9
|
+
viewBox="0 0 24 24"
|
|
10
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
11
|
+
className="text-foreground"
|
|
12
|
+
data-testid="loading"
|
|
13
|
+
>
|
|
14
|
+
<circle cx="4" cy="12" r="2" fill="currentColor">
|
|
15
|
+
<animate
|
|
16
|
+
id="spinner_qFRN"
|
|
17
|
+
begin="0;spinner_OcgL.end+0.25s"
|
|
18
|
+
attributeName="cy"
|
|
19
|
+
calcMode="spline"
|
|
20
|
+
dur="0.6s"
|
|
21
|
+
values="12;6;12"
|
|
22
|
+
keySplines=".33,.66,.66,1;.33,0,.66,.33"
|
|
23
|
+
/>
|
|
24
|
+
</circle>
|
|
25
|
+
<circle cx="12" cy="12" r="2" fill="currentColor">
|
|
26
|
+
<animate
|
|
27
|
+
begin="spinner_qFRN.begin+0.1s"
|
|
28
|
+
attributeName="cy"
|
|
29
|
+
calcMode="spline"
|
|
30
|
+
dur="0.6s"
|
|
31
|
+
values="12;6;12"
|
|
32
|
+
keySplines=".33,.66,.66,1;.33,0,.66,.33"
|
|
33
|
+
/>
|
|
34
|
+
</circle>
|
|
35
|
+
<circle cx="20" cy="12" r="2" fill="currentColor">
|
|
36
|
+
<animate
|
|
37
|
+
id="spinner_OcgL"
|
|
38
|
+
begin="spinner_qFRN.begin+0.2s"
|
|
39
|
+
attributeName="cy"
|
|
40
|
+
calcMode="spline"
|
|
41
|
+
dur="0.6s"
|
|
42
|
+
values="12;6;12"
|
|
43
|
+
keySplines=".33,.66,.66,1;.33,0,.66,.33"
|
|
44
|
+
/>
|
|
45
|
+
</circle>
|
|
46
|
+
</svg>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
/// <reference path="../../../cypress/support/types.d.ts" />
|
|
3
|
+
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { Checkbox } from './checkbox';
|
|
6
|
+
|
|
7
|
+
describe('Checkbox Component', () => {
|
|
8
|
+
it('renders correctly with default props', () => {
|
|
9
|
+
cy.mount(<Checkbox />);
|
|
10
|
+
|
|
11
|
+
cy.get('[data-testid="checkbox"]').should('exist');
|
|
12
|
+
cy.get('[data-testid="checkbox"]').should('have.attr', 'role', 'checkbox');
|
|
13
|
+
cy.get('[data-testid="checkbox"]').should('have.attr', 'aria-checked', 'false');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('can be checked and unchecked', () => {
|
|
17
|
+
cy.mount(<Checkbox />);
|
|
18
|
+
|
|
19
|
+
// Initially unchecked
|
|
20
|
+
cy.get('[data-testid="checkbox"]').should('have.attr', 'aria-checked', 'false');
|
|
21
|
+
|
|
22
|
+
// Click to check
|
|
23
|
+
cy.get('[data-testid="checkbox"]').click();
|
|
24
|
+
cy.get('[data-testid="checkbox"]').should('have.attr', 'aria-checked', 'true');
|
|
25
|
+
|
|
26
|
+
// Click to uncheck
|
|
27
|
+
cy.get('[data-testid="checkbox"]').click();
|
|
28
|
+
cy.get('[data-testid="checkbox"]').should('have.attr', 'aria-checked', 'false');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('works as controlled component', () => {
|
|
32
|
+
const TestComponent = () => {
|
|
33
|
+
const [checked, setChecked] = React.useState(false);
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div>
|
|
37
|
+
<Checkbox
|
|
38
|
+
checked={checked}
|
|
39
|
+
onCheckedChange={(value) => {
|
|
40
|
+
if (typeof value === 'boolean') {
|
|
41
|
+
setChecked(value);
|
|
42
|
+
}
|
|
43
|
+
}}
|
|
44
|
+
/>
|
|
45
|
+
<p>{checked ? 'Checked' : 'Unchecked'}</p>
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
cy.mount(<TestComponent />);
|
|
51
|
+
|
|
52
|
+
cy.contains('Unchecked').should('exist');
|
|
53
|
+
cy.get('[data-testid="checkbox"]').click();
|
|
54
|
+
cy.contains('Checked').should('exist');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('can be disabled', () => {
|
|
58
|
+
cy.mount(<Checkbox disabled />);
|
|
59
|
+
|
|
60
|
+
cy.get('[data-testid="checkbox"]').should('be.disabled');
|
|
61
|
+
cy.get('[data-testid="checkbox"]').should('have.attr', 'data-disabled');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('supports indeterminate state', () => {
|
|
65
|
+
cy.mount(<Checkbox checked="indeterminate" />);
|
|
66
|
+
|
|
67
|
+
cy.get('[data-testid="checkbox"]').should('have.attr', 'aria-checked', 'mixed');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('applies custom className', () => {
|
|
71
|
+
cy.mount(<Checkbox className="custom-checkbox h-6 w-6" />);
|
|
72
|
+
|
|
73
|
+
cy.get('[data-testid="checkbox"]')
|
|
74
|
+
.should('have.class', 'custom-checkbox')
|
|
75
|
+
.should('have.class', 'h-6')
|
|
76
|
+
.should('have.class', 'w-6');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('works with labels', () => {
|
|
80
|
+
cy.mount(
|
|
81
|
+
<div className="flex items-center space-x-2">
|
|
82
|
+
<Checkbox id="terms" />
|
|
83
|
+
<label
|
|
84
|
+
htmlFor="terms"
|
|
85
|
+
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
86
|
+
>
|
|
87
|
+
Accept terms and conditions
|
|
88
|
+
</label>
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
// Clicking the label should check the checkbox
|
|
93
|
+
cy.get('label').click();
|
|
94
|
+
cy.get('[data-testid="checkbox"]').should('have.attr', 'aria-checked', 'true');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('handles onCheckedChange callback', () => {
|
|
98
|
+
const onCheckedChange = cy.stub();
|
|
99
|
+
|
|
100
|
+
cy.mount(<Checkbox onCheckedChange={onCheckedChange} />);
|
|
101
|
+
|
|
102
|
+
cy.get('[data-testid="checkbox"]').click();
|
|
103
|
+
cy.wrap(onCheckedChange).should('have.been.calledWith', true);
|
|
104
|
+
|
|
105
|
+
cy.get('[data-testid="checkbox"]').click();
|
|
106
|
+
cy.wrap(onCheckedChange).should('have.been.calledWith', false);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('renders multiple checkboxes in a form', () => {
|
|
110
|
+
cy.mount(
|
|
111
|
+
<form className="space-y-4">
|
|
112
|
+
<div className="flex items-center space-x-2">
|
|
113
|
+
<Checkbox id="option1" />
|
|
114
|
+
<label htmlFor="option1">Option 1</label>
|
|
115
|
+
</div>
|
|
116
|
+
<div className="flex items-center space-x-2">
|
|
117
|
+
<Checkbox id="option2" />
|
|
118
|
+
<label htmlFor="option2">Option 2</label>
|
|
119
|
+
</div>
|
|
120
|
+
<div className="flex items-center space-x-2">
|
|
121
|
+
<Checkbox id="option3" />
|
|
122
|
+
<label htmlFor="option3">Option 3</label>
|
|
123
|
+
</div>
|
|
124
|
+
</form>
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
cy.get('[data-testid="checkbox"]').should('have.length', 3);
|
|
128
|
+
|
|
129
|
+
// Check first and third options
|
|
130
|
+
cy.get('#option1').click();
|
|
131
|
+
cy.get('#option3').click();
|
|
132
|
+
|
|
133
|
+
cy.get('#option1').should('have.attr', 'aria-checked', 'true');
|
|
134
|
+
cy.get('#option2').should('have.attr', 'aria-checked', 'false');
|
|
135
|
+
cy.get('#option3').should('have.attr', 'aria-checked', 'true');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('supports keyboard navigation', () => {
|
|
139
|
+
cy.mount(<Checkbox />);
|
|
140
|
+
|
|
141
|
+
// Initially unchecked
|
|
142
|
+
cy.get('[data-testid="checkbox"]').should('have.attr', 'aria-checked', 'false');
|
|
143
|
+
|
|
144
|
+
// Focus and verify focus
|
|
145
|
+
cy.get('[data-testid="checkbox"]').focus();
|
|
146
|
+
cy.get('[data-testid="checkbox"]').should('have.focus');
|
|
147
|
+
|
|
148
|
+
// Test that the checkbox is focusable and accessible via keyboard
|
|
149
|
+
// Note: Radix UI checkbox may handle space key internally
|
|
150
|
+
// For now, let's verify it's keyboard accessible
|
|
151
|
+
cy.get('[data-testid="checkbox"]')
|
|
152
|
+
.should('have.attr', 'role', 'checkbox')
|
|
153
|
+
.should('not.have.attr', 'disabled');
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('maintains visual focus indicator', () => {
|
|
157
|
+
cy.mount(<Checkbox />);
|
|
158
|
+
|
|
159
|
+
cy.get('[data-testid="checkbox"]').focus();
|
|
160
|
+
cy.get('[data-testid="checkbox"]').should('have.class', 'focus-visible:ring-1');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('supports data attributes', () => {
|
|
164
|
+
cy.mount(<Checkbox data-testid="custom-checkbox" data-field="agree" />);
|
|
165
|
+
|
|
166
|
+
cy.get('[data-testid="custom-checkbox"]')
|
|
167
|
+
.should('exist')
|
|
168
|
+
.should('have.attr', 'data-field', 'agree');
|
|
169
|
+
});
|
|
170
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
|
|
3
|
+
import { Check } from 'lucide-react';
|
|
4
|
+
|
|
5
|
+
import { cn } from '@/lib/utils';
|
|
6
|
+
|
|
7
|
+
interface CheckboxProps extends React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root> {
|
|
8
|
+
'data-testid'?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const Checkbox = React.forwardRef<React.ElementRef<typeof CheckboxPrimitive.Root>, CheckboxProps>(
|
|
12
|
+
({ className, ...props }, ref) => (
|
|
13
|
+
<CheckboxPrimitive.Root
|
|
14
|
+
ref={ref}
|
|
15
|
+
className={cn(
|
|
16
|
+
'peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
|
|
17
|
+
className
|
|
18
|
+
)}
|
|
19
|
+
data-testid={props['data-testid'] || 'checkbox'}
|
|
20
|
+
{...props}
|
|
21
|
+
>
|
|
22
|
+
<CheckboxPrimitive.Indicator className={cn('flex items-center justify-center text-current')}>
|
|
23
|
+
<Check className="h-4 w-4" />
|
|
24
|
+
</CheckboxPrimitive.Indicator>
|
|
25
|
+
</CheckboxPrimitive.Root>
|
|
26
|
+
)
|
|
27
|
+
);
|
|
28
|
+
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
|
|
29
|
+
|
|
30
|
+
export { Checkbox };
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
/// <reference path="../../../cypress/support/types.d.ts" />
|
|
3
|
+
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from './collapsible';
|
|
6
|
+
import { Button } from './button';
|
|
7
|
+
|
|
8
|
+
describe('Collapsible Component', () => {
|
|
9
|
+
it('renders basic collapsible', () => {
|
|
10
|
+
cy.mount(
|
|
11
|
+
<Collapsible>
|
|
12
|
+
<CollapsibleTrigger>Toggle Content</CollapsibleTrigger>
|
|
13
|
+
<CollapsibleContent>
|
|
14
|
+
<p>This is collapsible content</p>
|
|
15
|
+
</CollapsibleContent>
|
|
16
|
+
</Collapsible>
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
cy.contains('Toggle Content').should('be.visible');
|
|
20
|
+
cy.contains('This is collapsible content').should('not.exist');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('toggles content on click', () => {
|
|
24
|
+
cy.mount(
|
|
25
|
+
<Collapsible>
|
|
26
|
+
<CollapsibleTrigger asChild>
|
|
27
|
+
<Button variant="outline">Show More</Button>
|
|
28
|
+
</CollapsibleTrigger>
|
|
29
|
+
<CollapsibleContent>
|
|
30
|
+
<div className="p-4 border rounded">
|
|
31
|
+
<p>Additional content that can be toggled</p>
|
|
32
|
+
</div>
|
|
33
|
+
</CollapsibleContent>
|
|
34
|
+
</Collapsible>
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
// Initially collapsed
|
|
38
|
+
cy.contains('Additional content').should('not.exist');
|
|
39
|
+
|
|
40
|
+
// Click to expand
|
|
41
|
+
cy.contains('button', 'Show More').click();
|
|
42
|
+
cy.contains('Additional content').should('be.visible');
|
|
43
|
+
|
|
44
|
+
// Click to collapse
|
|
45
|
+
cy.contains('button', 'Show More').click();
|
|
46
|
+
cy.contains('Additional content').should('not.exist');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('works as controlled component', () => {
|
|
50
|
+
const TestComponent = () => {
|
|
51
|
+
const [open, setOpen] = React.useState(false);
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div>
|
|
55
|
+
<p>Is open: {open ? 'Yes' : 'No'}</p>
|
|
56
|
+
<Collapsible open={open} onOpenChange={setOpen}>
|
|
57
|
+
<CollapsibleTrigger>Toggle</CollapsibleTrigger>
|
|
58
|
+
<CollapsibleContent>
|
|
59
|
+
<p>Controlled content</p>
|
|
60
|
+
</CollapsibleContent>
|
|
61
|
+
</Collapsible>
|
|
62
|
+
<button onClick={() => setOpen(!open)}>External Toggle</button>
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
cy.mount(<TestComponent />);
|
|
68
|
+
|
|
69
|
+
cy.contains('Is open: No').should('be.visible');
|
|
70
|
+
cy.contains('Controlled content').should('not.exist');
|
|
71
|
+
|
|
72
|
+
// Use external toggle
|
|
73
|
+
cy.contains('button', 'External Toggle').click();
|
|
74
|
+
cy.contains('Is open: Yes').should('be.visible');
|
|
75
|
+
cy.contains('Controlled content').should('be.visible');
|
|
76
|
+
|
|
77
|
+
// Use internal trigger
|
|
78
|
+
cy.contains('Toggle').click();
|
|
79
|
+
cy.contains('Is open: No').should('be.visible');
|
|
80
|
+
cy.contains('Controlled content').should('not.exist');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('supports default open state', () => {
|
|
84
|
+
cy.mount(
|
|
85
|
+
<Collapsible defaultOpen>
|
|
86
|
+
<CollapsibleTrigger>Toggle</CollapsibleTrigger>
|
|
87
|
+
<CollapsibleContent>
|
|
88
|
+
<p>Initially visible content</p>
|
|
89
|
+
</CollapsibleContent>
|
|
90
|
+
</Collapsible>
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
cy.contains('Initially visible content').should('be.visible');
|
|
94
|
+
|
|
95
|
+
// Click to collapse
|
|
96
|
+
cy.contains('Toggle').click();
|
|
97
|
+
cy.contains('Initially visible content').should('not.exist');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('can be disabled', () => {
|
|
101
|
+
cy.mount(
|
|
102
|
+
<Collapsible disabled>
|
|
103
|
+
<CollapsibleTrigger asChild>
|
|
104
|
+
<Button disabled>Disabled Toggle</Button>
|
|
105
|
+
</CollapsibleTrigger>
|
|
106
|
+
<CollapsibleContent>
|
|
107
|
+
<p>This content cannot be toggled</p>
|
|
108
|
+
</CollapsibleContent>
|
|
109
|
+
</Collapsible>
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
cy.contains('button', 'Disabled Toggle').should('be.disabled');
|
|
113
|
+
cy.contains('This content cannot be toggled').should('not.exist');
|
|
114
|
+
|
|
115
|
+
// Try to click
|
|
116
|
+
cy.contains('button', 'Disabled Toggle').click({ force: true });
|
|
117
|
+
cy.contains('This content cannot be toggled').should('not.exist');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('supports custom className', () => {
|
|
121
|
+
cy.mount(
|
|
122
|
+
<Collapsible className="custom-collapsible border p-4">
|
|
123
|
+
<CollapsibleTrigger className="custom-trigger text-blue-600">
|
|
124
|
+
Click to expand
|
|
125
|
+
</CollapsibleTrigger>
|
|
126
|
+
<CollapsibleContent className="custom-content mt-4">
|
|
127
|
+
<p>Styled content</p>
|
|
128
|
+
</CollapsibleContent>
|
|
129
|
+
</Collapsible>
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
cy.get('.custom-collapsible').should('have.class', 'border');
|
|
133
|
+
cy.get('.custom-trigger').should('have.class', 'text-blue-600');
|
|
134
|
+
|
|
135
|
+
cy.contains('Click to expand').click();
|
|
136
|
+
cy.get('.custom-content').should('have.class', 'mt-4');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('handles multiple collapsibles', () => {
|
|
140
|
+
cy.mount(
|
|
141
|
+
<div className="space-y-4">
|
|
142
|
+
<Collapsible>
|
|
143
|
+
<CollapsibleTrigger>First Section</CollapsibleTrigger>
|
|
144
|
+
<CollapsibleContent>
|
|
145
|
+
<p>First content</p>
|
|
146
|
+
</CollapsibleContent>
|
|
147
|
+
</Collapsible>
|
|
148
|
+
|
|
149
|
+
<Collapsible>
|
|
150
|
+
<CollapsibleTrigger>Second Section</CollapsibleTrigger>
|
|
151
|
+
<CollapsibleContent>
|
|
152
|
+
<p>Second content</p>
|
|
153
|
+
</CollapsibleContent>
|
|
154
|
+
</Collapsible>
|
|
155
|
+
</div>
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
// Both initially collapsed
|
|
159
|
+
cy.contains('First content').should('not.exist');
|
|
160
|
+
cy.contains('Second content').should('not.exist');
|
|
161
|
+
|
|
162
|
+
// Expand first
|
|
163
|
+
cy.contains('First Section').click();
|
|
164
|
+
cy.contains('First content').should('be.visible');
|
|
165
|
+
cy.contains('Second content').should('not.exist');
|
|
166
|
+
|
|
167
|
+
// Expand second
|
|
168
|
+
cy.contains('Second Section').click();
|
|
169
|
+
cy.contains('First content').should('be.visible');
|
|
170
|
+
cy.contains('Second content').should('be.visible');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('works with complex content', () => {
|
|
174
|
+
cy.mount(
|
|
175
|
+
<Collapsible>
|
|
176
|
+
<div className="flex items-center justify-between">
|
|
177
|
+
<h3 className="font-semibold">Advanced Settings</h3>
|
|
178
|
+
<CollapsibleTrigger asChild>
|
|
179
|
+
<Button variant="ghost" size="sm">
|
|
180
|
+
<span>Show</span>
|
|
181
|
+
</Button>
|
|
182
|
+
</CollapsibleTrigger>
|
|
183
|
+
</div>
|
|
184
|
+
<CollapsibleContent>
|
|
185
|
+
<div className="mt-4 space-y-4">
|
|
186
|
+
<div>
|
|
187
|
+
<label>Option 1</label>
|
|
188
|
+
<input type="checkbox" />
|
|
189
|
+
</div>
|
|
190
|
+
<div>
|
|
191
|
+
<label>Option 2</label>
|
|
192
|
+
<input type="checkbox" />
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
</CollapsibleContent>
|
|
196
|
+
</Collapsible>
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
cy.contains('Advanced Settings').should('be.visible');
|
|
200
|
+
cy.get('input[type="checkbox"]').should('not.exist');
|
|
201
|
+
|
|
202
|
+
cy.contains('button', 'Show').click();
|
|
203
|
+
cy.get('input[type="checkbox"]').should('have.length', 2);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('animates smoothly', () => {
|
|
207
|
+
cy.mount(
|
|
208
|
+
<Collapsible>
|
|
209
|
+
<CollapsibleTrigger>Animate</CollapsibleTrigger>
|
|
210
|
+
<CollapsibleContent>
|
|
211
|
+
<div className="p-4 bg-gray-100">
|
|
212
|
+
<p>This content animates in and out</p>
|
|
213
|
+
</div>
|
|
214
|
+
</CollapsibleContent>
|
|
215
|
+
</Collapsible>
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
cy.contains('Animate').click();
|
|
219
|
+
cy.contains('This content animates').should('be.visible');
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('handles nested collapsibles', () => {
|
|
223
|
+
cy.mount(
|
|
224
|
+
<Collapsible>
|
|
225
|
+
<CollapsibleTrigger>Outer Collapsible</CollapsibleTrigger>
|
|
226
|
+
<CollapsibleContent>
|
|
227
|
+
<div className="p-4 border">
|
|
228
|
+
<p>Outer content</p>
|
|
229
|
+
<Collapsible>
|
|
230
|
+
<CollapsibleTrigger>Inner Collapsible</CollapsibleTrigger>
|
|
231
|
+
<CollapsibleContent>
|
|
232
|
+
<p className="p-4 bg-gray-100">Inner content</p>
|
|
233
|
+
</CollapsibleContent>
|
|
234
|
+
</Collapsible>
|
|
235
|
+
</div>
|
|
236
|
+
</CollapsibleContent>
|
|
237
|
+
</Collapsible>
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
// Both initially collapsed
|
|
241
|
+
cy.contains('Outer content').should('not.exist');
|
|
242
|
+
cy.contains('Inner content').should('not.exist');
|
|
243
|
+
|
|
244
|
+
// Expand outer
|
|
245
|
+
cy.contains('Outer Collapsible').click();
|
|
246
|
+
cy.contains('Outer content').should('be.visible');
|
|
247
|
+
cy.contains('Inner content').should('not.exist');
|
|
248
|
+
|
|
249
|
+
// Expand inner
|
|
250
|
+
cy.contains('Inner Collapsible').click();
|
|
251
|
+
cy.contains('Inner content').should('be.visible');
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('maintains state during content updates', () => {
|
|
255
|
+
const TestComponent = () => {
|
|
256
|
+
const [count, setCount] = React.useState(0);
|
|
257
|
+
|
|
258
|
+
return (
|
|
259
|
+
<div>
|
|
260
|
+
<button onClick={() => setCount(count + 1)}>Update Count: {count}</button>
|
|
261
|
+
<Collapsible>
|
|
262
|
+
<CollapsibleTrigger>Toggle</CollapsibleTrigger>
|
|
263
|
+
<CollapsibleContent>
|
|
264
|
+
<p>Content with count: {count}</p>
|
|
265
|
+
</CollapsibleContent>
|
|
266
|
+
</Collapsible>
|
|
267
|
+
</div>
|
|
268
|
+
);
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
cy.mount(<TestComponent />);
|
|
272
|
+
|
|
273
|
+
// Expand collapsible
|
|
274
|
+
cy.contains('Toggle').click();
|
|
275
|
+
cy.contains('Content with count: 0').should('be.visible');
|
|
276
|
+
|
|
277
|
+
// Update count
|
|
278
|
+
cy.contains('button', 'Update Count: 0').click();
|
|
279
|
+
|
|
280
|
+
// Content should still be visible with new count
|
|
281
|
+
cy.contains('Content with count: 1').should('be.visible');
|
|
282
|
+
});
|
|
283
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
|
|
2
|
+
|
|
3
|
+
const Collapsible = CollapsiblePrimitive.Root;
|
|
4
|
+
|
|
5
|
+
const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
|
|
6
|
+
|
|
7
|
+
const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent;
|
|
8
|
+
|
|
9
|
+
export { Collapsible, CollapsibleTrigger, CollapsibleContent };
|