diff --git a/x-pack/plugins/security_solution/public/common/hooks/esql/use_esql_availability.ts b/x-pack/plugins/security_solution/public/common/hooks/esql/use_esql_availability.ts
index 41fc7084b32bf..3e9bdb658c3e1 100644
--- a/x-pack/plugins/security_solution/public/common/hooks/esql/use_esql_availability.ts
+++ b/x-pack/plugins/security_solution/public/common/hooks/esql/use_esql_availability.ts
@@ -18,17 +18,19 @@ import { useIsExperimentalFeatureEnabled } from '../use_experimental_features';
export const useEsqlAvailability = () => {
const { uiSettings } = useKibana().services;
const isEsqlAdvancedSettingEnabled = uiSettings?.get(ENABLE_ESQL);
+
+ const isTimelineEsqlFeatureFlagDisabled =
+ useIsExperimentalFeatureEnabled('timelineEsqlTabDisabled');
+
const isEsqlRuleTypeEnabled =
!useIsExperimentalFeatureEnabled('esqlRulesDisabled') && isEsqlAdvancedSettingEnabled;
- const isESQLTabInTimelineEnabled =
- !useIsExperimentalFeatureEnabled('timelineEsqlTabDisabled') && isEsqlAdvancedSettingEnabled;
return useMemo(
() => ({
isEsqlAdvancedSettingEnabled,
isEsqlRuleTypeEnabled,
- isESQLTabInTimelineEnabled,
+ isTimelineEsqlEnabledByFeatureFlag: !isTimelineEsqlFeatureFlagDisabled,
}),
- [isESQLTabInTimelineEnabled, isEsqlAdvancedSettingEnabled, isEsqlRuleTypeEnabled]
+ [isEsqlAdvancedSettingEnabled, isTimelineEsqlFeatureFlagDisabled, isEsqlRuleTypeEnabled]
);
};
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/index.test.tsx
index 7f399aa095a8a..4d0a8f5cfd363 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/index.test.tsx
@@ -18,7 +18,8 @@ import { render, screen, waitFor } from '@testing-library/react';
jest.mock('../../../../common/hooks/esql/use_esql_availability', () => ({
useEsqlAvailability: jest.fn().mockReturnValue({
- isESQLTabInTimelineEnabled: true,
+ isEsqlAdvancedSettingEnabled: true,
+ isTimelineEsqlEnabledByFeatureFlag: true,
}),
}));
@@ -44,38 +45,79 @@ describe('Timeline', () => {
expect(screen.getByTestId(esqlTabSubj)).toBeVisible();
});
- it('should not show the esql tab when the advanced setting is disabled', async () => {
- useEsqlAvailabilityMock.mockReturnValue({
- isESQLTabInTimelineEnabled: false,
+ describe('no existing esql query is present', () => {
+ it('should not show the esql tab when the advanced setting is disabled', async () => {
+ useEsqlAvailabilityMock.mockReturnValue({
+ isEsqlAdvancedSettingEnabled: false,
+ isTimelineEsqlEnabledByFeatureFlag: true,
+ });
+ render(
+
+
+
+ );
+
+ await waitFor(() => {
+ expect(screen.queryByTestId(esqlTabSubj)).toBeNull();
+ });
});
- render(
-
-
-
- );
+ it('should not show the esql tab when the esql is disabled by feature flag', async () => {
+ useEsqlAvailabilityMock.mockReturnValue({
+ isEsqlAdvancedSettingEnabled: false,
+ isTimelineEsqlEnabledByFeatureFlag: false,
+ });
+ render(
+
+
+
+ );
- await waitFor(() => {
- expect(screen.queryByTestId(esqlTabSubj)).toBeNull();
+ await waitFor(() => {
+ expect(screen.queryByTestId(esqlTabSubj)).toBeNull();
+ });
});
});
- it('should show the esql tab when the advanced setting is disabled, but an esql query is present', async () => {
- useEsqlAvailabilityMock.mockReturnValue({
- isESQLTabInTimelineEnabled: false,
+ describe('existing esql query is present', () => {
+ let mockStore: ReturnType;
+ beforeEach(() => {
+ const stateWithSavedSearchId = structuredClone(mockGlobalState);
+ stateWithSavedSearchId.timeline.timelineById[TimelineId.test].savedSearchId = 'test-id';
+ mockStore = createMockStore(stateWithSavedSearchId);
});
- const stateWithSavedSearchId = structuredClone(mockGlobalState);
- stateWithSavedSearchId.timeline.timelineById[TimelineId.test].savedSearchId = 'test-id';
- const mockStore = createMockStore(stateWithSavedSearchId);
+ it('should show the esql tab when the advanced setting is disabled', async () => {
+ useEsqlAvailabilityMock.mockReturnValue({
+ isESQLTabInTimelineEnabled: false,
+ isTimelineEsqlEnabledByFeatureFlag: true,
+ });
- render(
-
-
-
- );
+ render(
+
+
+
+ );
+
+ await waitFor(() => {
+ expect(screen.queryByTestId(esqlTabSubj)).toBeVisible();
+ });
+ });
+
+ it('should not show the esql tab when the esql is disabled by the feature flag', async () => {
+ useEsqlAvailabilityMock.mockReturnValue({
+ isESQLTabInTimelineEnabled: true,
+ isTimelineEsqlEnabledByFeatureFlag: false,
+ });
+
+ render(
+
+
+
+ );
- await waitFor(() => {
- expect(screen.queryByTestId(esqlTabSubj)).toBeVisible();
+ await waitFor(() => {
+ expect(screen.queryByTestId(esqlTabSubj)).toBeNull();
+ });
});
});
});
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/index.tsx
index 2e164677735dd..643a5b54be415 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/index.tsx
@@ -110,11 +110,20 @@ const ActiveTimelineTab = memo(
showTimeline,
}) => {
const { hasAssistantPrivilege } = useAssistantAvailability();
- const { isESQLTabInTimelineEnabled } = useEsqlAvailability();
+ const { isTimelineEsqlEnabledByFeatureFlag, isEsqlAdvancedSettingEnabled } =
+ useEsqlAvailability();
const timelineESQLSavedSearch = useShallowEqualSelector((state) =>
selectTimelineESQLSavedSearchId(state, timelineId)
);
- const shouldShowESQLTab = isESQLTabInTimelineEnabled || timelineESQLSavedSearch != null;
+ const shouldShowESQLTab = useMemo(() => {
+ // disabling esql feature from feature flag should unequivocally hide the tab
+ // irrespective of the fact that the advanced setting is enabled or
+ // not or existing esql query is present or not
+ if (!isTimelineEsqlEnabledByFeatureFlag) {
+ return false;
+ }
+ return isEsqlAdvancedSettingEnabled || timelineESQLSavedSearch != null;
+ }, [isEsqlAdvancedSettingEnabled, isTimelineEsqlEnabledByFeatureFlag, timelineESQLSavedSearch]);
const aiAssistantFlyoutMode = useIsExperimentalFeatureEnabled('aiAssistantFlyoutMode');
const getTab = useCallback(
(tab: TimelineTabs) => {
@@ -271,14 +280,24 @@ const TabsContentComponent: React.FC = ({
const getAppNotes = useMemo(() => getNotesSelector(), []);
const getTimelineNoteIds = useMemo(() => getNoteIdsSelector(), []);
const getTimelinePinnedEventNotes = useMemo(() => getEventIdToNoteIdsSelector(), []);
- const { isESQLTabInTimelineEnabled } = useEsqlAvailability();
+ const { isEsqlAdvancedSettingEnabled, isTimelineEsqlEnabledByFeatureFlag } =
+ useEsqlAvailability();
+
const timelineESQLSavedSearch = useShallowEqualSelector((state) =>
selectTimelineESQLSavedSearchId(state, timelineId)
);
const activeTab = useShallowEqualSelector((state) => getActiveTab(state, timelineId));
const showTimeline = useShallowEqualSelector((state) => getShowTimeline(state, timelineId));
- const shouldShowESQLTab = isESQLTabInTimelineEnabled || timelineESQLSavedSearch != null;
+ const shouldShowESQLTab = useMemo(() => {
+ // disabling esql feature from feature flag should unequivocally hide the tab
+ // irrespective of the fact that the advanced setting is enabled or
+ // not or existing esql query is present or not
+ if (!isTimelineEsqlEnabledByFeatureFlag) {
+ return false;
+ }
+ return isEsqlAdvancedSettingEnabled || timelineESQLSavedSearch != null;
+ }, [isEsqlAdvancedSettingEnabled, isTimelineEsqlEnabledByFeatureFlag, timelineESQLSavedSearch]);
const numberOfPinnedEvents = useShallowEqualSelector((state) =>
getNumberOfPinnedEvents(state, timelineId)