Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions dashboards/src/components/Dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
import { Box, BoxProps } from '@mui/material';
import { ErrorBoundary, ErrorAlert } from '@perses-dev/components';
import { ReactElement, useRef } from 'react';
import { usePanelGroupIds } from '../../context';
import { GridLayout } from '../GridLayout';
import { usePanelGroup, usePanelGroupIds } from '../../context';
import { GridLayout, GridLayoutProps } from '../GridLayout';
import { TabLayout } from '../TabLayout';
import { EmptyDashboard, EmptyDashboardProps } from '../EmptyDashboard';
import { PanelOptions } from '../Panel';

Expand All @@ -30,6 +31,20 @@ export type DashboardProps = BoxProps & {
};
const HEADER_HEIGHT = 165; // Approximate height of the header in dashboard view (including the navbar and variables toolbar)

function PanelGroupRenderer({ panelGroupId, panelOptions, panelFullHeight }: GridLayoutProps): ReactElement {
const group = usePanelGroup(panelGroupId);
switch (group.layoutKind) {
case 'Tabs':
return <TabLayout panelGroupId={panelGroupId} panelOptions={panelOptions} panelFullHeight={panelFullHeight} />;
case 'Grid':
return <GridLayout panelGroupId={panelGroupId} panelOptions={panelOptions} panelFullHeight={panelFullHeight} />;
default: {
const _exhaustiveCheck: never = group;
throw new Error(`Unknown layout kind: ${(_exhaustiveCheck as { layoutKind: string }).layoutKind}`);
}
}
}

/**
* Renders a Dashboard for the provided Dashboard spec.
*/
Expand All @@ -50,7 +65,7 @@ export function Dashboard({ emptyDashboardProps, panelOptions, ...boxProps }: Da
)}
{!isEmpty &&
panelGroupIds.map((panelGroupId) => (
<GridLayout
<PanelGroupRenderer
key={panelGroupId}
panelGroupId={panelGroupId}
panelOptions={panelOptions}
Expand Down
11 changes: 6 additions & 5 deletions dashboards/src/components/GridLayout/GridLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { useVariableValues, VariableContext } from '@perses-dev/plugin-system';
import { useEditMode, usePanelGroup, usePanelGroupActions, useViewPanelGroup } from '../../context';
import { GRID_LAYOUT_SMALL_BREAKPOINT } from '../../constants';
import { PanelOptions } from '../Panel';
import { PanelGroupDefinition } from '../../model';
import { GridPanelGroup } from '../../model';
import { Row, RowProps } from './Row';

export interface GridLayoutProps {
Expand All @@ -27,12 +27,13 @@ export interface GridLayoutProps {
panelFullHeight?: number;
}

/**
* Layout component that arranges children in a Grid based on the definition.
*/
export function GridLayout(props: GridLayoutProps): ReactElement {
const { panelGroupId, panelOptions, panelFullHeight } = props;
const groupDefinition: PanelGroupDefinition = usePanelGroup(panelGroupId);
const panelGroup = usePanelGroup(panelGroupId);
if (panelGroup.layoutKind !== 'Grid') {
throw new Error(`GridLayout expects a Grid panel group, got ${panelGroup.layoutKind}`);
}
const groupDefinition: GridPanelGroup = panelGroup;
const { updatePanelGroupLayouts } = usePanelGroupActions(panelGroupId);
const viewPanelItemId = useViewPanelGroup();
const { isEditMode } = useEditMode();
Expand Down
27 changes: 12 additions & 15 deletions dashboards/src/components/GridLayout/Row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,21 @@
import { ReactElement, useEffect, useMemo, useState } from 'react';
import { Layout, Layouts, Responsive, WidthProvider } from 'react-grid-layout';
import { ErrorAlert, ErrorBoundary } from '@perses-dev/components';
import { GRID_LAYOUT_COLS, GRID_LAYOUT_SMALL_BREAKPOINT } from '../../constants';
import { PanelGroupDefinition, PanelGroupItemLayout } from '../../model';
import {
GRID_LAYOUT_COLS,
GRID_LAYOUT_MARGIN,
GRID_LAYOUT_ROW_HEIGHT,
GRID_LAYOUT_SMALL_BREAKPOINT,
calculateGridItemWidth,
} from '../../constants';
import { GridPanelGroup, PanelGroupItemLayout } from '../../model';
import { GridContainer } from './GridContainer';
import { GridItemContent } from './GridItemContent';
import { GridTitle } from './GridTitle';

const DEFAULT_MARGIN = 10;
const ROW_HEIGHT = 30;

export interface RowProps {
panelGroupId: PanelGroupId;
groupDefinition: PanelGroupDefinition;
groupDefinition: GridPanelGroup;
gridColWidth: number;
panelFullHeight?: number;
panelOptions?: PanelOptions;
Expand Down Expand Up @@ -84,7 +87,7 @@
if (itemLayout.i === itemLayoutViewed) {
const rowTitleHeight = 40 + 8; // 40 is the height of the row title and 8 is the margin height
return {
h: Math.round(((panelFullHeight ?? window.innerHeight) - rowTitleHeight) / (ROW_HEIGHT + DEFAULT_MARGIN)), // Viewed panel should take the full height remaining
h: Math.round(((panelFullHeight ?? window.innerHeight) - rowTitleHeight) / (GRID_LAYOUT_ROW_HEIGHT + GRID_LAYOUT_MARGIN)), // Viewed panel should take the full height remaining

Check failure on line 90 in dashboards/src/components/GridLayout/Row.tsx

View workflow job for this annotation

GitHub Actions / lint-npm

Replace `((panelFullHeight·??·window.innerHeight)·-·rowTitleHeight)·/·(GRID_LAYOUT_ROW_HEIGHT·+·GRID_LAYOUT_MARGIN)` with `⏎··············((panelFullHeight·??·window.innerHeight)·-·rowTitleHeight)·/·(GRID_LAYOUT_ROW_HEIGHT·+·GRID_LAYOUT_MARGIN)⏎············`
i: itemLayoutViewed,
w: 48,
x: 0,
Expand Down Expand Up @@ -121,12 +124,12 @@
className="layout"
breakpoints={{ [GRID_LAYOUT_SMALL_BREAKPOINT]: theme.breakpoints.values.sm, xxs: 0 }}
cols={GRID_LAYOUT_COLS}
rowHeight={ROW_HEIGHT}
rowHeight={GRID_LAYOUT_ROW_HEIGHT}
draggableHandle=".drag-handle"
resizeHandles={['se']}
isDraggable={isEditMode && !hasViewPanel}
isResizable={isEditMode && !hasViewPanel}
margin={[DEFAULT_MARGIN, DEFAULT_MARGIN]}
margin={[GRID_LAYOUT_MARGIN, GRID_LAYOUT_MARGIN]}
containerPadding={[0, 10]}
layouts={{ sm: itemLayouts }}
onLayoutChange={onLayoutChange}
Expand Down Expand Up @@ -154,9 +157,3 @@
</GridContainer>
);
}

const calculateGridItemWidth = (w: number, colWidth: number): number => {
// 0 * Infinity === NaN, which causes problems with resize contraints
if (!Number.isFinite(w)) return w;
return Math.round(colWidth * w + Math.max(0, w - 1) * DEFAULT_MARGIN);
};
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,28 @@ export function PanelGroupEditorForm(props: PanelGroupEditorFormProps): ReactEle
const [title, setTitle] = useState(initialValues.title);
const [isCollapsed, setIsCollapsed] = useState(initialValues.isCollapsed);
const [repeatVariable, setRepeatVariable] = useState<string | undefined>(initialValues.repeatVariable);
const [layoutKind, setLayoutKind] = useState<'Grid' | 'Tabs'>(initialValues.layoutKind);

const handleSubmit: FormEventHandler = (e) => {
e.preventDefault();
onSubmit({ title, isCollapsed, repeatVariable });
onSubmit({ title, isCollapsed, repeatVariable, layoutKind });
};

return (
<form id={panelGroupEditorFormId} onSubmit={handleSubmit}>
<FormControl fullWidth margin="normal">
<TextField
select
required
label="Layout Type"
value={layoutKind}
onChange={(e) => setLayoutKind(e.target.value as 'Grid' | 'Tabs')}
data-testid="panel-group-editor-layout-kind"
>
<MenuItem value="Grid">Grid</MenuItem>
<MenuItem value="Tabs">Tabs</MenuItem>
</TextField>
</FormControl>
<FormControl fullWidth margin="normal">
<TextField
required
Expand All @@ -57,26 +71,28 @@ export function PanelGroupEditorForm(props: PanelGroupEditorFormProps): ReactEle
<MenuItem value="Open">Open</MenuItem>
<MenuItem value="Closed">Closed</MenuItem>
</TextField>
<FormControl fullWidth margin="normal">
<TextField
select
label="Repeat Variable"
variant="outlined"
value={repeatVariable ?? ''}
onChange={(e) => setRepeatVariable(e.target.value === '' ? undefined : e.target.value)}
>
<MenuItem value="">
<Typography sx={{ fontStyle: 'italic' }}>None</Typography>
</MenuItem>
{variables
?.sort((a, b) => a.localeCompare(b))
.map((variable) => (
<MenuItem key={variable} value={variable}>
{variable}
</MenuItem>
))}
</TextField>
</FormControl>
{layoutKind === 'Grid' && (
<FormControl fullWidth margin="normal">
<TextField
select
label="Repeat Variable"
variant="outlined"
value={repeatVariable ?? ''}
onChange={(e) => setRepeatVariable(e.target.value === '' ? undefined : e.target.value)}
>
<MenuItem value="">
<Typography sx={{ fontStyle: 'italic' }}>None</Typography>
</MenuItem>
{variables
?.sort((a, b) => a.localeCompare(b))
.map((variable) => (
<MenuItem key={variable} value={variable}>
{variable}
</MenuItem>
))}
</TextField>
</FormControl>
)}
</FormControl>
</form>
);
Expand Down
Loading
Loading