From 12b98b8801243fc5eedd90446118976fcf74e986 Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Tue, 6 May 2025 16:04:42 -0600 Subject: [PATCH] dashboard drag and drop --- .../dashboard-updates.test.ts | 158 ++++++++++++++++++ .../ui/grid/BusterResizeColumns.tsx | 8 +- .../DashboardMetricItem.tsx | 1 + 3 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 web/playwright-tests/dashboard-updates.test.ts diff --git a/web/playwright-tests/dashboard-updates.test.ts b/web/playwright-tests/dashboard-updates.test.ts new file mode 100644 index 000000000..fbff2e2cc --- /dev/null +++ b/web/playwright-tests/dashboard-updates.test.ts @@ -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 + `); +}); diff --git a/web/src/components/ui/grid/BusterResizeColumns.tsx b/web/src/components/ui/grid/BusterResizeColumns.tsx index 26b2a76c3..ab2f8d18f 100644 --- a/web/src/components/ui/grid/BusterResizeColumns.tsx +++ b/web/src/components/ui/grid/BusterResizeColumns.tsx @@ -137,7 +137,10 @@ export const BusterResizeColumns: React.FC = ({ return ( -
+
= ({ )} key={item.id} minSize={'25%'}> -
+
); }); diff --git a/web/src/controllers/DashboardController/DashboardViewDashboardController/DashboardContentController/DashboardMetricItem/DashboardMetricItem.tsx b/web/src/controllers/DashboardController/DashboardViewDashboardController/DashboardContentController/DashboardMetricItem/DashboardMetricItem.tsx index da4e0a78c..dbaeed839 100644 --- a/web/src/controllers/DashboardController/DashboardViewDashboardController/DashboardContentController/DashboardMetricItem/DashboardMetricItem.tsx +++ b/web/src/controllers/DashboardController/DashboardViewDashboardController/DashboardContentController/DashboardMetricItem/DashboardMetricItem.tsx @@ -76,6 +76,7 @@ const DashboardMetricItemBase: React.FC<{ className={`metric-item flex h-full w-full flex-col overflow-auto ${className}`}>