Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remember tab choice between logs explorer and discover #194930

Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
3e51359
Add app to remember last used discover vs. observability-logs-explore…
awahab07 Oct 4, 2024
62e8a98
Merge remote-tracking branch 'origin/main' into 193321-Remember-tab-c…
awahab07 Oct 4, 2024
ed7bf0e
Improve comments.
awahab07 Oct 4, 2024
9bf3db2
[CI] Auto-commit changed files from 'node scripts/yarn_deduplicate'
kibanamachine Oct 4, 2024
2740f79
Update functional test.
awahab07 Oct 4, 2024
427efd7
Add `last-use-logs-viewer` app schema to usage collection.
awahab07 Oct 4, 2024
edfc3b9
Exclude `last-use-logs-viewer` from app usage tracking.
awahab07 Oct 4, 2024
de35094
Add `last-used-logs-viewer` app schema.
awahab07 Oct 4, 2024
0b967fb
Merge remote-tracking branch 'origin/main' into 193321-Remember-tab-c…
awahab07 Oct 4, 2024
8b6a774
Merge remote-tracking branch 'origin/main' into 193321-Remember-tab-c…
awahab07 Oct 7, 2024
04ab97f
Add `last-used-logs-viewer` redirect for solutions view on stateful.
awahab07 Oct 8, 2024
a9f2b50
Merge remote-tracking branch 'origin/main' into 193321-Remember-tab-c…
awahab07 Oct 8, 2024
545446d
PR feedback: simplify onclick callback.
awahab07 Oct 9, 2024
4a63964
Make sure Discover on sidenav is active when Discover or Logs Explore…
awahab07 Oct 9, 2024
bffb883
Merge remote-tracking branch 'origin/main' into 193321-Remember-tab-c…
awahab07 Oct 9, 2024
65671c9
Persist Discover/Observability Logs Explorer tab selection based on l…
awahab07 Oct 9, 2024
ec39158
Merge remote-tracking branch 'origin/main' into 193321-Remember-tab-c…
awahab07 Oct 9, 2024
f223dc6
Merge remote-tracking branch 'origin/main' into 193321-Remember-tab-c…
awahab07 Oct 14, 2024
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
3 changes: 3 additions & 0 deletions packages/deeplinks/observability/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ export const LOGS_APP_ID = 'logs';

export const OBSERVABILITY_LOGS_EXPLORER_APP_ID = 'observability-logs-explorer';

// TODO: Remove the app once context-aware switching between discover and observability logs explorer is implemented
export const LAST_USED_LOGS_VIEWER_APP_ID = 'last-used-logs-viewer';

export const OBSERVABILITY_OVERVIEW_APP_ID = 'observability-overview';

export const METRICS_APP_ID = 'metrics';
Expand Down
3 changes: 3 additions & 0 deletions packages/deeplinks/observability/deep_links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
LOGS_APP_ID,
METRICS_APP_ID,
OBSERVABILITY_LOGS_EXPLORER_APP_ID,
LAST_USED_LOGS_VIEWER_APP_ID,
OBSERVABILITY_ONBOARDING_APP_ID,
OBSERVABILITY_OVERVIEW_APP_ID,
SYNTHETICS_APP_ID,
Expand All @@ -24,6 +25,7 @@ import {

type LogsApp = typeof LOGS_APP_ID;
type ObservabilityLogsExplorerApp = typeof OBSERVABILITY_LOGS_EXPLORER_APP_ID;
type LastUsedLogsViewerApp = typeof LAST_USED_LOGS_VIEWER_APP_ID;
type ObservabilityOverviewApp = typeof OBSERVABILITY_OVERVIEW_APP_ID;
type MetricsApp = typeof METRICS_APP_ID;
type ApmApp = typeof APM_APP_ID;
Expand All @@ -38,6 +40,7 @@ type InventoryApp = typeof INVENTORY_APP_ID;
export type AppId =
| LogsApp
| ObservabilityLogsExplorerApp
| LastUsedLogsViewerApp
| ObservabilityOverviewApp
| ObservabilityOnboardingApp
| ApmApp
Expand Down
1 change: 1 addition & 0 deletions packages/deeplinks/observability/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
export {
LOGS_APP_ID,
OBSERVABILITY_LOGS_EXPLORER_APP_ID,
LAST_USED_LOGS_VIEWER_APP_ID,
OBSERVABILITY_ONBOARDING_APP_ID,
OBSERVABILITY_OVERVIEW_APP_ID,
AI_ASSISTANT_APP_ID,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,6 @@ export interface ObsLogsExplorerDataViewLocatorParams extends DatasetLocatorPara
*/
id: string;
}

// To store the last used logs viewer (either of discover or observability-logs-explorer)
export const OBS_LOGS_EXPLORER_LOGS_VIEWER_KEY = 'obs-logs-explorer:lastUsedViewer';
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,21 @@
import userEvent from '@testing-library/user-event';
import { render, screen } from '@testing-library/react';
import React from 'react';
import { DISCOVER_APP_ID } from '@kbn/deeplinks-analytics';
import {
ALL_DATASETS_LOCATOR_ID,
OBSERVABILITY_LOGS_EXPLORER_APP_ID,
} from '@kbn/deeplinks-observability';
import { discoverServiceMock } from '../../__mocks__/services';
import { LogsExplorerTabs, LogsExplorerTabsProps } from './logs_explorer_tabs';
import { DISCOVER_APP_LOCATOR } from '../../../common';
import { ALL_DATASETS_LOCATOR_ID } from '@kbn/deeplinks-observability';

const mockSetLastUsedViewer = jest.fn();
jest.mock('react-use/lib/useLocalStorage', () => {
return jest.fn((key: string, initialValue: string) => {
return [initialValue, mockSetLastUsedViewer];
});
});

const createMockLocator = (id: string) => ({
navigate: jest.fn(),
Expand Down Expand Up @@ -86,4 +97,16 @@ describe('LogsExplorerTabs', () => {
await userEvent.click(getDiscoverTab());
expect(mockDiscoverLocator.navigate).toHaveBeenCalledWith({});
});

it('should update the last used viewer in local storage', async () => {
renderTabs();

mockSetLastUsedViewer.mockClear();
await userEvent.click(getLogsExplorerTab());
expect(mockSetLastUsedViewer).toHaveBeenCalledWith(OBSERVABILITY_LOGS_EXPLORER_APP_ID);

mockSetLastUsedViewer.mockClear();
await userEvent.click(getDiscoverTab());
expect(mockSetLastUsedViewer).toHaveBeenCalledWith(DISCOVER_APP_ID);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,16 @@
*/

import { EuiTab, EuiTabs, useEuiTheme } from '@elastic/eui';
import { AllDatasetsLocatorParams, ALL_DATASETS_LOCATOR_ID } from '@kbn/deeplinks-observability';
import { DISCOVER_APP_ID } from '@kbn/deeplinks-analytics';
import {
AllDatasetsLocatorParams,
ALL_DATASETS_LOCATOR_ID,
OBS_LOGS_EXPLORER_LOGS_VIEWER_KEY,
OBSERVABILITY_LOGS_EXPLORER_APP_ID,
} from '@kbn/deeplinks-observability';
import { i18n } from '@kbn/i18n';
import React, { MouseEvent } from 'react';
import useLocalStorage from 'react-use/lib/useLocalStorage';
import { DiscoverAppLocatorParams, DISCOVER_APP_LOCATOR } from '../../../common';
import type { DiscoverServices } from '../../build_services';

Expand All @@ -29,17 +36,31 @@ export const LogsExplorerTabs = ({ services, selectedTab }: LogsExplorerTabsProp
const discoverUrl = discoverLocator?.getRedirectUrl(emptyParams);
const logsExplorerUrl = logsExplorerLocator?.getRedirectUrl(emptyParams);

const navigateToDiscover = createNavigateHandler(() => {
if (selectedTab !== 'discover') {
discoverLocator?.navigate(emptyParams);
const [_, setLastUsedViewer] = useLocalStorage<
typeof DISCOVER_APP_ID | typeof OBSERVABILITY_LOGS_EXPLORER_APP_ID
>(OBS_LOGS_EXPLORER_LOGS_VIEWER_KEY, OBSERVABILITY_LOGS_EXPLORER_APP_ID);

const navigateToDiscover = createNavigateHandler(
() => {
if (selectedTab !== 'discover') {
discoverLocator?.navigate(emptyParams);
}
},
() => {
setLastUsedViewer(DISCOVER_APP_ID);
}
awahab07 marked this conversation as resolved.
Show resolved Hide resolved
});
);

const navigateToLogsExplorer = createNavigateHandler(() => {
if (selectedTab !== 'logs-explorer') {
logsExplorerLocator?.navigate(emptyParams);
const navigateToLogsExplorer = createNavigateHandler(
() => {
if (selectedTab !== 'logs-explorer') {
logsExplorerLocator?.navigate(emptyParams);
}
},
() => {
setLastUsedViewer(OBSERVABILITY_LOGS_EXPLORER_APP_ID);
}
});
);

return (
<EuiTabs bottomBorder={false} data-test-subj="logsExplorerTabs">
Expand Down Expand Up @@ -77,11 +98,12 @@ const isModifiedEvent = (event: MouseEvent) =>

const isLeftClickEvent = (event: MouseEvent) => event.button === 0;

const createNavigateHandler = (onClick: () => void) => (e: MouseEvent) => {
const createNavigateHandler = (onClick: () => void, updateState: () => void) => (e: MouseEvent) => {
if (isModifiedEvent(e) || !isLeftClickEvent(e)) {
return;
}

e.preventDefault();
updateState(); // Update local storage
onClick();
};
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ export const applicationUsageSchema = {
monitoring: commonSchema,
'observability-log-explorer': commonSchema,
'observability-logs-explorer': commonSchema,
'last-used-logs-viewer': commonSchema,
'observability-overview': commonSchema,
observabilityOnboarding: commonSchema,
observabilityAIAssistant: commonSchema,
Expand Down
131 changes: 131 additions & 0 deletions src/plugins/telemetry/schema/oss_plugins.json
Original file line number Diff line number Diff line change
Expand Up @@ -5111,6 +5111,137 @@
}
}
},
"last-used-logs-viewer": {
"properties": {
"appId": {
"type": "keyword",
"_meta": {
"description": "The application being tracked"
}
},
"viewId": {
"type": "keyword",
"_meta": {
"description": "Always `main`"
}
},
"clicks_total": {
"type": "long",
"_meta": {
"description": "General number of clicks in the application since we started counting them"
}
},
"clicks_7_days": {
"type": "long",
"_meta": {
"description": "General number of clicks in the application over the last 7 days"
}
},
"clicks_30_days": {
"type": "long",
"_meta": {
"description": "General number of clicks in the application over the last 30 days"
}
},
"clicks_90_days": {
"type": "long",
"_meta": {
"description": "General number of clicks in the application over the last 90 days"
}
},
"minutes_on_screen_total": {
"type": "float",
"_meta": {
"description": "Minutes the application is active and on-screen since we started counting them."
}
},
"minutes_on_screen_7_days": {
"type": "float",
"_meta": {
"description": "Minutes the application is active and on-screen over the last 7 days"
}
},
"minutes_on_screen_30_days": {
"type": "float",
"_meta": {
"description": "Minutes the application is active and on-screen over the last 30 days"
}
},
"minutes_on_screen_90_days": {
"type": "float",
"_meta": {
"description": "Minutes the application is active and on-screen over the last 90 days"
}
},
"views": {
"type": "array",
"items": {
"properties": {
"appId": {
"type": "keyword",
"_meta": {
"description": "The application being tracked"
}
},
"viewId": {
"type": "keyword",
"_meta": {
"description": "The application view being tracked"
}
},
"clicks_total": {
"type": "long",
"_meta": {
"description": "General number of clicks in the application sub view since we started counting them"
}
},
"clicks_7_days": {
"type": "long",
"_meta": {
"description": "General number of clicks in the active application sub view over the last 7 days"
}
},
"clicks_30_days": {
"type": "long",
"_meta": {
"description": "General number of clicks in the active application sub view over the last 30 days"
}
},
"clicks_90_days": {
"type": "long",
"_meta": {
"description": "General number of clicks in the active application sub view over the last 90 days"
}
},
"minutes_on_screen_total": {
"type": "float",
"_meta": {
"description": "Minutes the application sub view is active and on-screen since we started counting them."
}
},
"minutes_on_screen_7_days": {
"type": "float",
"_meta": {
"description": "Minutes the application is active and on-screen active application sub view over the last 7 days"
}
},
"minutes_on_screen_30_days": {
"type": "float",
"_meta": {
"description": "Minutes the application is active and on-screen active application sub view over the last 30 days"
}
},
"minutes_on_screen_90_days": {
"type": "float",
"_meta": {
"description": "Minutes the application is active and on-screen active application sub view over the last 90 days"
}
}
}
}
}
}
},
"observability-overview": {
"properties": {
"appId": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React, { useEffect } from 'react';
import ReactDOM from 'react-dom';
import { useLocation } from 'react-router-dom';
import useLocalStorage from 'react-use/lib/useLocalStorage';
import { Router } from '@kbn/shared-ux-router';
import {
OBSERVABILITY_LOGS_EXPLORER_APP_ID,
OBS_LOGS_EXPLORER_LOGS_VIEWER_KEY,
} from '@kbn/deeplinks-observability';
import { DISCOVER_APP_ID } from '@kbn/deeplinks-analytics';
import { AppMountParameters, CoreStart } from '@kbn/core/public';

export const renderLastUsedLogsViewerRedirect = (
core: CoreStart,
appParams: AppMountParameters
) => {
ReactDOM.render(
<Router history={appParams.history}>
<LastUsedLogsViewerRedirect core={core} />
</Router>,
appParams.element
);

return () => {
ReactDOM.unmountComponentAtNode(appParams.element);
};
};

export const LastUsedLogsViewerRedirect = ({ core }: { core: CoreStart }) => {
const location = useLocation();
const path = `${location.pathname}${location.search}`;
const [lastUsedLogsViewApp] = useLocalStorage<
typeof DISCOVER_APP_ID | typeof OBSERVABILITY_LOGS_EXPLORER_APP_ID
>(OBS_LOGS_EXPLORER_LOGS_VIEWER_KEY, OBSERVABILITY_LOGS_EXPLORER_APP_ID);

if (
lastUsedLogsViewApp &&
lastUsedLogsViewApp !== DISCOVER_APP_ID &&
lastUsedLogsViewApp !== OBSERVABILITY_LOGS_EXPLORER_APP_ID
) {
throw new Error(
`Invalid last used logs viewer app: "${lastUsedLogsViewApp}". Allowed values are "${DISCOVER_APP_ID}" and "${OBSERVABILITY_LOGS_EXPLORER_APP_ID}"`
);
}

useEffect(() => {
if (lastUsedLogsViewApp === DISCOVER_APP_ID) {
core.application.navigateToApp(DISCOVER_APP_ID, { replace: true, path });
}

if (lastUsedLogsViewApp === OBSERVABILITY_LOGS_EXPLORER_APP_ID) {
core.application.navigateToApp(OBSERVABILITY_LOGS_EXPLORER_APP_ID, { replace: true, path });
}
}, [core, path, lastUsedLogsViewApp]);

return <></>;
};
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,22 @@ export class ObservabilityLogsExplorerPlugin
},
});

// App used solely to redirect to either "/app/observability-logs-explorer" or "/app/discover"
// based on the last used app value in localStorage
core.application.register({
id: 'last-used-logs-viewer',
title: logsExplorerAppTitle,
visibleIn: [],
mount: async (appMountParams: AppMountParameters) => {
const [coreStart] = await core.getStartServices();
const { renderLastUsedLogsViewerRedirect } = await import(
'./applications/last_used_logs_viewer'
);

return renderLastUsedLogsViewerRedirect(coreStart, appMountParams);
},
});

core.analytics.registerEventType(DATA_RECEIVED_TELEMETRY_EVENT);

// Register Locators
Expand Down
Loading