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
5 changes: 5 additions & 0 deletions .changeset/source-chip-kebab-menu.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hyperdx/app': patch
---

feat(source-picker): chip + kebab menu UX
57 changes: 46 additions & 11 deletions packages/app/src/DBSearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,9 @@ import DBSqlRowTableWithSideBar from './components/DBSqlRowTableWithSidebar';
import PatternTable from './components/PatternTable';
import { DBSearchHeatmapChart } from './components/Search/DBSearchHeatmapChart';
import DirectTraceSidePanel from './components/Search/DirectTraceSidePanel';
import SourceSchemaPreview from './components/SourceSchemaPreview';
import SourceSchemaPreview, {
isSourceSchemaPreviewEnabled,
} from './components/SourceSchemaPreview';
import {
getRelativeTimeOptionLabel,
LIVE_TAIL_DURATION_MS,
Expand Down Expand Up @@ -1664,10 +1666,8 @@ export function DBSearchPage() {
[inputSourceObj],
);

const sourceSchemaPreview = useMemo(
() => <SourceSchemaPreview source={inputSourceObj} variant="text" />,
[inputSourceObj],
);
const [isSourceSchemaPreviewOpen, setIsSourceSchemaPreviewOpen] =
useState(false);

const onTimePickerSearch = useCallback(
(range: string) => {
Expand Down Expand Up @@ -1742,13 +1742,38 @@ export function DBSearchPage() {
setModelFormExpanded(false);
}, [setModelFormExpanded]);

const onEditSources = useCallback(() => {
// `Edit source` (singular): operate on the currently selected source.
// Local mode opens the inline edit modal seeded with `inputSource`;
// non-local uses a hard navigation so the page's `useQueryStates`
// (source/where/select/whereLanguage/filters/orderBy) can't merge
// stale /search state into the destination URL, and so
// `router.basePath` is correctly prepended for the /clickstack build.
const onEditCurrentSource = useCallback(() => {
if (IS_LOCAL_MODE) {
setModelFormExpanded(v => !v);
setModelFormExpanded(true);
return;
}
if (inputSource) {
window.location.assign(`${router.basePath}/team?source=${inputSource}`);
} else {
router.push('/team');
window.location.assign(`${router.basePath}/team`);
}
}, [setModelFormExpanded]);
}, [inputSource, setModelFormExpanded]);

// `Manage sources`: open the all-sources list view. Only wired in
// non-local mode; local has no list-view surface so the menu item
// hides itself when this prop is undefined. We use `window.location`
// for a hard navigation instead of `router.push` so the page's
// `useQueryStates` (source/where/select/whereLanguage/filters/orderBy)
// can't restore its state into the new URL during the client-side
// transition, and so `router.basePath` is correctly prepended for
// the /clickstack build.
const onManageSources = useMemo(() => {
if (IS_LOCAL_MODE) return undefined;
return () => {
window.location.assign(`${router.basePath}/team`);
};
}, []);

const setNewSourceModalClosed = useCallback(
() => setNewSourceModalOpened(false),
Expand Down Expand Up @@ -1885,12 +1910,22 @@ export function DBSearchPage() {
control={control}
name="source"
onCreate={openNewSourceModal}
onEdit={onEditSources}
onEdit={onEditCurrentSource}
onManageSources={onManageSources}
onSchemaPreview={() => setIsSourceSchemaPreviewOpen(true)}
isSchemaPreviewEnabled={isSourceSchemaPreviewEnabled(
inputSourceObj,
)}
allowedSourceKinds={ALLOWED_SOURCE_KINDS}
data-testid="source-selector"
sourceSchemaPreview={sourceSchemaPreview}
style={{ minWidth: 150 }}
/>
<SourceSchemaPreview
source={inputSourceObj}
controlled
open={isSourceSchemaPreviewOpen}
onClose={() => setIsSourceSchemaPreviewOpen(false)}
/>
<Box style={{ flex: '1 1 0%', minWidth: 100 }}>
<SQLInlineEditorControlled
tableConnection={inputSourceTableConnection}
Expand Down
31 changes: 21 additions & 10 deletions packages/app/src/DBServiceMapPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import { withAppNav } from '@/layout';
import OnboardingModal from './components/OnboardingModal';
import ServiceMap from './components/ServiceMap/ServiceMap';
import { TableSourceForm } from './components/Sources/SourceForm';
import SourceSchemaPreview from './components/SourceSchemaPreview';
import SourceSchemaPreview, {
isSourceSchemaPreviewEnabled,
} from './components/SourceSchemaPreview';
import { SourceSelectControlled } from './components/SourceSelect';
import { TimePicker } from './components/TimePicker';
import { useBrandDisplayName } from './theme/ThemeProvider';
Expand Down Expand Up @@ -86,6 +88,8 @@ function DBServiceMapPage() {
});

const watchedSource = useWatch({ control, name: 'source' });
const [isSourceSchemaPreviewOpen, setIsSourceSchemaPreviewOpen] =
useState(false);

useEffect(() => {
if (watchedSource !== sourceId) {
Expand Down Expand Up @@ -116,15 +120,22 @@ function DBServiceMapPage() {
);

const sourceSelect = source ? (
<SourceSelectControlled
control={control}
name="source"
size="xs"
allowedSourceKinds={[SourceKind.Trace]}
sourceSchemaPreview={
<SourceSchemaPreview source={source} variant="text" />
}
/>
<>
<SourceSelectControlled
control={control}
name="source"
size="xs"
allowedSourceKinds={[SourceKind.Trace]}
onSchemaPreview={() => setIsSourceSchemaPreviewOpen(true)}
isSchemaPreviewEnabled={isSourceSchemaPreviewEnabled(source)}
/>
<SourceSchemaPreview
source={source}
controlled
open={isSourceSchemaPreviewOpen}
onClose={() => setIsSourceSchemaPreviewOpen(false)}
/>
</>
) : null;

const headerActions = (
Expand Down
17 changes: 13 additions & 4 deletions packages/app/src/DashboardFiltersModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ import SearchWhereInput, {
import { SQLInlineEditorControlled } from '@/components/SQLEditor/SQLInlineEditor';

import { SourceMultiSelectControlled } from './components/SourceMultiSelect';
import SourceSchemaPreview from './components/SourceSchemaPreview';
import SourceSchemaPreview, {
isSourceSchemaPreviewEnabled,
} from './components/SourceSchemaPreview';
import { SourceSelectControlled } from './components/SourceSelect';
import { useSource, useSources } from './source';
import { getMetricTableName } from './utils';
Expand Down Expand Up @@ -139,6 +141,8 @@ const DashboardFilterEditForm = ({
const [modalContentRef, setModalContentRef] = useState<HTMLElement | null>(
null,
);
const [isSourceSchemaPreviewOpen, setIsSourceSchemaPreviewOpen] =
useState(false);

return (
<Modal
Expand Down Expand Up @@ -183,11 +187,16 @@ const DashboardFilterEditForm = ({
data-testid="source-selector"
rules={{ required: true }}
comboboxProps={{ withinPortal: true }}
sourceSchemaPreview={
<SourceSchemaPreview source={source} variant="text" />
}
onSchemaPreview={() => setIsSourceSchemaPreviewOpen(true)}
isSchemaPreviewEnabled={isSourceSchemaPreviewEnabled(source)}
disabled={!!presetSource}
/>
<SourceSchemaPreview
source={source}
controlled
open={isSourceSchemaPreviewOpen}
onClose={() => setIsSourceSchemaPreviewOpen(false)}
/>
</CustomInputWrapper>
{!presetSource && (
<CustomInputWrapper
Expand Down
32 changes: 24 additions & 8 deletions packages/app/src/KubernetesDashboardPage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { useCallback, useEffect, useMemo, useRef } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import dynamic from 'next/dynamic';
import Head from 'next/head';
import Link from 'next/link';
Expand Down Expand Up @@ -50,7 +50,9 @@ import { DBTimeChart } from './components/DBTimeChart';
import { FormatPodStatus } from './components/KubeComponents';
import { KubernetesFilters } from './components/KubernetesFilters';
import OnboardingModal from './components/OnboardingModal';
import SourceSchemaPreview from './components/SourceSchemaPreview';
import SourceSchemaPreview, {
isSourceSchemaPreviewEnabled,
} from './components/SourceSchemaPreview';
import { SourceSelectControlled } from './components/SourceSelect';
import { useQueriedChartConfig } from './hooks/useChartConfig';
import { useDashboardRefresh } from './hooks/useDashboardRefresh';
Expand Down Expand Up @@ -1187,6 +1189,10 @@ function KubernetesDashboardPage() {
defaultValue: 'pods',
});

const [isLogSchemaPreviewOpen, setIsLogSchemaPreviewOpen] = useState(false);
const [isMetricSchemaPreviewOpen, setIsMetricSchemaPreviewOpen] =
useState(false);

const [searchQuery, setSearchQuery] = useQueryState('q', {
defaultValue: '',
});
Expand Down Expand Up @@ -1257,19 +1263,29 @@ function KubernetesDashboardPage() {
allowedSourceKinds={[SourceKind.Log]}
size="xs"
allowDeselect={false}
sourceSchemaPreview={
<SourceSchemaPreview source={logSource} variant="text" />
}
onSchemaPreview={() => setIsLogSchemaPreviewOpen(true)}
isSchemaPreviewEnabled={isSourceSchemaPreviewEnabled(logSource)}
/>
<SourceSchemaPreview
source={logSource}
controlled
open={isLogSchemaPreviewOpen}
onClose={() => setIsLogSchemaPreviewOpen(false)}
/>
<SourceSelectControlled
name="metricSourceId"
control={control}
allowedSourceKinds={[SourceKind.Metric]}
size="xs"
allowDeselect={false}
sourceSchemaPreview={
<SourceSchemaPreview source={metricSource} variant="text" />
}
onSchemaPreview={() => setIsMetricSchemaPreviewOpen(true)}
isSchemaPreviewEnabled={isSourceSchemaPreviewEnabled(metricSource)}
/>
<SourceSchemaPreview
source={metricSource}
controlled
open={isMetricSchemaPreviewOpen}
onClose={() => setIsMetricSchemaPreviewOpen(false)}
/>
</Group>
);
Expand Down
7 changes: 6 additions & 1 deletion packages/app/src/__tests__/DBSearchPage.directTrace.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,12 @@ jest.mock('../components/PatternTable', () => () => <div />);
jest.mock('../components/Search/DBSearchHeatmapChart', () => ({
DBSearchHeatmapChart: () => <div />,
}));
jest.mock('../components/SourceSchemaPreview', () => () => <div />);
jest.mock('../components/SourceSchemaPreview', () => ({
__esModule: true,
default: () => <div />,
isSourceSchemaPreviewEnabled: () => false,
getSourceSchemaTables: () => [],
}));
jest.mock('../components/Error/ErrorBoundary', () => ({
ErrorBoundary: ({ children }: { children: React.ReactNode }) => (
<>{children}</>
Expand Down
20 changes: 14 additions & 6 deletions packages/app/src/components/ChartEditor/RawSqlChartEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useMemo } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { Control, UseFormSetValue, useWatch } from 'react-hook-form';
import {
TableConnection,
Expand Down Expand Up @@ -31,7 +31,9 @@ import { DEFAULT_TILE_ALERT } from '@/utils/alerts';

import { ConnectionSelectControlled } from '../ConnectionSelect';
import { OnClickFormButton } from '../DBEditTimeChartForm/OnClickForm/OnClickFormButton';
import SourceSchemaPreview from '../SourceSchemaPreview';
import SourceSchemaPreview, {
isSourceSchemaPreviewEnabled,
} from '../SourceSchemaPreview';
import { SourceSelectControlled } from '../SourceSelect';

import { SQL_PLACEHOLDERS } from './constants';
Expand Down Expand Up @@ -137,9 +139,8 @@ export default function RawSqlChartEditor({
return [...paramCompletions, ...macroCompletions];
}, [displayType]);

const sourceSchemaPreview = useMemo(() => {
return <SourceSchemaPreview source={sourceObject} variant="text" />;
}, [sourceObject]);
const [isSourceSchemaPreviewOpen, setIsSourceSchemaPreviewOpen] =
useState(false);

const tableConnections: TableConnection[] = useMemo(() => {
if (!sources) return [];
Expand Down Expand Up @@ -206,7 +207,14 @@ export default function RawSqlChartEditor({
size="xs"
clearable
placeholder="None"
sourceSchemaPreview={sourceSchemaPreview}
onSchemaPreview={() => setIsSourceSchemaPreviewOpen(true)}
isSchemaPreviewEnabled={isSourceSchemaPreviewEnabled(sourceObject)}
/>
<SourceSchemaPreview
source={sourceObject}
controlled
open={isSourceSchemaPreviewOpen}
onClose={() => setIsSourceSchemaPreviewOpen(false)}
/>
</Group>
<Group gap="xs">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useState } from 'react';
import {
Control,
FieldArrayWithId,
Expand Down Expand Up @@ -25,7 +26,9 @@ import {
} from '@/components/ChartEditor/types';
import MVOptimizationIndicator from '@/components/MaterializedViews/MVOptimizationIndicator';
import SearchWhereInput from '@/components/SearchInput/SearchWhereInput';
import SourceSchemaPreview from '@/components/SourceSchemaPreview';
import SourceSchemaPreview, {
isSourceSchemaPreviewEnabled,
} from '@/components/SourceSchemaPreview';
import { SourceSelectControlled } from '@/components/SourceSelect';
import { SQLInlineEditorControlled } from '@/components/SQLEditor/SQLInlineEditor';
import { IS_LOCAL_MODE } from '@/config';
Expand Down Expand Up @@ -91,6 +94,9 @@ export function ChartEditorControls({
openDisplaySettings,
openHeatmapSettings,
}: ChartEditorControlsProps) {
const [isSourceSchemaPreviewOpen, setIsSourceSchemaPreviewOpen] =
useState(false);

return (
<>
<Flex mb="md" align="center" justify="space-between">
Expand All @@ -108,9 +114,14 @@ export function ChartEditorControls({
? [...HEATMAP_ALLOWED_SOURCE_KINDS]
: undefined
}
sourceSchemaPreview={
<SourceSchemaPreview source={tableSource} variant="text" />
}
onSchemaPreview={() => setIsSourceSchemaPreviewOpen(true)}
isSchemaPreviewEnabled={isSourceSchemaPreviewEnabled(tableSource)}
/>
<SourceSchemaPreview
source={tableSource}
controlled
open={isSourceSchemaPreviewOpen}
onClose={() => setIsSourceSchemaPreviewOpen(false)}
/>
</Group>
<Group>
Expand Down
Loading
Loading