@startsimpli/ui 0.4.7 → 0.4.9
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 +21 -23
- package/src/__mocks__/next/link.js +11 -0
- package/src/components/account/__tests__/account.test.tsx +315 -0
- package/src/components/command-palette/CommandGroup.tsx +23 -0
- package/src/components/command-palette/CommandPalette.tsx +183 -200
- package/src/components/command-palette/CommandResultItem.tsx +59 -0
- package/src/components/command-palette/__tests__/CommandGroup.test.tsx +81 -0
- package/src/components/command-palette/__tests__/CommandResultItem.test.tsx +166 -0
- package/src/components/command-palette/__tests__/command-palette-context.test.tsx +166 -0
- package/src/components/command-palette/__tests__/useCommandPaletteSearch.test.ts +271 -0
- package/src/components/command-palette/index.ts +6 -0
- package/src/components/command-palette/useCommandPaletteSearch.ts +114 -0
- package/src/components/compose/__tests__/compose.test.tsx +656 -0
- package/src/components/dashboard/PipelineFunnel.tsx +126 -0
- package/src/components/dashboard/TopCampaigns.tsx +132 -0
- package/src/components/dashboard/__tests__/dashboard.test.tsx +785 -0
- package/src/components/dashboard/index.ts +6 -0
- package/src/components/dialog/ConfirmDialog.tsx +72 -0
- package/src/components/dialog/__tests__/ConfirmDialog.test.tsx +126 -0
- package/src/components/dialog/index.ts +3 -0
- package/src/components/email-dialogs/__tests__/email-dialogs.test.tsx +982 -0
- package/src/components/email-editor/BlockRenderer.tsx +120 -0
- package/src/components/email-editor/__tests__/BlockRenderer.test.tsx +332 -0
- package/src/components/email-editor/__tests__/block-renderers.test.ts +624 -0
- package/src/components/email-editor/__tests__/email-html-renderer.test.ts +376 -0
- package/src/components/email-editor/blocks/__tests__/blocks.test.tsx +818 -0
- package/src/components/email-editor/editor-sidebar.tsx +6 -731
- package/src/components/email-editor/email-editor.tsx +78 -467
- package/src/components/email-editor/hooks/__tests__/useDragDrop.test.ts +355 -0
- package/src/components/email-editor/hooks/__tests__/useEmailEditorState.test.ts +551 -0
- package/src/components/email-editor/hooks/useDragDrop.ts +181 -0
- package/src/components/email-editor/hooks/useEmailEditorState.ts +426 -0
- package/src/components/email-editor/index.ts +1 -0
- package/src/components/email-editor/panels/BlockPropertyPanel.tsx +637 -0
- package/src/components/email-editor/panels/GlobalStylesPanel.tsx +108 -0
- package/src/components/email-editor/panels/SectionSettingsPanel.tsx +80 -0
- package/src/components/email-editor/panels/__tests__/BlockPropertyPanel.test.tsx +707 -0
- package/src/components/email-editor/panels/__tests__/GlobalStylesPanel.test.tsx +226 -0
- package/src/components/email-editor/panels/index.ts +3 -0
- package/src/components/enrichment/__tests__/enrichment.test.tsx +184 -0
- package/src/components/gantt/GanttBoardView.tsx +71 -0
- package/src/components/gantt/GanttChart.tsx +134 -881
- package/src/components/gantt/GanttFilterBar.tsx +100 -0
- package/src/components/gantt/GanttListView.tsx +63 -0
- package/src/components/gantt/GanttTimelineView.tsx +215 -0
- package/src/components/gantt/__tests__/GanttBoardView.test.tsx +305 -0
- package/src/components/gantt/__tests__/GanttFilterBar.test.tsx +544 -0
- package/src/components/gantt/__tests__/GanttListView.test.tsx +337 -0
- package/src/components/gantt/__tests__/GanttTimelineView.test.tsx +375 -0
- package/src/components/gantt/__tests__/gantt-utils.test.ts +341 -0
- package/src/components/gantt/__tests__/useGanttState.test.ts +535 -0
- package/src/components/gantt/hooks/useGanttState.ts +644 -0
- package/src/components/gantt/index.ts +10 -0
- package/src/components/integrations/__tests__/integrations.test.tsx +191 -0
- package/src/components/kanban/__tests__/kanban.test.tsx +157 -0
- package/src/components/lists/__tests__/lists.test.tsx +263 -0
- package/src/components/loading/__tests__/loading.test.tsx +114 -0
- package/src/components/navigation/__tests__/navigation.test.tsx +194 -0
- package/src/components/pipeline/__tests__/pipeline.test.tsx +169 -0
- package/src/components/safe-html.tsx +9 -8
- package/src/components/settings/__tests__/settings.test.tsx +181 -0
- package/src/components/wizard/__tests__/wizard.test.tsx +97 -0
|
@@ -12,3 +12,9 @@ export type { DashboardGridProps } from './DashboardGrid'
|
|
|
12
12
|
|
|
13
13
|
export { DashboardSection } from './DashboardSection'
|
|
14
14
|
export type { DashboardSectionProps } from './DashboardSection'
|
|
15
|
+
|
|
16
|
+
export { PipelineFunnel } from './PipelineFunnel'
|
|
17
|
+
export type { PipelineFunnelProps, FunnelStage } from './PipelineFunnel'
|
|
18
|
+
|
|
19
|
+
export { TopCampaigns } from './TopCampaigns'
|
|
20
|
+
export type { TopCampaignsProps, TopCampaign } from './TopCampaigns'
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { Loader2 } from 'lucide-react'
|
|
5
|
+
import { BaseDialog } from './BaseDialog'
|
|
6
|
+
import { Button } from '../ui/button'
|
|
7
|
+
|
|
8
|
+
export interface ConfirmDialogProps {
|
|
9
|
+
open: boolean
|
|
10
|
+
onClose: () => void
|
|
11
|
+
onConfirm: () => void | Promise<void>
|
|
12
|
+
title: string
|
|
13
|
+
description?: string | React.ReactNode
|
|
14
|
+
confirmLabel?: string
|
|
15
|
+
cancelLabel?: string
|
|
16
|
+
destructive?: boolean
|
|
17
|
+
loading?: boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function ConfirmDialog({
|
|
21
|
+
open,
|
|
22
|
+
onClose,
|
|
23
|
+
onConfirm,
|
|
24
|
+
title,
|
|
25
|
+
description,
|
|
26
|
+
confirmLabel = 'Confirm',
|
|
27
|
+
cancelLabel = 'Cancel',
|
|
28
|
+
destructive = false,
|
|
29
|
+
loading = false,
|
|
30
|
+
}: ConfirmDialogProps) {
|
|
31
|
+
const handleClose = () => {
|
|
32
|
+
if (loading) return
|
|
33
|
+
onClose()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<BaseDialog
|
|
38
|
+
open={open}
|
|
39
|
+
onOpenChange={(o) => {
|
|
40
|
+
if (!o) handleClose()
|
|
41
|
+
}}
|
|
42
|
+
size="sm"
|
|
43
|
+
loading={loading}
|
|
44
|
+
>
|
|
45
|
+
<BaseDialog.Header>
|
|
46
|
+
<BaseDialog.Title>{title}</BaseDialog.Title>
|
|
47
|
+
</BaseDialog.Header>
|
|
48
|
+
{description && (
|
|
49
|
+
<BaseDialog.Body>
|
|
50
|
+
{typeof description === 'string' ? (
|
|
51
|
+
<p className="text-sm text-gray-600">{description}</p>
|
|
52
|
+
) : (
|
|
53
|
+
description
|
|
54
|
+
)}
|
|
55
|
+
</BaseDialog.Body>
|
|
56
|
+
)}
|
|
57
|
+
<BaseDialog.Footer>
|
|
58
|
+
<Button variant="outline" onClick={handleClose} disabled={loading}>
|
|
59
|
+
{cancelLabel}
|
|
60
|
+
</Button>
|
|
61
|
+
<Button
|
|
62
|
+
variant={destructive ? 'destructive' : 'default'}
|
|
63
|
+
onClick={onConfirm}
|
|
64
|
+
disabled={loading}
|
|
65
|
+
>
|
|
66
|
+
{loading && <Loader2 className="w-4 h-4 animate-spin mr-2" />}
|
|
67
|
+
{confirmLabel}
|
|
68
|
+
</Button>
|
|
69
|
+
</BaseDialog.Footer>
|
|
70
|
+
</BaseDialog>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
|
|
2
|
+
import { ConfirmDialog } from '../ConfirmDialog'
|
|
3
|
+
|
|
4
|
+
// Mock Radix portal to render inline
|
|
5
|
+
jest.mock('@radix-ui/react-dialog', () => {
|
|
6
|
+
const actual = jest.requireActual('@radix-ui/react-dialog')
|
|
7
|
+
return {
|
|
8
|
+
...actual,
|
|
9
|
+
Portal: ({ children }: { children: React.ReactNode }) => <>{children}</>,
|
|
10
|
+
}
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
describe('ConfirmDialog', () => {
|
|
14
|
+
const defaultProps = {
|
|
15
|
+
open: true,
|
|
16
|
+
onClose: jest.fn(),
|
|
17
|
+
onConfirm: jest.fn(),
|
|
18
|
+
title: 'Delete item?',
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
jest.clearAllMocks()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('renders title', () => {
|
|
26
|
+
render(<ConfirmDialog {...defaultProps} />)
|
|
27
|
+
expect(screen.getByText('Delete item?')).toBeInTheDocument()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('renders string description', () => {
|
|
31
|
+
render(
|
|
32
|
+
<ConfirmDialog {...defaultProps} description="This cannot be undone." />
|
|
33
|
+
)
|
|
34
|
+
expect(screen.getByText('This cannot be undone.')).toBeInTheDocument()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('renders ReactNode description', () => {
|
|
38
|
+
render(
|
|
39
|
+
<ConfirmDialog
|
|
40
|
+
{...defaultProps}
|
|
41
|
+
description={<span data-testid="custom">Custom content</span>}
|
|
42
|
+
/>
|
|
43
|
+
)
|
|
44
|
+
expect(screen.getByTestId('custom')).toBeInTheDocument()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('renders default button labels', () => {
|
|
48
|
+
render(<ConfirmDialog {...defaultProps} />)
|
|
49
|
+
expect(screen.getByText('Confirm')).toBeInTheDocument()
|
|
50
|
+
expect(screen.getByText('Cancel')).toBeInTheDocument()
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('renders custom button labels', () => {
|
|
54
|
+
render(
|
|
55
|
+
<ConfirmDialog
|
|
56
|
+
{...defaultProps}
|
|
57
|
+
confirmLabel="Delete"
|
|
58
|
+
cancelLabel="Keep"
|
|
59
|
+
/>
|
|
60
|
+
)
|
|
61
|
+
expect(screen.getByText('Delete')).toBeInTheDocument()
|
|
62
|
+
expect(screen.getByText('Keep')).toBeInTheDocument()
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('calls onConfirm when confirm button is clicked', () => {
|
|
66
|
+
render(<ConfirmDialog {...defaultProps} />)
|
|
67
|
+
fireEvent.click(screen.getByText('Confirm'))
|
|
68
|
+
expect(defaultProps.onConfirm).toHaveBeenCalledTimes(1)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('calls onClose when cancel button is clicked', () => {
|
|
72
|
+
render(<ConfirmDialog {...defaultProps} />)
|
|
73
|
+
fireEvent.click(screen.getByText('Cancel'))
|
|
74
|
+
expect(defaultProps.onClose).toHaveBeenCalledTimes(1)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('disables buttons when loading', () => {
|
|
78
|
+
render(<ConfirmDialog {...defaultProps} loading />)
|
|
79
|
+
const confirmBtn = screen.getByText('Confirm').closest('button')
|
|
80
|
+
const cancelBtn = screen.getByText('Cancel').closest('button')
|
|
81
|
+
expect(confirmBtn).toBeDisabled()
|
|
82
|
+
expect(cancelBtn).toBeDisabled()
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('shows spinner when loading', () => {
|
|
86
|
+
const { container } = render(<ConfirmDialog {...defaultProps} loading />)
|
|
87
|
+
const spinner = container.querySelector('.animate-spin')
|
|
88
|
+
expect(spinner).toBeInTheDocument()
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('does not call onClose when cancel clicked during loading', () => {
|
|
92
|
+
render(<ConfirmDialog {...defaultProps} loading />)
|
|
93
|
+
// The button is disabled so the click handler won't fire,
|
|
94
|
+
// but also handleClose guards against it
|
|
95
|
+
const cancelBtn = screen.getByText('Cancel').closest('button')!
|
|
96
|
+
fireEvent.click(cancelBtn)
|
|
97
|
+
expect(defaultProps.onClose).not.toHaveBeenCalled()
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('applies destructive variant to confirm button', () => {
|
|
101
|
+
render(<ConfirmDialog {...defaultProps} destructive />)
|
|
102
|
+
const confirmBtn = screen.getByText('Confirm').closest('button')
|
|
103
|
+
expect(confirmBtn?.className).toMatch(/destructive/)
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('applies default variant when not destructive', () => {
|
|
107
|
+
render(<ConfirmDialog {...defaultProps} />)
|
|
108
|
+
const confirmBtn = screen.getByText('Confirm').closest('button')
|
|
109
|
+
expect(confirmBtn?.className).not.toMatch(/destructive/)
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('does not render body when no description provided', () => {
|
|
113
|
+
const { container } = render(<ConfirmDialog {...defaultProps} />)
|
|
114
|
+
// Body has class py-4, should not exist
|
|
115
|
+
expect(container.querySelector('.py-4')).not.toBeInTheDocument()
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('handles async onConfirm', async () => {
|
|
119
|
+
const asyncConfirm = jest.fn().mockResolvedValue(undefined)
|
|
120
|
+
render(<ConfirmDialog {...defaultProps} onConfirm={asyncConfirm} />)
|
|
121
|
+
fireEvent.click(screen.getByText('Confirm'))
|
|
122
|
+
await waitFor(() => {
|
|
123
|
+
expect(asyncConfirm).toHaveBeenCalledTimes(1)
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
})
|