diff --git a/public/app/features/explore/Logs/Logs.tsx b/public/app/features/explore/Logs/Logs.tsx index d79e51dd445..0701b166179 100644 --- a/public/app/features/explore/Logs/Logs.tsx +++ b/public/app/features/explore/Logs/Logs.tsx @@ -82,8 +82,8 @@ interface Props extends Themeable2 { loadLogsVolumeData: () => void; showContextToggle?: (row?: LogRowModel) => boolean; onChangeTime: (range: AbsoluteTimeRange) => void; - onClickFilterLabel: (key: string, value: string) => void; - onClickFilterOutLabel: (key: string, value: string) => void; + onClickFilterLabel?: (key: string, value: string) => void; + onClickFilterOutLabel?: (key: string, value: string) => void; onStartScanning?: () => void; onStopScanning?: () => void; getRowContext?: (row: LogRowModel, origRow: LogRowModel, options: LogRowContextOptions) => Promise; @@ -95,7 +95,7 @@ interface Props extends Themeable2 { eventBus: EventBus; panelState?: ExplorePanelsState; scrollElement?: HTMLDivElement; - isFilterLabelActive: (key: string, value: string) => Promise; + isFilterLabelActive?: (key: string, value: string) => Promise; logsFrames?: DataFrame[]; range: TimeRange; } diff --git a/public/app/features/explore/Logs/LogsContainer.tsx b/public/app/features/explore/Logs/LogsContainer.tsx index e964ffed1cc..31a95a4ef1b 100644 --- a/public/app/features/explore/Logs/LogsContainer.tsx +++ b/public/app/features/explore/Logs/LogsContainer.tsx @@ -17,7 +17,9 @@ import { LogRowContextOptions, DataSourceWithLogsContextSupport, DataSourceApi, + hasToggleableQueryFiltersSupport, } from '@grafana/data'; +import { getDataSourceSrv } from '@grafana/runtime'; import { DataQuery } from '@grafana/schema'; import { Collapse } from '@grafana/ui'; import { StoreState } from 'app/types'; @@ -55,7 +57,50 @@ interface LogsContainerProps extends PropsFromRedux { isFilterLabelActive: (key: string, value: string) => Promise; } -class LogsContainer extends PureComponent { +interface LogsContainerState { + logDetailsFilterAvailable: boolean; +} + +class LogsContainer extends PureComponent { + state: LogsContainerState = { + logDetailsFilterAvailable: false, + }; + + componentDidMount() { + this.checkFiltersAvailability(); + } + + componentDidUpdate(prevProps: LogsContainerProps) { + this.checkFiltersAvailability(); + } + + private checkFiltersAvailability() { + const { logsQueries, datasourceInstance } = this.props; + + if (!logsQueries) { + return; + } + + if (datasourceInstance?.modifyQuery || hasToggleableQueryFiltersSupport(datasourceInstance)) { + this.setState({ logDetailsFilterAvailable: true }); + return; + } + + const promises = []; + for (const query of logsQueries) { + if (query.datasource) { + promises.push(getDataSourceSrv().get(query.datasource)); + } + } + + Promise.all(promises).then((dataSources) => { + const logDetailsFilterAvailable = dataSources.some( + (ds) => ds.modifyQuery || hasToggleableQueryFiltersSupport(ds) + ); + this.setState({ logDetailsFilterAvailable }); + }); + } + onChangeTime = (absoluteRange: AbsoluteTimeRange) => { const { exploreId, updateTimeRange } = this.props; updateTimeRange({ exploreId, absoluteRange }); @@ -153,6 +198,7 @@ class LogsContainer extends PureComponent { logsVolume, scrollElement, } = this.props; + const { logDetailsFilterAvailable } = this.state; if (!logRows) { return null; @@ -197,8 +243,8 @@ class LogsContainer extends PureComponent { loadingState={loadingState} loadLogsVolumeData={() => loadSupplementaryQueryData(exploreId, SupplementaryQueryType.LogsVolume)} onChangeTime={this.onChangeTime} - onClickFilterLabel={onClickFilterLabel} - onClickFilterOutLabel={onClickFilterOutLabel} + onClickFilterLabel={logDetailsFilterAvailable ? onClickFilterLabel : undefined} + onClickFilterOutLabel={logDetailsFilterAvailable ? onClickFilterOutLabel : undefined} onStartScanning={onStartScanning} onStopScanning={onStopScanning} absoluteRange={absoluteRange} @@ -217,7 +263,7 @@ class LogsContainer extends PureComponent { panelState={this.props.panelState} logsFrames={this.props.logsFrames} scrollElement={scrollElement} - isFilterLabelActive={this.props.isFilterLabelActive} + isFilterLabelActive={logDetailsFilterAvailable ? this.props.isFilterLabelActive : undefined} range={range} /> diff --git a/public/app/features/logs/components/LogDetails.test.tsx b/public/app/features/logs/components/LogDetails.test.tsx index b22af38a95c..a22da725b6c 100644 --- a/public/app/features/logs/components/LogDetails.test.tsx +++ b/public/app/features/logs/components/LogDetails.test.tsx @@ -47,6 +47,28 @@ describe('LogDetails', () => { expect(screen.getByRole('cell', { name: 'key2' })).toBeInTheDocument(); expect(screen.getByRole('cell', { name: 'label2' })).toBeInTheDocument(); }); + it('should render filter controls when the callbacks are provided', () => { + setup( + { + onClickFilterLabel: () => {}, + onClickFilterOutLabel: () => {}, + }, + { labels: { key1: 'label1' } } + ); + expect(screen.getByLabelText('Filter for value')).toBeInTheDocument(); + expect(screen.getByLabelText('Filter out value')).toBeInTheDocument(); + }); + it('should not render filter controls when the callbacks are not provided', () => { + setup( + { + onClickFilterLabel: undefined, + onClickFilterOutLabel: undefined, + }, + { labels: { key1: 'label1' } } + ); + expect(screen.queryByLabelText('Filter for value')).not.toBeInTheDocument(); + expect(screen.queryByLabelText('Filter out value')).not.toBeInTheDocument(); + }); }); describe('when log row has error', () => { it('should not render log level border', () => {