diff --git a/package-lock.json b/package-lock.json index b6b2a10..6029301 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@ioai/rosview", - "version": "1.6.3", + "version": "1.7.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@ioai/rosview", - "version": "1.6.3", + "version": "1.7.0", "license": "MIT", "devDependencies": { "@eslint/js": "^9.39.4", diff --git a/package.json b/package.json index b9aed50..fa75263 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ioai/rosview", - "version": "1.6.3", + "version": "1.7.0", "description": "High-performance robotics data visualization for MCAP, ROS bag, ROS2 db3, HDF5 and BVH — embeddable React component and standalone SPA", "keywords": [ "ros", diff --git a/src/features/panels/Image/ImagePanel.tsx b/src/features/panels/Image/ImagePanel.tsx index 2af9d4e..bd3811f 100644 --- a/src/features/panels/Image/ImagePanel.tsx +++ b/src/features/panels/Image/ImagePanel.tsx @@ -19,6 +19,7 @@ import { isH264MessageEvent, toWorkerFrame } from './core/messageFrameAdapter'; import { applyDepthTopicPreset } from './core/depthColorDefaults'; import type { ImageConfig } from './defaults'; import { TopicQuickPicker } from '../framework/TopicQuickPicker'; +import { PanelTopicBar } from '../framework/PanelTopicBar'; import ImageRenderWorkerClass from './core/ImageRender.worker.ts?worker&inline'; type ColorOptions = Pick; @@ -304,7 +305,7 @@ export const ImagePanel: React.FC = (props) => { style={{ background: backgroundColor }} data-testid="image-panel" > -
+ setConfig((prev) => applyDepthTopicPreset(nextTopic, prev))} @@ -313,7 +314,7 @@ export const ImagePanel: React.FC = (props) => { className="min-w-0 flex-1" triggerClassName="border-zinc-700 bg-zinc-950 text-zinc-100 hover:bg-zinc-900 hover:text-zinc-50" /> -
+
= ({ return (
-
+ = ({ placeholder={formatMessage({ id: 'panels.framework.topicPicker.placeholder' })} className="min-w-0 w-full" /> -
+
{ + let container: HTMLDivElement; + let root: Root; + + beforeEach(() => { + container = document.createElement('div'); + document.body.appendChild(container); + root = createRoot(container); + }); + + afterEach(() => { + act(() => root.unmount()); + container.remove(); + }); + + function getBar(): HTMLDivElement { + const el = container.firstElementChild; + if (!(el instanceof HTMLDivElement)) throw new Error('bar div not found'); + return el; + } + + it('renders children inside a compact, horizontally-padded row', () => { + act(() => { + root.render( + + + , + ); + }); + const bar = getBar(); + expect(bar.textContent).toBe('picker'); + // No vertical padding: height should come purely from content, matching + // the Image panel's compact reference experience (RawMessages used to + // add `py-1`, which made its bar taller than Image's). + expect(bar.className).not.toMatch(/(^|\s)py-\d/); + expect(bar.className).toContain('items-center'); + expect(bar.className).toContain('px-2'); + }); + + it('merges className overrides without losing the shared layout classes', () => { + act(() => { + root.render( + + content + , + ); + }); + const bar = getBar(); + expect(bar.className).toContain('border-zinc-800'); + expect(bar.className).toContain('bg-zinc-950'); + // The override replaces the default border/background color utilities + // rather than doubling up on conflicting classes. + expect(bar.className).not.toContain('border-border'); + expect(bar.className).not.toContain('bg-muted'); + expect(bar.className).toContain('items-center'); + }); +}); diff --git a/src/features/panels/framework/PanelTopicBar.tsx b/src/features/panels/framework/PanelTopicBar.tsx new file mode 100644 index 0000000..74f021c --- /dev/null +++ b/src/features/panels/framework/PanelTopicBar.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { cn } from '@/shared/lib/utils'; + +export interface PanelTopicBarProps { + className?: string; + children: React.ReactNode; +} + +/** + * Shared container for a panel's top "topic picker" row (`TopicQuickPicker` + * plus any adjacent controls). Height comes purely from the row's content + * (the picker's own `h-8` trigger) with horizontal-only padding, so every + * panel using this gets the same compact height instead of each hand-rolling + * its own wrapper with inconsistent vertical padding. Colors/borders stay + * overridable via `className` (e.g. Image panel's permanently-dark chrome) + * since panels can differ there while sharing the same box model. + */ +export const PanelTopicBar: React.FC = ({ className, children }) => ( +
+ {children} +
+); diff --git a/src/features/panels/framework/index.ts b/src/features/panels/framework/index.ts index 893183b..703b67a 100644 --- a/src/features/panels/framework/index.ts +++ b/src/features/panels/framework/index.ts @@ -13,6 +13,8 @@ export type { } from './foxgloveAdapter'; export { TopicQuickPicker } from './TopicQuickPicker'; export type { TopicQuickPickerProps } from './TopicQuickPicker'; +export { PanelTopicBar } from './PanelTopicBar'; +export type { PanelTopicBarProps } from './PanelTopicBar'; export { PanelErrorBoundary } from './PanelErrorBoundary'; export { PanelRuntimeShell } from './PanelRuntimeShell'; export {