Skip to content

Commit 53f192e

Browse files
authored
Merge pull request #26 from olliethedev/feat/impoved-component-popover
feat: improved add-component-popover
2 parents a4b2870 + 772ef31 commit 53f192e

File tree

6 files changed

+619
-119
lines changed

6 files changed

+619
-119
lines changed

__tests__/add-component-popover.test.tsx

Lines changed: 102 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,11 @@ describe('AddComponentsPopover', () => {
9999
fireEvent.click(trigger);
100100

101101
await waitFor(() => {
102-
expect(screen.getByPlaceholderText('Add component')).toBeInTheDocument();
102+
expect(screen.getByPlaceholderText('Find components')).toBeInTheDocument();
103103
});
104104
});
105105

106-
it('should render component search input', async () => {
106+
it('should render component search input with search icon', async () => {
107107
render(
108108
<AddComponentsPopover parentLayerId="parent-1">
109109
<button>Add Component</button>
@@ -114,13 +114,13 @@ describe('AddComponentsPopover', () => {
114114
fireEvent.click(trigger);
115115

116116
await waitFor(() => {
117-
expect(screen.getByPlaceholderText('Add component')).toBeInTheDocument();
117+
expect(screen.getByPlaceholderText('Find components')).toBeInTheDocument();
118118
});
119119
});
120120
});
121121

122-
describe('Component Grouping', () => {
123-
it('should group components by their path', async () => {
122+
describe('Tab-based Component Grouping', () => {
123+
it('should render tabs for each component group', async () => {
124124
render(
125125
<AddComponentsPopover parentLayerId="parent-1">
126126
<button>Add Component</button>
@@ -131,14 +131,14 @@ describe('AddComponentsPopover', () => {
131131
fireEvent.click(trigger);
132132

133133
await waitFor(() => {
134-
// Should show groups
135-
expect(screen.getByText('@/components/ui')).toBeInTheDocument();
136-
expect(screen.getByText('@/custom/components')).toBeInTheDocument();
137-
expect(screen.getByText('other')).toBeInTheDocument();
134+
// Should show tabs for different groups - check by the formatted names
135+
expect(screen.getByText('Ui')).toBeInTheDocument();
136+
expect(screen.getByText('Components')).toBeInTheDocument();
137+
expect(screen.getByText('Other')).toBeInTheDocument();
138138
});
139139
});
140140

141-
it('should render components in their respective groups', async () => {
141+
it('should render components with grey box previews in active tab', async () => {
142142
render(
143143
<AddComponentsPopover parentLayerId="parent-1">
144144
<button>Add Component</button>
@@ -149,16 +149,56 @@ describe('AddComponentsPopover', () => {
149149
fireEvent.click(trigger);
150150

151151
await waitFor(() => {
152-
// UI components group
152+
// Components in the first tab should be visible by default
153153
expect(screen.getByText('Button')).toBeInTheDocument();
154154
expect(screen.getByText('Input')).toBeInTheDocument();
155155
expect(screen.getByText('Card')).toBeInTheDocument();
156-
157-
// Custom components group
158-
expect(screen.getByText('CustomComponent')).toBeInTheDocument();
159-
160-
// Other group (no from path)
161-
expect(screen.getByText('UnGroupedComponent')).toBeInTheDocument();
156+
});
157+
158+
// Check for grey box previews (muted background divs) - but they might be lazy loaded
159+
await waitFor(() => {
160+
const greyBoxes = screen.getAllByRole('generic').filter(el =>
161+
el.className.includes('bg-muted') || el.className.includes('rounded border')
162+
);
163+
expect(greyBoxes.length).toBeGreaterThan(0);
164+
});
165+
});
166+
167+
it('should limit tabs to maximum of 3', async () => {
168+
// Mock registry with more than 3 groups
169+
const registryWithManyGroups = {
170+
...mockRegistry,
171+
ExtraComponent1: {
172+
component: () => null,
173+
name: 'ExtraComponent1',
174+
from: '@/group1/component'
175+
},
176+
ExtraComponent2: {
177+
component: () => null,
178+
name: 'ExtraComponent2',
179+
from: '@/group2/component'
180+
}
181+
};
182+
183+
mockUseEditorStore.mockImplementation((selector) => {
184+
const state = {
185+
registry: registryWithManyGroups,
186+
};
187+
return selector(state as any);
188+
});
189+
190+
render(
191+
<AddComponentsPopover parentLayerId="parent-1">
192+
<button>Add Component</button>
193+
</AddComponentsPopover>
194+
);
195+
196+
const trigger = screen.getByText('Add Component');
197+
fireEvent.click(trigger);
198+
199+
await waitFor(() => {
200+
const tabs = screen.getAllByRole('tab');
201+
expect(tabs.length).toBeLessThanOrEqual(3);
162202
});
163203
});
164204
});
@@ -251,7 +291,7 @@ describe('AddComponentsPopover', () => {
251291
fireEvent.click(buttonOption);
252292

253293
await waitFor(() => {
254-
expect(screen.queryByPlaceholderText('Add component')).not.toBeInTheDocument();
294+
expect(screen.queryByPlaceholderText('Find components')).not.toBeInTheDocument();
255295
});
256296
});
257297

@@ -282,7 +322,7 @@ describe('AddComponentsPopover', () => {
282322
});
283323

284324
describe('Search Functionality', () => {
285-
it('should filter components based on search input', async () => {
325+
it('should filter components based on search input across tabs', async () => {
286326
render(
287327
<AddComponentsPopover parentLayerId="parent-1">
288328
<button>Add Component</button>
@@ -293,13 +333,13 @@ describe('AddComponentsPopover', () => {
293333
fireEvent.click(trigger);
294334

295335
await waitFor(() => {
296-
expect(screen.getByPlaceholderText('Add component')).toBeInTheDocument();
336+
expect(screen.getByPlaceholderText('Find components')).toBeInTheDocument();
297337
});
298338

299-
const searchInput = screen.getByPlaceholderText('Add component');
339+
const searchInput = screen.getByPlaceholderText('Find components');
300340
fireEvent.change(searchInput, { target: { value: 'Button' } });
301341

302-
// Should still show Button but might filter others based on search implementation
342+
// Should still show Button
303343
expect(screen.getByText('Button')).toBeInTheDocument();
304344
});
305345

@@ -314,10 +354,10 @@ describe('AddComponentsPopover', () => {
314354
fireEvent.click(trigger);
315355

316356
await waitFor(() => {
317-
expect(screen.getByPlaceholderText('Add component')).toBeInTheDocument();
357+
expect(screen.getByPlaceholderText('Find components')).toBeInTheDocument();
318358
});
319359

320-
const searchInput = screen.getByPlaceholderText('Add component');
360+
const searchInput = screen.getByPlaceholderText('Find components');
321361
fireEvent.change(searchInput, { target: { value: 'NonExistentComponent' } });
322362

323363
await waitFor(() => {
@@ -338,7 +378,7 @@ describe('AddComponentsPopover', () => {
338378
expect(popoverWrapper).toBeInTheDocument();
339379
});
340380

341-
it('should handle empty registry', async () => {
381+
it('should handle empty registry gracefully', async () => {
342382
// Mock empty registry
343383
mockUseEditorStore.mockImplementation((selector) => {
344384
const state = {
@@ -359,11 +399,28 @@ describe('AddComponentsPopover', () => {
359399
await waitFor(() => {
360400
expect(screen.getByText('No components found')).toBeInTheDocument();
361401
});
402+
403+
// Should not render tabs when no components
404+
expect(screen.queryByRole('tablist')).not.toBeInTheDocument();
362405
});
363-
});
364406

365-
describe('Component Item Functionality', () => {
366-
it('should handle component selection via GroupedComponentItem', async () => {
407+
it('should handle single group with single tab', async () => {
408+
// Mock registry with only one group
409+
const singleGroupRegistry = {
410+
Button: {
411+
component: () => null,
412+
name: 'Button',
413+
from: '@/components/ui/button'
414+
},
415+
};
416+
417+
mockUseEditorStore.mockImplementation((selector) => {
418+
const state = {
419+
registry: singleGroupRegistry,
420+
};
421+
return selector(state as any);
422+
});
423+
367424
render(
368425
<AddComponentsPopover parentLayerId="parent-1">
369426
<button>Add Component</button>
@@ -374,31 +431,16 @@ describe('AddComponentsPopover', () => {
374431
fireEvent.click(trigger);
375432

376433
await waitFor(() => {
377-
expect(screen.getByText('Input')).toBeInTheDocument();
434+
expect(screen.getByText('Ui')).toBeInTheDocument();
435+
expect(screen.getByText('Button')).toBeInTheDocument();
378436
});
379-
380-
const inputOption = screen.getByText('Input');
381-
fireEvent.click(inputOption);
382-
383-
expect(mockAddComponentLayer).toHaveBeenCalledWith('Input', 'parent-1', undefined);
384437
});
438+
});
385439

386-
it('should handle component with missing registry entry gracefully', async () => {
387-
// Test selecting a component that might not be in registry
388-
mockUseEditorStore.mockImplementation((selector) => {
389-
const state = {
390-
registry: {
391-
// Missing some components that might be referenced
392-
},
393-
};
394-
return selector(state as any);
395-
});
396-
440+
describe('Component Preview Boxes', () => {
441+
it('should render grey preview boxes for each component', async () => {
397442
render(
398-
<AddComponentsPopover
399-
parentLayerId="parent-1"
400-
onChange={mockOnChange}
401-
>
443+
<AddComponentsPopover parentLayerId="parent-1">
402444
<button>Add Component</button>
403445
</AddComponentsPopover>
404446
);
@@ -407,8 +449,18 @@ describe('AddComponentsPopover', () => {
407449
fireEvent.click(trigger);
408450

409451
await waitFor(() => {
410-
expect(screen.getByText('No components found')).toBeInTheDocument();
452+
expect(screen.getByText('Button')).toBeInTheDocument();
411453
});
454+
455+
// Check for preview containers - these should exist even if lazy loading is happening
456+
const previewContainers = screen.getAllByRole('generic').filter(el =>
457+
el.className.includes('flex-shrink-0') &&
458+
el.className.includes('w-10') &&
459+
el.className.includes('h-8')
460+
);
461+
462+
// Should have at least one preview container for components in the active tab
463+
expect(previewContainers.length).toBeGreaterThan(0);
412464
});
413465
});
414466

0 commit comments

Comments
 (0)