Skip to content

Commit

Permalink
feat(*): Revamp exploration type selector (#94)
Browse files Browse the repository at this point in the history
  • Loading branch information
grafakus authored Jul 29, 2024
1 parent 48acb88 commit 60dab67
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 90 deletions.
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ assignees: ''

### Bug description

- **Grafana version:**
- **Plugin version:**
- **Grafana version:**
- **Plugin version:**

<!-- A clear and concise description of what the bug is and where you see it -->
<!-- Please specify in which environment(s) you see it (cloud or open-source) -->
Expand Down
2 changes: 1 addition & 1 deletion e2e/fixtures/pages/ExploreProfilesPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export class ExploreProfilesPage extends PyroscopePage {
}

async getSelectedExplorationType() {
const label = await this.getExplorationTypeSelector().locator('input[checked] + label').textContent();
const label = await this.getExplorationTypeSelector().locator('button[data-testid="is-active"]').textContent();
return label?.trim();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ import {
SplitLayout,
} from '@grafana/scenes';
import { IconButton, InlineLabel, useStyles2 } from '@grafana/ui';
import { useResizeObserver } from '@react-aria/utils';
import { displayError, displaySuccess } from '@shared/domain/displayStatus';
import { reportInteraction } from '@shared/domain/reportInteraction';
import { VersionInfoTooltip } from '@shared/ui/VersionInfoTooltip';
import React, { useRef, useState } from 'react';
import React from 'react';

import { SceneExploreAllServices } from '../../components/SceneExploreAllServices/SceneExploreAllServices';
import { SceneExploreFavorites } from '../../components/SceneExploreFavorites/SceneExploreFavorites';
Expand All @@ -45,7 +44,7 @@ import { ScenePanelTypeSwitcher } from '../SceneByVariableRepeaterGrid/component
import { SceneQuickFilter } from '../SceneByVariableRepeaterGrid/components/SceneQuickFilter';
import { GridItemData } from '../SceneByVariableRepeaterGrid/types/GridItemData';
import { SceneExploreServiceFlameGraph } from '../SceneExploreServiceFlameGraph/SceneExploreServiceFlameGraph';
import { ExplorationTypeSelector, ExplorationTypeSelectorProps } from './ui/ExplorationTypeSelector';
import { ExplorationTypeSelector } from './ui/ExplorationTypeSelector';

export interface SceneProfilesExplorerState extends Partial<EmbeddedSceneState> {
explorationType?: ExplorationType;
Expand Down Expand Up @@ -87,6 +86,7 @@ export class SceneProfilesExplorer extends SceneObjectBase<SceneProfilesExplorer
value: ExplorationType.FAVORITES,
label: 'Favorites',
description: 'Overview of favorited visualizations',
icon: 'favorite',
},
];

Expand Down Expand Up @@ -300,29 +300,6 @@ export class SceneProfilesExplorer extends SceneObjectBase<SceneProfilesExplorer
} catch {}
};

useExplorationTypeSelectorLayout = () => {
const headerRef = useRef<HTMLDivElement>(null);
const headerLeftRef = useRef<HTMLDivElement>(null);
const headerRightRef = useRef<HTMLDivElement>(null);

const [layout, setLayout] = useState<ExplorationTypeSelectorProps['layout']>('radio');

const onResize = () => {
const currentRight = headerRightRef.current?.getBoundingClientRect();
setLayout(Math.ceil(currentRight?.left || 970) >= 970 ? 'radio' : 'select');
};

useResizeObserver({ ref: headerRef, onResize });
useResizeObserver({ ref: headerRightRef, onResize });

return {
headerRef,
headerLeftRef,
headerRightRef,
layout,
};
};

useProfilesExplorer = () => {
const { explorationType, controls, body } = this.useState();

Expand All @@ -334,25 +311,12 @@ export class SceneProfilesExplorer extends SceneObjectBase<SceneProfilesExplorer
gridControls: Array<SceneObject & { key?: string }>;
};

const {
headerRef,
headerLeftRef,
headerRightRef,
layout: explorationTypeSelectorLayout,
} = this.useExplorationTypeSelectorLayout();

return {
data: {
explorationType,
dataSourceVariable,
timePickerControl,
refreshPickerControl,
headerRefs: {
full: headerRef,
left: headerLeftRef,
right: headerRightRef,
},
explorationTypeSelectorLayout,
sceneVariables,
gridControls,
body,
Expand All @@ -373,32 +337,29 @@ export class SceneProfilesExplorer extends SceneObjectBase<SceneProfilesExplorer
dataSourceVariable,
timePickerControl,
refreshPickerControl,
headerRefs,
explorationTypeSelectorLayout,
sceneVariables,
gridControls,
body,
} = data;

return (
<>
<div ref={headerRefs.full} className={styles.header}>
<div className={styles.header}>
<div className={styles.controls}>
<div ref={headerRefs.left} className={styles.headerLeft}>
<div className={styles.headerLeft}>
<div className={styles.dataSourceVariable}>
<InlineLabel width="auto">{dataSourceVariable.state.label}</InlineLabel>
<dataSourceVariable.Component model={dataSourceVariable} />
</div>

<ExplorationTypeSelector
layout={explorationTypeSelectorLayout}
options={SceneProfilesExplorer.EXPLORATION_TYPE_OPTIONS}
value={explorationType as string}
onChange={actions.onChangeExplorationType}
/>
</div>

<div ref={headerRefs.right} className={styles.headerRight}>
<div className={styles.headerRight}>
<timePickerControl.Component key={timePickerControl.state.key} model={timePickerControl} />
<refreshPickerControl.Component key={refreshPickerControl.state.key} model={refreshPickerControl} />
<IconButton
Expand Down Expand Up @@ -447,24 +408,13 @@ const getStyles = (theme: GrafanaTheme2) => ({
display: flex;
gap: ${theme.spacing(1)};
`,
dataSourceVariable: css`
display: flex;
min-width: 160px;
`,
explorationTypeContainer: css`
display: flex;
`,
explorationTypeRadio: css`
display: flex;
`,
explorationTypeSelect: css`
display: flex;
min-width: 180px;
`,
headerRight: css`
display: flex;
gap: ${theme.spacing(1)};
`,
dataSourceVariable: css`
display: flex;
`,
variable: css`
display: flex;
`,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,52 +1,71 @@
import { css } from '@emotion/css';
import { SelectableValue } from '@grafana/data';
import { InlineLabel, RadioButtonGroup, Select, useStyles2 } from '@grafana/ui';
import { css, cx } from '@emotion/css';
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
import { Button, Icon, InlineLabel, useStyles2 } from '@grafana/ui';
import { noOp } from '@shared/domain/noOp';
import React from 'react';

export type ExplorationTypeSelectorProps = {
layout: 'radio' | 'select';
options: Array<SelectableValue<string>>;
value: string;
onChange: (newValue: string) => void;
};

export function ExplorationTypeSelector({ layout, options, value, onChange }: ExplorationTypeSelectorProps) {
export function ExplorationTypeSelector({ options, value, onChange }: ExplorationTypeSelectorProps) {
const styles = useStyles2(getStyles);

return (
<div className={styles.explorationTypeContainer} data-testid="exploration-types">
<InlineLabel width="auto">Exploration type</InlineLabel>
<InlineLabel width="auto">Exploration</InlineLabel>

{layout === 'radio' ? (
<RadioButtonGroup
className={styles.explorationTypeRadio}
options={options}
value={value}
fullWidth={false}
onChange={onChange}
/>
) : (
<Select
className={styles.explorationTypeSelect}
placeholder="Select a type"
value={value}
options={options}
onChange={(option) => onChange(option.value!)}
/>
)}
<div className={styles.breadcrumb}>
{options.map((option, i) => {
const isActive = value === option.value;
return (
<>
<Button
className={isActive ? cx(styles.button, styles.active) : styles.button}
size="sm"
icon={option.icon as any}
variant={isActive ? 'primary' : 'secondary'}
onClick={isActive ? noOp : () => onChange(option.value as string)}
tooltip={option.description}
tooltipPlacement="top"
data-testid={isActive ? 'is-active' : undefined}
>
{option.label}
</Button>

{i < options.length - 2 && <Icon name="arrow-right" />}
</>
);
})}
</div>
</div>
);
}

const getStyles = () => ({
const getStyles = (theme: GrafanaTheme2) => ({
explorationTypeContainer: css`
display: flex;
align-items: center;
`,
explorationTypeRadio: css`
breadcrumb: css`
height: 32px;
line-height: 32px;
display: flex;
align-items: center;
& > button:last-child {
margin-left: ${theme.spacing(2)};
}
`,
explorationTypeSelect: css`
display: flex;
min-width: 180px;
button: css`
height: 30px;
line-height: 30px;
`,
active: css`
&:hover {
cursor: default;
}
`,
});

1 comment on commit 60dab67

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unit test coverage

Lines Statements Branches Functions
Coverage: 12%
12.58% (470/3736) 9.82% (138/1404) 9.49% (108/1138)

Please sign in to comment.