mirror of https://github.com/buster-so/buster.git
dashboard drag and drop
This commit is contained in:
parent
5e788012c4
commit
12b98b8801
|
@ -0,0 +1,158 @@
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
test('Go to dashboard', async ({ page }) => {
|
||||||
|
await page.goto('http://localhost:3000/app/dashboards');
|
||||||
|
await expect(page.getByText('Dashboards').nth(1)).toBeVisible();
|
||||||
|
await expect(page.getByRole('button', { name: 'New dashboard' })).toBeVisible();
|
||||||
|
await expect(page.getByRole('button', { name: '12px star' })).toBeVisible();
|
||||||
|
await page.getByRole('link', { name: 'Important Metrics 12px star' }).click();
|
||||||
|
await expect(page.getByRole('textbox', { name: 'New dashboard' })).toHaveValue(
|
||||||
|
'Important Metrics'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Can remove a metric from a dashboard', async ({ page }) => {
|
||||||
|
await page.goto('http://localhost:3000/app/dashboards/c0855f0f-f50a-424e-9e72-9e53711a7f6a');
|
||||||
|
await expect(page.getByRole('button', { name: 'Quarterly Gross Profit Margin' })).toBeVisible();
|
||||||
|
// Hover over the metric to reveal the three-dot menu
|
||||||
|
await page
|
||||||
|
.locator(`[data-testid="metric-item-72e445a5-fb08-5b76-8c77-1642adf0cb72"]`)
|
||||||
|
.hover({ timeout: 500 });
|
||||||
|
await expect(
|
||||||
|
page.locator(`[data-testid="metric-item-72e445a5-fb08-5b76-8c77-1642adf0cb72"]`)
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
await page
|
||||||
|
.getByTestId('metric-item-72e445a5-fb08-5b76-8c77-1642adf0cb72')
|
||||||
|
.locator('button')
|
||||||
|
.click();
|
||||||
|
await page.getByRole('menuitem', { name: 'Delete' }).click();
|
||||||
|
await expect(page.getByRole('button', { name: 'Submit' })).toBeVisible();
|
||||||
|
await page.getByRole('button', { name: 'Submit' }).click();
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Quarterly Gross Profit Margin' })
|
||||||
|
).not.toBeVisible();
|
||||||
|
await page
|
||||||
|
.locator('div')
|
||||||
|
.filter({ hasText: /^DashboardFileStart chat$/ })
|
||||||
|
.getByRole('button')
|
||||||
|
.nth(2)
|
||||||
|
.click();
|
||||||
|
await page.getByRole('textbox', { name: 'Search...' }).click();
|
||||||
|
await page
|
||||||
|
.getByRole('textbox', { name: 'Search...' })
|
||||||
|
.fill('Quarterly Gross Profit Margin Trend (Q2 2023 - Q1 2024)');
|
||||||
|
await page.waitForTimeout(450);
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
await page
|
||||||
|
.locator('div:nth-child(2) > div > div > div > .border-border > div > .peer')
|
||||||
|
.first()
|
||||||
|
.click();
|
||||||
|
await expect(page.getByRole('button', { name: 'Add metrics' })).toBeVisible();
|
||||||
|
await page.getByRole('button', { name: 'Add metrics' }).click();
|
||||||
|
await expect(page.getByTestId('metric-item-72e445a5-fb08-5b76-8c77-1642adf0cb72')).toBeVisible();
|
||||||
|
|
||||||
|
//drag back into place
|
||||||
|
// Use a more specific selector if the element is a link
|
||||||
|
const sourceElement = page.getByText('Quarterly Gross Profit Margin');
|
||||||
|
const targetElement = page.getByRole('button', { name: 'Revenue by Product Category (' });
|
||||||
|
|
||||||
|
expect(sourceElement).toBeVisible();
|
||||||
|
expect(targetElement).toBeVisible();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Skip the initial click since it's a link and would navigate away
|
||||||
|
// Go straight to hover and mouse operations
|
||||||
|
await sourceElement.hover({ force: true });
|
||||||
|
await page.waitForTimeout(200);
|
||||||
|
await page.mouse.down();
|
||||||
|
await page.waitForTimeout(200);
|
||||||
|
|
||||||
|
// Move to target element
|
||||||
|
await targetElement.hover({ force: true, position: { x: 5, y: 5 } });
|
||||||
|
await page.waitForTimeout(400);
|
||||||
|
|
||||||
|
await page.mouse.up();
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Drag and drop failed with first approach, trying backup method');
|
||||||
|
|
||||||
|
const sourceBoundingBox = await sourceElement.boundingBox();
|
||||||
|
const targetBoundingBox = await targetElement.boundingBox();
|
||||||
|
|
||||||
|
if (sourceBoundingBox && targetBoundingBox) {
|
||||||
|
const startX = sourceBoundingBox.x + sourceBoundingBox.width / 2;
|
||||||
|
const startY = sourceBoundingBox.y + sourceBoundingBox.height / 2;
|
||||||
|
const endX = targetBoundingBox.x + 10;
|
||||||
|
const endY = targetBoundingBox.y + targetBoundingBox.height / 2;
|
||||||
|
|
||||||
|
// Skip the initial click
|
||||||
|
await page.mouse.move(startX, startY);
|
||||||
|
await page.waitForTimeout(300);
|
||||||
|
await page.mouse.down();
|
||||||
|
await page.waitForTimeout(300);
|
||||||
|
|
||||||
|
// Move to destination with a slower motion
|
||||||
|
const steps = 20;
|
||||||
|
for (let i = 0; i <= steps; i++) {
|
||||||
|
const stepX = startX + (endX - startX) * (i / steps);
|
||||||
|
const stepY = startY + (endY - startY) * (i / steps);
|
||||||
|
await page.mouse.move(stepX, stepY);
|
||||||
|
await page.waitForTimeout(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.waitForTimeout(100);
|
||||||
|
await page.mouse.up();
|
||||||
|
await page.waitForTimeout(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the element was moved successfully
|
||||||
|
await expect(sourceElement).toBeVisible();
|
||||||
|
await expect(page.locator('body')).toMatchAriaSnapshot(`
|
||||||
|
- button /Quarterly Gross Profit Margin Trend \\(Q2 \\d+ - Q1 \\d+\\) Q2 \\d+ - Q1 \\d+ • What is the trend of average gross profit margin from Q2 \\d+ to Q1 \\d+\\?/:
|
||||||
|
- heading /Quarterly Gross Profit Margin Trend \\(Q2 \\d+ - Q1 \\d+\\)/ [level=4]
|
||||||
|
- img
|
||||||
|
- button /Revenue by Product Category \\(Q2 \\d+ - Q1 \\d+\\) Q2 \\d+ - Q1 \\d+ • How does revenue break down by product category from Q2 \\d+ to Q1 \\d+\\?/:
|
||||||
|
- heading /Revenue by Product Category \\(Q2 \\d+ - Q1 \\d+\\)/ [level=4]
|
||||||
|
- img
|
||||||
|
- button /Revenue by Sales Territory \\(Q2 \\d+ - Q1 \\d+\\) Q2 \\d+ - Q1 \\d+ • How does revenue break down by sales territory from Q2 \\d+ to Q1 \\d+\\?/:
|
||||||
|
- heading /Revenue by Sales Territory \\(Q2 \\d+ - Q1 \\d+\\)/ [level=4]
|
||||||
|
- img
|
||||||
|
- button /Quarterly Revenue Growth Rate \\(QoQ\\) \\(Q2 \\d+ - Q1 \\d+\\) Q2 \\d+ - Q1 \\d+ • What is the quarter-over-quarter sales revenue growth rate from Q2 \\d+ to Q1 \\d+\\?/:
|
||||||
|
- heading /Quarterly Revenue Growth Rate \\(QoQ\\) \\(Q2 \\d+ - Q1 \\d+\\)/ [level=4]
|
||||||
|
- img
|
||||||
|
- button /Quarterly Revenue Trend \\(Q2 \\d+ - Q1 \\d+\\) Q2 \\d+ - Q1 \\d+ • What is the trend of total sales revenue from Q2 \\d+ to Q1 \\d+\\?/:
|
||||||
|
- heading /Quarterly Revenue Trend \\(Q2 \\d+ - Q1 \\d+\\)/ [level=4]
|
||||||
|
- img
|
||||||
|
- button "Total Unique Products Sold All Time • What is the total number of distinct products ever sold?":
|
||||||
|
- heading "Total Unique Products Sold" [level=4]
|
||||||
|
- heading /\\d+/ [level=1]
|
||||||
|
- text: Drag here to create a new row
|
||||||
|
`);
|
||||||
|
await expect(page.getByRole('button', { name: 'Save' })).toBeVisible();
|
||||||
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
|
await page.waitForTimeout(55);
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
await expect(page.locator('body')).toMatchAriaSnapshot(`
|
||||||
|
- button /Quarterly Gross Profit Margin Trend \\(Q2 \\d+ - Q1 \\d+\\) Q2 \\d+ - Q1 \\d+ • What is the trend of average gross profit margin from Q2 \\d+ to Q1 \\d+\\?/:
|
||||||
|
- heading /Quarterly Gross Profit Margin Trend \\(Q2 \\d+ - Q1 \\d+\\)/ [level=4]
|
||||||
|
- img
|
||||||
|
- button /Revenue by Product Category \\(Q2 \\d+ - Q1 \\d+\\) Q2 \\d+ - Q1 \\d+ • How does revenue break down by product category from Q2 \\d+ to Q1 \\d+\\?/:
|
||||||
|
- heading /Revenue by Product Category \\(Q2 \\d+ - Q1 \\d+\\)/ [level=4]
|
||||||
|
- img
|
||||||
|
- button /Revenue by Sales Territory \\(Q2 \\d+ - Q1 \\d+\\) Q2 \\d+ - Q1 \\d+ • How does revenue break down by sales territory from Q2 \\d+ to Q1 \\d+\\?/:
|
||||||
|
- heading /Revenue by Sales Territory \\(Q2 \\d+ - Q1 \\d+\\)/ [level=4]
|
||||||
|
- img
|
||||||
|
- button /Quarterly Revenue Growth Rate \\(QoQ\\) \\(Q2 \\d+ - Q1 \\d+\\) Q2 \\d+ - Q1 \\d+ • What is the quarter-over-quarter sales revenue growth rate from Q2 \\d+ to Q1 \\d+\\?/:
|
||||||
|
- heading /Quarterly Revenue Growth Rate \\(QoQ\\) \\(Q2 \\d+ - Q1 \\d+\\)/ [level=4]
|
||||||
|
- img
|
||||||
|
- button /Quarterly Revenue Trend \\(Q2 \\d+ - Q1 \\d+\\) Q2 \\d+ - Q1 \\d+ • What is the trend of total sales revenue from Q2 \\d+ to Q1 \\d+\\?/:
|
||||||
|
- heading /Quarterly Revenue Trend \\(Q2 \\d+ - Q1 \\d+\\)/ [level=4]
|
||||||
|
- img
|
||||||
|
- button "Total Unique Products Sold All Time • What is the total number of distinct products ever sold?":
|
||||||
|
- heading "Total Unique Products Sold" [level=4]
|
||||||
|
- heading /\\d+/ [level=1]
|
||||||
|
- text: Drag here to create a new row
|
||||||
|
`);
|
||||||
|
});
|
|
@ -137,7 +137,10 @@ export const BusterResizeColumns: React.FC<ContainerProps> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SortableContext id={rowId} items={items} disabled={false}>
|
<SortableContext id={rowId} items={items} disabled={false}>
|
||||||
<div ref={setNodeRef} className="buster-resize-columns relative h-full w-full">
|
<div
|
||||||
|
ref={setNodeRef}
|
||||||
|
className="buster-resize-columns relative h-full w-full"
|
||||||
|
data-testid={`buster-resize-columns-${rowIndex}`}>
|
||||||
<BusterDragColumnMarkers
|
<BusterDragColumnMarkers
|
||||||
isDraggingIndex={columnMarkerColumnIndex}
|
isDraggingIndex={columnMarkerColumnIndex}
|
||||||
itemsLength={items.length}
|
itemsLength={items.length}
|
||||||
|
@ -163,7 +166,7 @@ export const BusterResizeColumns: React.FC<ContainerProps> = ({
|
||||||
)}
|
)}
|
||||||
key={item.id}
|
key={item.id}
|
||||||
minSize={'25%'}>
|
minSize={'25%'}>
|
||||||
<div className="relative h-full w-full">
|
<div className="relative h-full w-full" data-testid={`pane-${index}`}>
|
||||||
<DropzonePlaceholder
|
<DropzonePlaceholder
|
||||||
right={false}
|
right={false}
|
||||||
isDropzoneActives={isDropzoneActives}
|
isDropzoneActives={isDropzoneActives}
|
||||||
|
@ -216,6 +219,7 @@ const DropzonePlaceholder: React.FC<{
|
||||||
active && 'bg-primary! z-10 opacity-100'
|
active && 'bg-primary! z-10 opacity-100'
|
||||||
)}
|
)}
|
||||||
style={memoizedStyle}
|
style={memoizedStyle}
|
||||||
|
data-testid={`dropzone-placeholder-${right ? 'right' : 'left'}`}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -76,6 +76,7 @@ const DashboardMetricItemBase: React.FC<{
|
||||||
className={`metric-item flex h-full w-full flex-col overflow-auto ${className}`}>
|
className={`metric-item flex h-full w-full flex-col overflow-auto ${className}`}>
|
||||||
<CardHeader
|
<CardHeader
|
||||||
size="small"
|
size="small"
|
||||||
|
data-testid={`metric-item-${metricId}`}
|
||||||
className="hover:bg-item-hover group min-h-13! justify-center overflow-hidden border-b px-4 py-2">
|
className="hover:bg-item-hover group min-h-13! justify-center overflow-hidden border-b px-4 py-2">
|
||||||
<MetricTitle
|
<MetricTitle
|
||||||
name={metric?.name || ''}
|
name={metric?.name || ''}
|
||||||
|
|
Loading…
Reference in New Issue