New Logs Panel: Add events for Panel and Details (#108272)
* New Logs Panel: Add events for Panel and Details * Logs Panel: add noInteractions property * New Logs Panel: conditionally report interactions * Test
This commit is contained in:
@@ -24,6 +24,7 @@ export interface Options {
|
||||
logLineMenuCustomItems?: unknown;
|
||||
logRowMenuIconsAfter?: unknown;
|
||||
logRowMenuIconsBefore?: unknown;
|
||||
noInteractions?: boolean;
|
||||
/**
|
||||
* TODO: figure out how to define callbacks
|
||||
*/
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Resizable } from 're-resizable';
|
||||
import { memo, useCallback, useEffect, useRef } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { getDragStyles, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { LogLineDetailsComponent } from './LogLineDetailsComponent';
|
||||
@@ -20,13 +21,18 @@ export interface Props {
|
||||
export type LogLineDetailsMode = 'inline' | 'sidebar';
|
||||
|
||||
export const LogLineDetails = ({ containerElement, focusLogLine, logs, onResize }: Props) => {
|
||||
const { detailsWidth, setDetailsWidth, showDetails } = useLogListContext();
|
||||
const { detailsWidth, noInteractions, setDetailsWidth, showDetails } = useLogListContext();
|
||||
const styles = useStyles2(getStyles, 'sidebar');
|
||||
const dragStyles = useStyles2(getDragStyles);
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
focusLogLine(showDetails[0]);
|
||||
if (!noInteractions) {
|
||||
reportInteraction('logs_log_line_details_displayed', {
|
||||
mode: 'sidebar',
|
||||
});
|
||||
}
|
||||
// Just once
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
@@ -38,6 +44,14 @@ export const LogLineDetails = ({ containerElement, focusLogLine, logs, onResize
|
||||
onResize();
|
||||
}, [onResize, setDetailsWidth]);
|
||||
|
||||
const reportResize = useCallback(() => {
|
||||
if (containerRef.current && !noInteractions) {
|
||||
reportInteraction('logs_log_line_details_sidebar_resized', {
|
||||
width: Math.round(containerRef.current.clientWidth),
|
||||
});
|
||||
}
|
||||
}, [noInteractions]);
|
||||
|
||||
const maxWidth = containerElement.clientWidth - LOG_LIST_MIN_WIDTH;
|
||||
|
||||
if (!showDetails.length) {
|
||||
@@ -47,6 +61,7 @@ export const LogLineDetails = ({ containerElement, focusLogLine, logs, onResize
|
||||
return (
|
||||
<Resizable
|
||||
onResize={handleResize}
|
||||
onResizeStop={reportResize}
|
||||
handleClasses={{ left: dragStyles.dragHandleVertical }}
|
||||
defaultSize={{ width: detailsWidth, height: containerElement.clientHeight }}
|
||||
size={{ width: detailsWidth, height: containerElement.clientHeight }}
|
||||
@@ -68,10 +83,18 @@ export interface InlineLogLineDetailsProps {
|
||||
}
|
||||
|
||||
export const InlineLogLineDetails = memo(({ logs }: InlineLogLineDetailsProps) => {
|
||||
const { showDetails } = useLogListContext();
|
||||
const { noInteractions, showDetails } = useLogListContext();
|
||||
const styles = useStyles2(getStyles, 'inline');
|
||||
const scrollRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!noInteractions) {
|
||||
reportInteraction('logs_log_line_details_displayed', {
|
||||
mode: 'inline',
|
||||
});
|
||||
}
|
||||
}, [noInteractions]);
|
||||
|
||||
const saveScroll = useCallback(() => {
|
||||
saveDetailsScrollPosition(showDetails[0], scrollRef.current?.scrollTop ?? 0);
|
||||
}, [showDetails]);
|
||||
|
||||
@@ -4,6 +4,7 @@ import { memo, startTransition, useCallback, useMemo, useRef, useState } from 'r
|
||||
|
||||
import { DataFrameType, GrafanaTheme2, store } from '@grafana/data';
|
||||
import { t, Trans } from '@grafana/i18n';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { ControlledCollapse, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { getLabelTypeFromRow } from '../../utils';
|
||||
@@ -23,7 +24,7 @@ interface LogLineDetailsComponentProps {
|
||||
}
|
||||
|
||||
export const LogLineDetailsComponent = memo(({ log, logs }: LogLineDetailsComponentProps) => {
|
||||
const { displayedFields, logOptionsStorageKey, setDisplayedFields } = useLogListContext();
|
||||
const { displayedFields, noInteractions, logOptionsStorageKey, setDisplayedFields } = useLogListContext();
|
||||
const [search, setSearch] = useState('');
|
||||
const inputRef = useRef('');
|
||||
const styles = useStyles2(getStyles);
|
||||
@@ -75,8 +76,14 @@ export const LogLineDetailsComponent = memo(({ log, logs }: LogLineDetailsCompon
|
||||
const handleToggle = useCallback(
|
||||
(option: string, isOpen: boolean) => {
|
||||
store.set(`${logOptionsStorageKey}.log-details.${option}`, isOpen);
|
||||
if (!noInteractions) {
|
||||
reportInteraction('logs_log_line_details_section_toggled', {
|
||||
section: option.replace('Open', ''),
|
||||
state: isOpen ? 'open' : 'closed',
|
||||
});
|
||||
}
|
||||
},
|
||||
[logOptionsStorageKey]
|
||||
[logOptionsStorageKey, noInteractions]
|
||||
);
|
||||
|
||||
const handleSearch = useCallback((newSearch: string) => {
|
||||
|
||||
@@ -143,6 +143,7 @@ export const LogLineDetailsField = ({
|
||||
closeDetails,
|
||||
displayedFields,
|
||||
isLabelFilterActive,
|
||||
noInteractions,
|
||||
onClickFilterLabel,
|
||||
onClickFilterOutLabel,
|
||||
onClickShowField,
|
||||
@@ -179,53 +180,59 @@ export const LogLineDetailsField = ({
|
||||
}
|
||||
}, [showFieldsStats, updateStats]);
|
||||
|
||||
const reportInteractionWrapper = useCallback(
|
||||
(interactionName: string, properties?: Record<string, unknown>) => {
|
||||
if (noInteractions) {
|
||||
return;
|
||||
}
|
||||
reportInteraction(interactionName, properties);
|
||||
},
|
||||
[noInteractions]
|
||||
);
|
||||
|
||||
const showField = useCallback(() => {
|
||||
if (onClickShowField) {
|
||||
onClickShowField(keys[0]);
|
||||
}
|
||||
|
||||
reportInteraction('grafana_explore_logs_log_details_replace_line_clicked', {
|
||||
reportInteractionWrapper('logs_log_line_details_show_field_clicked', {
|
||||
datasourceType: log.datasourceType,
|
||||
logRowUid: log.uid,
|
||||
type: 'enable',
|
||||
});
|
||||
}, [onClickShowField, keys, log.datasourceType, log.uid]);
|
||||
}, [onClickShowField, reportInteractionWrapper, log.datasourceType, keys]);
|
||||
|
||||
const hideField = useCallback(() => {
|
||||
if (onClickHideField) {
|
||||
onClickHideField(keys[0]);
|
||||
}
|
||||
|
||||
reportInteraction('grafana_explore_logs_log_details_replace_line_clicked', {
|
||||
reportInteractionWrapper('logs_log_line_details_hide_field_clicked', {
|
||||
datasourceType: log.datasourceType,
|
||||
logRowUid: log.uid,
|
||||
type: 'disable',
|
||||
});
|
||||
}, [onClickHideField, keys, log.datasourceType, log.uid]);
|
||||
}, [onClickHideField, reportInteractionWrapper, log.datasourceType, keys]);
|
||||
|
||||
const filterLabel = useCallback(() => {
|
||||
if (onClickFilterLabel) {
|
||||
onClickFilterLabel(keys[0], values[0], logRowToSingleRowDataFrame(log) || undefined);
|
||||
}
|
||||
|
||||
reportInteraction('grafana_explore_logs_log_details_filter_clicked', {
|
||||
reportInteractionWrapper('logs_log_line_details_filter_clicked', {
|
||||
datasourceType: log.datasourceType,
|
||||
filterType: 'include',
|
||||
logRowUid: log.uid,
|
||||
});
|
||||
}, [onClickFilterLabel, keys, values, log]);
|
||||
}, [onClickFilterLabel, reportInteractionWrapper, log, keys, values]);
|
||||
|
||||
const filterOutLabel = useCallback(() => {
|
||||
if (onClickFilterOutLabel) {
|
||||
onClickFilterOutLabel(keys[0], values[0], logRowToSingleRowDataFrame(log) || undefined);
|
||||
}
|
||||
|
||||
reportInteraction('grafana_explore_logs_log_details_filter_clicked', {
|
||||
reportInteractionWrapper('logs_log_line_details_filter_clicked', {
|
||||
datasourceType: log.datasourceType,
|
||||
filterType: 'exclude',
|
||||
logRowUid: log.uid,
|
||||
});
|
||||
}, [onClickFilterOutLabel, keys, values, log]);
|
||||
}, [onClickFilterOutLabel, reportInteractionWrapper, log, keys, values]);
|
||||
|
||||
const labelFilterActive = useCallback(async () => {
|
||||
if (isLabelFilterActive) {
|
||||
@@ -237,14 +244,14 @@ export const LogLineDetailsField = ({
|
||||
const showStats = useCallback(() => {
|
||||
setShowFieldStats((showFieldStats: boolean) => !showFieldStats);
|
||||
|
||||
reportInteraction('grafana_explore_logs_log_details_stats_clicked', {
|
||||
reportInteractionWrapper('logs_log_line_details_stats_clicked', {
|
||||
dataSourceType: log.datasourceType,
|
||||
fieldType: isLabel ? 'label' : 'detectedField',
|
||||
fieldType: isLabel ? 'label' : 'field',
|
||||
type: showFieldsStats ? 'close' : 'open',
|
||||
logRowUid: log.uid,
|
||||
app,
|
||||
});
|
||||
}, [app, isLabel, log.datasourceType, log.uid, showFieldsStats]);
|
||||
}, [app, isLabel, log.datasourceType, log.uid, reportInteractionWrapper, showFieldsStats]);
|
||||
|
||||
const refIdTooltip = useMemo(
|
||||
() => (app === CoreApp.Explore && log.dataFrame?.refId ? ` in query ${log.dataFrame?.refId}` : ''),
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useCallback, useMemo, MouseEvent, useRef, ChangeEvent } from 'react';
|
||||
|
||||
import { colorManipulator, GrafanaTheme2, LogRowModel, store } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { IconButton, Input, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { copyText, handleOpenLogsContextClick } from '../../utils';
|
||||
@@ -26,6 +27,7 @@ export const LogLineDetailsHeader = ({ log, search, onSearch }: Props) => {
|
||||
getRowContextQuery,
|
||||
logOptionsStorageKey,
|
||||
logSupportsContext,
|
||||
noInteractions,
|
||||
setDetailsMode,
|
||||
onClickHideField,
|
||||
onClickShowField,
|
||||
@@ -39,14 +41,27 @@ export const LogLineDetailsHeader = ({ log, search, onSearch }: Props) => {
|
||||
const styles = useStyles2(getStyles, detailsMode, wrapLogMessage);
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
const searchUsedRef = useRef(false);
|
||||
|
||||
const reportInteractionWrapper = useCallback(
|
||||
(interactionName: string, properties?: Record<string, unknown>) => {
|
||||
if (noInteractions) {
|
||||
return;
|
||||
}
|
||||
reportInteraction(interactionName, properties);
|
||||
},
|
||||
[noInteractions]
|
||||
);
|
||||
|
||||
const copyLogLine = useCallback(() => {
|
||||
copyText(log.entry, containerRef);
|
||||
}, [log.entry]);
|
||||
reportInteractionWrapper('logs_log_line_details_header_copy_clicked');
|
||||
}, [log.entry, reportInteractionWrapper]);
|
||||
|
||||
const copyLinkToLogLine = useCallback(() => {
|
||||
onPermalinkClick?.(log);
|
||||
}, [log, onPermalinkClick]);
|
||||
reportInteractionWrapper('logs_log_line_details_header_permalink_clicked');
|
||||
}, [log, onPermalinkClick, reportInteractionWrapper]);
|
||||
|
||||
const togglePinning = useCallback(() => {
|
||||
if (pinned) {
|
||||
@@ -54,7 +69,8 @@ export const LogLineDetailsHeader = ({ log, search, onSearch }: Props) => {
|
||||
} else {
|
||||
onPinLine?.(log);
|
||||
}
|
||||
}, [log, onPinLine, onUnpinLine, pinned]);
|
||||
reportInteractionWrapper('logs_log_line_details_header_pinning_clicked');
|
||||
}, [log, onPinLine, onUnpinLine, pinned, reportInteractionWrapper]);
|
||||
|
||||
const shouldlogSupportsContext = useMemo(
|
||||
() => (logSupportsContext ? logSupportsContext(log) : false),
|
||||
@@ -64,8 +80,9 @@ export const LogLineDetailsHeader = ({ log, search, onSearch }: Props) => {
|
||||
const showContext = useCallback(
|
||||
async (event: MouseEvent<HTMLElement>) => {
|
||||
handleOpenLogsContextClick(event, log, getRowContextQuery, (log: LogRowModel) => onOpenContext?.(log, () => {}));
|
||||
reportInteractionWrapper('logs_log_line_details_header_context_clicked');
|
||||
},
|
||||
[onOpenContext, getRowContextQuery, log]
|
||||
[log, getRowContextQuery, reportInteractionWrapper, onOpenContext]
|
||||
);
|
||||
|
||||
const showLogLineToggle = onClickHideField && onClickShowField && displayedFields.length > 0;
|
||||
@@ -86,7 +103,8 @@ export const LogLineDetailsHeader = ({ log, search, onSearch }: Props) => {
|
||||
} else {
|
||||
onClickShowField?.(LOG_LINE_BODY_FIELD_NAME);
|
||||
}
|
||||
}, [logLineDisplayed, onClickHideField, onClickShowField]);
|
||||
reportInteractionWrapper('logs_log_line_details_header_show_logline_clicked');
|
||||
}, [logLineDisplayed, onClickHideField, onClickShowField, reportInteractionWrapper]);
|
||||
|
||||
const clearSearch = useMemo(
|
||||
() => (
|
||||
@@ -95,6 +113,7 @@ export const LogLineDetailsHeader = ({ log, search, onSearch }: Props) => {
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
onSearch('');
|
||||
reportInteractionWrapper('logs_log_line_details_header_search_cleared');
|
||||
if (inputRef.current) {
|
||||
inputRef.current.value = '';
|
||||
}
|
||||
@@ -102,14 +121,18 @@ export const LogLineDetailsHeader = ({ log, search, onSearch }: Props) => {
|
||||
tooltip={t('logs.log-line-details.clear-search', 'Clear')}
|
||||
/>
|
||||
),
|
||||
[onSearch]
|
||||
[onSearch, reportInteractionWrapper]
|
||||
);
|
||||
|
||||
const handleSearch = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
onSearch(e.target.value);
|
||||
if (!searchUsedRef.current) {
|
||||
reportInteractionWrapper('logs_log_line_details_header_search_used');
|
||||
searchUsedRef.current = true;
|
||||
}
|
||||
},
|
||||
[onSearch]
|
||||
[onSearch, reportInteractionWrapper]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -2,6 +2,7 @@ import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { CoreApp, getDefaultTimeRange, LogRowModel, LogsDedupStrategy, LogsSortOrder, store } from '@grafana/data';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
|
||||
import { disablePopoverMenu, enablePopoverMenu, isPopoverMenuDisabled } from '../../utils';
|
||||
import { createLogRow } from '../mocks/logRow';
|
||||
@@ -12,6 +13,7 @@ jest.mock('@grafana/runtime', () => {
|
||||
return {
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
usePluginLinks: jest.fn().mockReturnValue({ links: [] }),
|
||||
reportInteraction: jest.fn(),
|
||||
config: {
|
||||
...jest.requireActual('@grafana/runtime').config,
|
||||
featureToggles: {
|
||||
@@ -338,4 +340,20 @@ describe('LogList', () => {
|
||||
expect(screen.getByText('some text')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
describe('Interactions', () => {
|
||||
beforeEach(() => {
|
||||
sessionStorage.clear();
|
||||
jest.mocked(reportInteraction).mockClear();
|
||||
});
|
||||
test('Reports interactions ', async () => {
|
||||
render(<LogList {...defaultProps} />);
|
||||
await screen.findByText('log message 1');
|
||||
expect(reportInteraction).toHaveBeenCalled();
|
||||
});
|
||||
test('Can disable interaction report ', async () => {
|
||||
render(<LogList {...defaultProps} noInteractions={true} />);
|
||||
await screen.findByText('log message 1');
|
||||
expect(reportInteraction).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -59,6 +59,7 @@ export interface Props {
|
||||
logs: LogRowModel[];
|
||||
logsMeta?: LogsMetaItem[];
|
||||
logSupportsContext?: (row: LogRowModel) => boolean;
|
||||
noInteractions?: boolean;
|
||||
onClickFilterLabel?: (key: string, value: string, frame?: DataFrame) => void;
|
||||
onClickFilterOutLabel?: (key: string, value: string, frame?: DataFrame) => void;
|
||||
onClickFilterString?: (value: string, refId?: string) => void;
|
||||
@@ -124,6 +125,7 @@ export const LogList = ({
|
||||
logs,
|
||||
logsMeta,
|
||||
logSupportsContext,
|
||||
noInteractions,
|
||||
onClickFilterLabel,
|
||||
onClickFilterOutLabel,
|
||||
onClickFilterString,
|
||||
@@ -165,6 +167,7 @@ export const LogList = ({
|
||||
logLineMenuCustomItems={logLineMenuCustomItems}
|
||||
logOptionsStorageKey={logOptionsStorageKey}
|
||||
logSupportsContext={logSupportsContext}
|
||||
noInteractions={noInteractions}
|
||||
onClickFilterLabel={onClickFilterLabel}
|
||||
onClickFilterOutLabel={onClickFilterOutLabel}
|
||||
onClickFilterString={onClickFilterString}
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
shallowCompare,
|
||||
store,
|
||||
} from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { config, reportInteraction } from '@grafana/runtime';
|
||||
import { PopoverContent } from '@grafana/ui';
|
||||
|
||||
import { DownloadFormat, checkLogsError, checkLogsSampled, downloadLogs as download } from '../../utils';
|
||||
@@ -79,6 +79,7 @@ export const LogListContext = createContext<LogListContextData>({
|
||||
forceEscape: false,
|
||||
fontSize: 'default',
|
||||
hasUnescapedContent: false,
|
||||
noInteractions: false,
|
||||
setDedupStrategy: () => {},
|
||||
setDetailsMode: () => {},
|
||||
setDetailsWidth: () => {},
|
||||
@@ -153,6 +154,7 @@ export interface Props {
|
||||
logsMeta?: LogsMetaItem[];
|
||||
logOptionsStorageKey?: string;
|
||||
logSupportsContext?: (row: LogRowModel) => boolean;
|
||||
noInteractions?: boolean;
|
||||
onClickFilterLabel?: (key: string, value: string, frame?: DataFrame) => void;
|
||||
onClickFilterOutLabel?: (key: string, value: string, frame?: DataFrame) => void;
|
||||
onClickFilterString?: (value: string, refId?: string) => void;
|
||||
@@ -195,6 +197,7 @@ export const LogListContextProvider = ({
|
||||
logsMeta,
|
||||
logOptionsStorageKey,
|
||||
logSupportsContext,
|
||||
noInteractions,
|
||||
onClickFilterLabel,
|
||||
onClickFilterOutLabel,
|
||||
onClickFilterString,
|
||||
@@ -237,6 +240,25 @@ export const LogListContextProvider = ({
|
||||
const [detailsWidth, setDetailsWidthState] = useState(getDetailsWidth(containerElement, logOptionsStorageKey));
|
||||
const [detailsMode, setDetailsMode] = useState<LogLineDetailsMode>(detailsModeProp ?? 'sidebar');
|
||||
|
||||
useEffect(() => {
|
||||
if (noInteractions) {
|
||||
return;
|
||||
}
|
||||
reportInteractionOnce(`logs_log_list_${app}_logs_displayed`, {
|
||||
dedupStrategy,
|
||||
fontSize,
|
||||
forceEscape: logListState.forceEscape,
|
||||
showTime,
|
||||
syntaxHighlighting,
|
||||
wrapLogMessage,
|
||||
detailsWidth,
|
||||
detailsMode,
|
||||
withDisplayedFields: displayedFields.length > 0,
|
||||
});
|
||||
// Just once
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (displayedFields.length > 0 || !config.featureToggles.otelLogsFormatting || !setDisplayedFields) {
|
||||
return;
|
||||
@@ -510,6 +532,7 @@ export const LogListContextProvider = ({
|
||||
logSupportsContext,
|
||||
logLineMenuCustomItems,
|
||||
logOptionsStorageKey,
|
||||
noInteractions: noInteractions ?? false,
|
||||
onClickFilterLabel,
|
||||
onClickFilterOutLabel,
|
||||
onClickFilterString,
|
||||
@@ -605,3 +628,12 @@ export function getDetailsScrollPosition(log: LogListModel) {
|
||||
export function removeDetailsScrollPosition(log: LogListModel) {
|
||||
detailsScrollMap.delete(log.uid);
|
||||
}
|
||||
|
||||
const reportInteractionOnce = (interactionName: string, properties?: Record<string, unknown>) => {
|
||||
const key = `logs.log-list-context.events.${interactionName}`;
|
||||
if (sessionStorage.getItem(key)) {
|
||||
return;
|
||||
}
|
||||
sessionStorage.setItem(key, '1');
|
||||
reportInteraction(interactionName, properties);
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ import { VariableSizeList } from 'react-window';
|
||||
|
||||
import { escapeRegex, GrafanaTheme2, shallowCompare } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { IconButton, Input, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { useLogListContext } from './LogListContext';
|
||||
@@ -26,10 +27,11 @@ export const LogListSearch = ({ listRef, logs }: Props) => {
|
||||
searchVisible,
|
||||
toggleFilterLogs,
|
||||
} = useLogListSearchContext();
|
||||
const { displayedFields } = useLogListContext();
|
||||
const { displayedFields, noInteractions } = useLogListContext();
|
||||
const [search, setSearch] = useState('');
|
||||
const [currentResult, setCurrentResult] = useState<number | null>(null);
|
||||
const inputRef = useRef('');
|
||||
const searchUsedRef = useRef(false);
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const matches = useMemo(() => {
|
||||
@@ -39,12 +41,19 @@ export const LogListSearch = ({ listRef, logs }: Props) => {
|
||||
return findMatchingLogs(logs, search, displayedFields);
|
||||
}, [displayedFields, logs, search, searchVisible]);
|
||||
|
||||
const handleChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
inputRef.current = e.target.value;
|
||||
startTransition(() => {
|
||||
setSearch(inputRef.current);
|
||||
});
|
||||
}, []);
|
||||
const handleChange = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
inputRef.current = e.target.value;
|
||||
startTransition(() => {
|
||||
setSearch(inputRef.current);
|
||||
});
|
||||
if (!searchUsedRef.current && !noInteractions) {
|
||||
reportInteraction('logs_log_list_search_used');
|
||||
searchUsedRef.current = true;
|
||||
}
|
||||
},
|
||||
[noInteractions]
|
||||
);
|
||||
|
||||
const prevResult = useCallback(() => {
|
||||
if (currentResult === null) {
|
||||
|
||||
@@ -164,6 +164,7 @@ export const LogsPanel = ({
|
||||
fontSize,
|
||||
syntaxHighlighting,
|
||||
detailsMode: detailsModeProp,
|
||||
noInteractions,
|
||||
...options
|
||||
},
|
||||
id,
|
||||
@@ -566,6 +567,7 @@ export const LogsPanel = ({
|
||||
logs={deduplicatedRows}
|
||||
logSupportsContext={showContextToggle}
|
||||
loadMore={enableInfiniteScrolling ? loadMoreLogs : undefined}
|
||||
noInteractions={noInteractions}
|
||||
onClickFilterLabel={
|
||||
isOnClickFilterLabel(onClickFilterLabel) ? onClickFilterLabel : defaultOnClickFilterLabel
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ composableKinds: PanelCfg: {
|
||||
sortOrder: common.LogsSortOrder
|
||||
dedupStrategy: common.LogsDedupStrategy
|
||||
enableInfiniteScrolling?: bool
|
||||
noInteractions?: bool
|
||||
fontSize?: "default" | "small" @cuetsy(kind="enum", memberNames="default|small")
|
||||
detailsMode?: "inline" | "sidebar" @cuetsy(kind="enum", memberNames="inline|sidebar")
|
||||
// TODO: figure out how to define callbacks
|
||||
|
||||
@@ -22,6 +22,7 @@ export interface Options {
|
||||
logLineMenuCustomItems?: unknown;
|
||||
logRowMenuIconsAfter?: unknown;
|
||||
logRowMenuIconsBefore?: unknown;
|
||||
noInteractions?: boolean;
|
||||
/**
|
||||
* TODO: figure out how to define callbacks
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user