diff --git a/.changeset/famous-files-attend.md b/.changeset/famous-files-attend.md new file mode 100644 index 000000000..8db7ac93d --- /dev/null +++ b/.changeset/famous-files-attend.md @@ -0,0 +1,5 @@ +--- +'@hyperdx/app': minor +--- + +feat: Toggle columns from LogSidePanel diff --git a/packages/app/src/LogSidePanel.tsx b/packages/app/src/LogSidePanel.tsx index 3e4bc48ac..1d5dae4c5 100644 --- a/packages/app/src/LogSidePanel.tsx +++ b/packages/app/src/LogSidePanel.tsx @@ -482,6 +482,8 @@ function TraceSubpanel({ onPropertyAddClick, generateChartUrl, generateSearchUrl, + displayedColumns, + toggleColumn, }: { logData: any; onClose: () => void; @@ -493,6 +495,8 @@ function TraceSubpanel({ }) => string; onPropertyAddClick?: (name: string, value: string) => void; + displayedColumns?: string[]; + toggleColumn?: (column: string) => void; }) { const date = new Date(logData.timestamp); const start = add(date, { minutes: -240 }); @@ -681,6 +685,8 @@ function TraceSubpanel({ generateSearchUrl={generateSearchUrl} onClose={onClose} generateChartUrl={generateChartUrl} + displayedColumns={displayedColumns} + toggleColumn={toggleColumn} /> @@ -1300,6 +1306,8 @@ function PropertySubpanel({ generateSearchUrl, onClose, generateChartUrl, + displayedColumns, + toggleColumn, }: { logData: any; generateSearchUrl: (query?: string, timeRange?: [Date, Date]) => string; @@ -1312,6 +1320,8 @@ function PropertySubpanel({ }) => string; onPropertyAddClick?: (key: string, value: string) => void; + displayedColumns?: string[]; + toggleColumn?: (column: string) => void; }) { const [propertySearchValue, setPropertySearchValue] = useState(''); const [isNestedView, setIsNestedView] = useLocalStorage( @@ -1582,6 +1592,8 @@ function PropertySubpanel({ }} valueRenderer={(raw, value, ...rawKeyPath) => { const keyPath = rawKeyPath.slice().reverse(); + const keyPathString = keyPath.join('.'); + return (
                   
                 ) : null}
+
+                {!!toggleColumn && keyPath.length === 1 ? (
+                  
+                ) : null}
+
                  {
@@ -2032,6 +2062,8 @@ export default function LogSidePanel({
   generateChartUrl,
   sortKey,
   isNestedPanel = false,
+  displayedColumns,
+  toggleColumn,
 }: {
   logId: string | undefined;
   onClose: () => void;
@@ -2048,6 +2080,8 @@ export default function LogSidePanel({
   }) => string;
   sortKey: string | undefined;
   isNestedPanel?: boolean;
+  displayedColumns?: string[];
+  toggleColumn?: (column: string) => void;
 }) {
   const contextZIndex = useZIndex();
 
@@ -2222,6 +2256,8 @@ export default function LogSidePanel({
                       generateSearchUrl={generateSearchUrl}
                       generateChartUrl={generateChartUrl}
                       onClose={_onClose}
+                      displayedColumns={displayedColumns}
+                      toggleColumn={toggleColumn}
                     />
                     
                   
) : null} diff --git a/packages/app/src/LogTable.tsx b/packages/app/src/LogTable.tsx index 71e2187df..e0aad4eda 100644 --- a/packages/app/src/LogTable.tsx +++ b/packages/app/src/LogTable.tsx @@ -784,6 +784,8 @@ export default function LogTable({ onEnd, onShowPatternsClick, tableId, + displayedColumns, + setDisplayedColumns, }: { config: { where: string; @@ -802,10 +804,11 @@ export default function LogTable({ onEnd?: () => void; onShowPatternsClick?: () => void; tableId?: string; + displayedColumns: string[]; + setDisplayedColumns: (columns: string[]) => void; }) { const [instructionsOpen, setInstructionsOpen] = useState(false); const [settingsOpen, setSettingsOpen] = useState(false); - const [displayedColumns, setDisplayedColumns] = useState([]); const [wrapLines, setWrapLines] = useState(false); const prevQueryConfig = usePrevious({ searchedQuery, isLive }); diff --git a/packages/app/src/LogTableWithSidePanel.tsx b/packages/app/src/LogTableWithSidePanel.tsx index cc5d1f3c4..730ad9097 100644 --- a/packages/app/src/LogTableWithSidePanel.tsx +++ b/packages/app/src/LogTableWithSidePanel.tsx @@ -4,6 +4,7 @@ import usePortal from 'react-useportal'; import type { LogView } from './types'; import LogSidePanel from './LogSidePanel'; import LogTable from './LogTable'; +import { useDisplayedColumns } from './useDisplayedColumns'; export function LogTableWithSidePanel({ config, @@ -82,6 +83,9 @@ export function LogTableWithSidePanel({ const voidFn = useCallback(() => {}, []); + const { displayedColumns, setDisplayedColumns, toggleColumn } = + useDisplayedColumns(); + return ( <> {openedLog != null ? ( @@ -95,6 +99,8 @@ export function LogTableWithSidePanel({ onPropertyAddClick={onPropertyAddClick} generateSearchUrl={generateSearchUrl} generateChartUrl={generateChartUrl} + displayedColumns={displayedColumns} + toggleColumn={toggleColumn} /> ) : null} @@ -114,6 +120,8 @@ export function LogTableWithSidePanel({ [setOpenedLog, onRowExpandClick], )} onEnd={onSettled} + displayedColumns={displayedColumns} + setDisplayedColumns={setDisplayedColumns} /> ); diff --git a/packages/app/src/SearchPage.tsx b/packages/app/src/SearchPage.tsx index de6efcffc..69ea95049 100644 --- a/packages/app/src/SearchPage.tsx +++ b/packages/app/src/SearchPage.tsx @@ -45,6 +45,7 @@ import SearchPageActionBar from './SearchPageActionBar'; import { useTimeQuery } from './timeQuery'; import { MemoPatternTableWithSidePanel } from './PatternTableWithSidePanel'; import { ErrorBoundary } from 'react-error-boundary'; +import { useDisplayedColumns } from './useDisplayedColumns'; const formatDate = ( date: Date, @@ -323,6 +324,9 @@ const LogViewerContainer = memo(function LogViewerContainer({ [setOpenedLogQuery], ); + const { displayedColumns, setDisplayedColumns, toggleColumn } = + useDisplayedColumns(); + return ( <> ); diff --git a/packages/app/src/useDisplayedColumns.ts b/packages/app/src/useDisplayedColumns.ts new file mode 100644 index 000000000..bf9978b43 --- /dev/null +++ b/packages/app/src/useDisplayedColumns.ts @@ -0,0 +1,16 @@ +import { useState } from 'react'; + +// TODO: Instead of prop drilling additional columns, we can consider using React.Context or Jotai +export const useDisplayedColumns = () => { + const [displayedColumns, setDisplayedColumns] = useState([]); + + const toggleColumn = (column: string) => { + if (displayedColumns.includes(column)) { + setDisplayedColumns(displayedColumns.filter(c => c !== column)); + } else { + setDisplayedColumns([...displayedColumns, column]); + } + }; + + return { displayedColumns, setDisplayedColumns, toggleColumn }; +};