Redesign topic details sidebar with clickable navigation and improved mobile layout (WIP - demo video test regression) (#1011)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: thomasnordquist <7721625+thomasnordquist@users.noreply.github.com>
Co-authored-by: Thomas Nordquist <thomasnordquist@users.noreply.github.com>
This commit is contained in:
Copilot
2026-01-12 09:16:22 +01:00
committed by GitHub
parent 36b4c0fce5
commit c7d28da4ec
13 changed files with 789 additions and 51 deletions

View File

@@ -0,0 +1,84 @@
import { chromium, Page } from 'playwright'
async function inspect() {
const browser = await chromium.launch({ headless: true })
const page = await browser.newPage()
console.log('Navigating to localhost:3000...')
await page.goto('http://localhost:3000')
await page.waitForTimeout(2000)
// Login
console.log('Logging in...')
await page.fill('[name="username"]', 'test')
await page.fill('[name="password"]', 'test123')
await page.locator('[type="submit"]').click()
await page.waitForTimeout(3000)
// Expand kitchen/coffee_maker topic
console.log('Expanding kitchen topic...')
const kitchenTopic = page.locator('[data-test-topic="kitchen"]').first()
await kitchenTopic.click()
await page.waitForTimeout(500)
console.log('Clicking coffee_maker topic...')
const coffeeMakerTopic = page.locator('[data-test-topic="kitchen/coffee_maker"]').first()
await coffeeMakerTopic.click()
await page.waitForTimeout(1500)
// Look for ShowChart icons
console.log('\n=== ShowChart Elements ===')
const showCharts = await page.locator('//*[contains(@data-test-type, "ShowChart")]').all()
console.log(`Found ${showCharts.length} ShowChart elements:`)
for (let i = 0; i < showCharts.length; i++) {
const dataTest = await showCharts[i].getAttribute('data-test')
const isVisible = await showCharts[i].isVisible()
console.log(` [${i}] data-test="${dataTest}", visible=${isVisible}`)
}
// Click heater ShowChart icon
console.log('\n=== Clicking heater ShowChart ===')
const heaterChart = page.locator('//*[contains(@data-test-type, "ShowChart")][contains(@data-test, "heater")]').first()
await heaterChart.waitFor({ state: 'visible', timeout: 5000 })
await heaterChart.click()
await page.waitForTimeout(2000)
// Check for ChartPanel
console.log('\n=== Looking for ChartPanel ===')
const chartPanels = await page.locator('[data-test-type="ChartPaper"]').all()
console.log(`Found ${chartPanels.length} ChartPanel elements`)
for (let i = 0; i < chartPanels.length; i++) {
const dataTest = await chartPanels[i].getAttribute('data-test')
console.log(` [${i}] data-test="${dataTest}"`)
}
// Look for ChartSettings buttons
console.log('\n=== Looking for ChartSettings Elements ===')
const allSettings = await page.locator('//*[@data-test-type="ChartSettings"]').all()
console.log(`Found ${allSettings.length} ChartSettings elements (using @data-test-type):`)
for (let i = 0; i < allSettings.length; i++) {
const dataTest = await allSettings[i].getAttribute('data-test')
const isVisible = await allSettings[i].isVisible()
const box = await allSettings[i].boundingBox()
console.log(` [${i}] data-test="${dataTest}", visible=${isVisible}, box=${JSON.stringify(box)}`)
}
// Try different locator strategies
console.log('\n=== Trying contains query for ChartSettings with "heater" ===')
const heaterSettings = page.locator('//*[contains(@data-test-type, "ChartSettings")][contains(@data-test, "heater")]')
const count = await heaterSettings.count()
console.log(`Found ${count} elements`)
if (count > 0) {
const dataTest = await heaterSettings.first().getAttribute('data-test')
const isVisible = await heaterSettings.first().isVisible()
console.log(` data-test="${dataTest}", visible=${isVisible}`)
}
// Take screenshot
await page.screenshot({ path: '/tmp/chart-settings-inspection.png', fullPage: true })
console.log('\nScreenshot saved to /tmp/chart-settings-inspection.png')
await browser.close()
}
inspect().catch(console.error)

View File

@@ -2,7 +2,9 @@ import { Page } from 'playwright'
import { clickOn } from '../util'
export async function copyTopicToClipboard(browser: Page) {
// Select the copy button specifically in the Topic panel (not Value panel or MessageHistory)
const copyButton = browser.getByRole('button', { name: /Topic/i }).getByTestId('copy-button')
// Select the first copy button (topic path copy button in the new sidebar structure)
// The new sidebar has copy buttons in the topic section (for path) and value section (for value)
const copyButtons = browser.getByTestId('copy-button')
const copyButton = copyButtons.first()
await clickOn(copyButton, 1)
}

View File

@@ -2,7 +2,9 @@ import { Page } from 'playwright'
import { clickOn } from '../util'
export async function copyValueToClipboard(browser: Page) {
// Select the copy button specifically in the Value panel (not Topic panel or MessageHistory)
const copyButton = browser.getByRole('button', { name: /Value/i }).getByTestId('copy-button')
// Select the second copy button (value copy button in the new sidebar structure)
// The new sidebar has copy buttons in the topic section (for path) and value section (for value)
const copyButtons = browser.getByTestId('copy-button')
const copyButton = copyButtons.nth(1) // Second copy button is for the value
await clickOn(copyButton, 1)
}

View File

@@ -2,7 +2,7 @@ import { Page } from 'playwright'
import { clickOn } from '../util'
export async function saveMessageToFile(browser: Page) {
// Select the save button specifically in the Value panel
const saveButton = browser.getByRole('button', { name: /Value/i }).getByTestId('save-button')
// Select the save button in the new sidebar structure (directly by testid)
const saveButton = browser.getByTestId('save-button')
await clickOn(saveButton, 1)
}

View File

@@ -5,20 +5,25 @@ export async function showNumericPlot(browser: Page) {
// On desktop, expandTopic will also select the topic (original behavior restored)
// This shows the JSON properties in the details panel where chart icons are located
await expandTopic('kitchen/coffee_maker', browser)
// Switch to Details tab to ensure ShowChart icons are visible
await switchToDetailsTab(browser)
await sleep(500)
let heater = await valuePreviewGuttersShowChartIcon('heater', browser)
await moveToCenterOfElement(heater)
await sleep(1000)
// Refocus and click
// Refocus and click (force:true bypasses tooltip overlay)
heater = await valuePreviewGuttersShowChartIcon('heater', browser)
await heater.click()
await heater.click({ force: true })
await sleep(1000)
let temperature = await valuePreviewGuttersShowChartIcon('temperature', browser)
await moveToCenterOfElement(temperature)
await sleep(1000)
// Refocus and click
// Refocus and click (force:true bypasses tooltip overlay)
temperature = await valuePreviewGuttersShowChartIcon('temperature', browser)
await temperature.click()
await temperature.click({ force: true })
await sleep(1000)
await chartSettings('heater', browser)
@@ -81,3 +86,9 @@ async function clickOnMenuPoint(name: string, browser: Page) {
const item = await browser.locator(`[data-menu-item="${name}"]`)
return clickOn(item)
}
async function switchToDetailsTab(browser: Page) {
// Click the Details tab to ensure it's active and ShowChart icons are visible
const detailsTab = browser.getByRole('tab', { name: 'Details' })
await detailsTab.click()
}

View File

@@ -275,8 +275,11 @@ describe('MQTT Explorer UI Tests', function () {
await expandTopic('livingroom/lamp/state', page)
await sleep(1000)
// When: Copy topic button is clicked
const copyTopicButton = page.getByRole('button', { name: /Topic/i }).getByTestId('copy-button')
// When: Copy topic button is clicked (in the topic section at the top)
// The new sidebar has copy buttons in the topic section (for path) and value section (for value)
// We need to find the first copy button (topic path copy button)
const copyButtons = page.getByTestId('copy-button')
const copyTopicButton = copyButtons.first()
await copyTopicButton.click()
await sleep(500)
@@ -317,8 +320,9 @@ describe('MQTT Explorer UI Tests', function () {
it('should copy message value to clipboard in both Electron and browser modes', async function () {
// Given: A topic with a value is selected (reuse already expanded topic)
// When: Copy value button is clicked
const copyValueButton = page.getByRole('button', { name: /Value/i }).getByTestId('copy-button')
// When: Copy value button is clicked (the second copy button in the value section)
const copyButtons = page.getByTestId('copy-button')
const copyValueButton = copyButtons.nth(1) // Second copy button is for the value
await copyValueButton.click()
await sleep(500)
@@ -367,8 +371,8 @@ describe('MQTT Explorer UI Tests', function () {
// In browser mode, set up download handling
const downloadPromise = page.waitForEvent('download', { timeout: 10000 })
// When: Save button is clicked
const saveButton = page.getByRole('button', { name: /Value/i }).getByTestId('save-button')
// When: Save button is clicked (in the new sidebar, save button is in the value section)
const saveButton = page.getByTestId('save-button')
await saveButton.click()
// Then: Download should be triggered
@@ -385,7 +389,7 @@ describe('MQTT Explorer UI Tests', function () {
} else {
// In Electron mode, the file dialog would open
// We can't easily test the native file dialog, but we can verify the button works
const saveButton = page.getByRole('button', { name: /Value/i }).getByTestId('save-button')
const saveButton = page.getByTestId('save-button')
const isVisible = await saveButton.isVisible()
expect(isVisible).to.be.true