Skip to content

Commit

Permalink
[ML] Migrate to React BrowserRouter and Kibana provided History. (#71941
Browse files Browse the repository at this point in the history
)

- Migrate to React BrowserRouter and Kibana provided History including a fallback to redirect legacy hash based URLs.
- Migrate breadcrumbs away from hash based URLs.
- Make sure relative custom urls still work after migration.
  • Loading branch information
walterra authored Jul 31, 2020
1 parent 39cca0e commit 145f2ee
Show file tree
Hide file tree
Showing 49 changed files with 669 additions and 480 deletions.
10 changes: 8 additions & 2 deletions x-pack/plugins/ml/public/application/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export type MlDependencies = Omit<MlSetupDependencies, 'share'> & MlStartDepende
interface AppProps {
coreStart: CoreStart;
deps: MlDependencies;
appMountParams: AppMountParameters;
}

const localStorage = new Storage(window.localStorage);
Expand All @@ -46,8 +47,9 @@ export interface MlServicesContext {

export type MlGlobalServices = ReturnType<typeof getMlGlobalServices>;

const App: FC<AppProps> = ({ coreStart, deps }) => {
const App: FC<AppProps> = ({ coreStart, deps, appMountParams }) => {
const pageDeps = {
history: appMountParams.history,
indexPatterns: deps.data.indexPatterns,
config: coreStart.uiSettings!,
setBreadcrumbs: coreStart.chrome!.setBreadcrumbs,
Expand Down Expand Up @@ -104,7 +106,11 @@ export const renderApp = (
appMountParams.onAppLeave((actions) => actions.default());

const mlLicense = setLicenseCache(deps.licensing, [
() => ReactDOM.render(<App coreStart={coreStart} deps={deps} />, appMountParams.element),
() =>
ReactDOM.render(
<App coreStart={coreStart} deps={deps} appMountParams={appMountParams} />,
appMountParams.element
),
]);

return () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ class LinksMenuUI extends Component {
const timestamp = record.timestamp;
const configuredUrlValue = customUrl.url_value;
const timeRangeInterval = parseInterval(customUrl.time_range);
const basePath = this.props.kibana.services.http.basePath.get();

if (configuredUrlValue.includes('$earliest$')) {
let earliestMoment = moment(timestamp);
if (timeRangeInterval !== null) {
Expand Down Expand Up @@ -117,7 +119,7 @@ class LinksMenuUI extends Component {
// Replace any tokens in the configured url_value with values from the source record,
// and then open link in a new tab/window.
const urlPath = replaceStringTokens(customUrl.url_value, record, true);
openCustomUrlWindow(urlPath, customUrl);
openCustomUrlWindow(urlPath, customUrl, basePath);
})
.catch((resp) => {
console.log('openCustomUrl(): error loading categoryDefinition:', resp);
Expand All @@ -136,7 +138,7 @@ class LinksMenuUI extends Component {
// Replace any tokens in the configured url_value with values from the source record,
// and then open link in a new tab/window.
const urlPath = getUrlForRecord(customUrl, record);
openCustomUrlWindow(urlPath, customUrl);
openCustomUrlWindow(urlPath, customUrl, basePath);
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

export { useMlKibana, StartServices, MlKibanaReactContextValue } from './kibana_context';
export { useNavigateToPath, NavigateToPath } from './use_navigate_to_path';
export { useUiSettings } from './use_ui_settings_context';
export { useTimefilter } from './use_timefilter';
export { useNotifications } from './use_notifications_context';
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { useMemo } from 'react';
import { useLocation } from 'react-router-dom';
import { PLUGIN_ID } from '../../../../common/constants/app';

import { useMlKibana } from './kibana_context';

export type NavigateToPath = ReturnType<typeof useNavigateToPath>;

export const useNavigateToPath = () => {
const {
services: {
application: { getUrlForApp, navigateToUrl },
},
} = useMlKibana();

const location = useLocation();

return useMemo(
() => (path: string | undefined, preserveSearch = false) => {
navigateToUrl(
getUrlForApp(PLUGIN_ID, {
path: `${path}${preserveSearch === true ? location.search : ''}`,
})
);
},
[location]
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,13 @@
import React, { FC, Fragment } from 'react';
import { EuiCard, EuiHorizontalRule, EuiIcon } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useMlKibana } from '../../../../../contexts/kibana';
import { useNavigateToPath } from '../../../../../contexts/kibana';

export const BackToListPanel: FC = () => {
const {
services: {
application: { navigateToUrl },
},
} = useMlKibana();
const navigateToPath = useNavigateToPath();

const redirectToAnalyticsManagementPage = async () => {
await navigateToUrl('#/data_frame_analytics?');
await navigateToPath('/data_frame_analytics');
};

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { DeepReadonly } from '../../../../../../../common/types/common';
import { DataFrameAnalyticsConfig, isOutlierAnalysis } from '../../../../common';
import { isClassificationAnalysis, isRegressionAnalysis } from '../../../../common/analytics';
import { DEFAULT_RESULTS_FIELD } from '../../../../common/constants';
import { useMlKibana } from '../../../../../contexts/kibana';
import { useMlKibana, useNavigateToPath } from '../../../../../contexts/kibana';
import {
CreateAnalyticsFormProps,
DEFAULT_NUM_TOP_FEATURE_IMPORTANCE_VALUES,
Expand Down Expand Up @@ -350,11 +350,11 @@ export function getCloneAction(createAnalyticsForm: CreateAnalyticsFormProps) {
export const useNavigateToWizardWithClonedJob = () => {
const {
services: {
application: { navigateToUrl },
notifications: { toasts },
savedObjects,
},
} = useMlKibana();
const navigateToPath = useNavigateToPath();

const savedObjectsClient = savedObjects.client;

Expand Down Expand Up @@ -395,8 +395,8 @@ export const useNavigateToWizardWithClonedJob = () => {
}

if (sourceIndexId) {
await navigateToUrl(
`ml#/data_frame_analytics/new_job?index=${encodeURIComponent(sourceIndexId)}&jobId=${
await navigateToPath(
`/data_frame_analytics/new_job?index=${encodeURIComponent(sourceIndexId)}&jobId=${
item.config.id
}`
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,9 @@ import { DataFrameAnalyticsListRow } from '../analytics_list/common';

import { ViewButton } from './view_button';

export const getViewAction = (
isManagementTable: boolean = false
): EuiTableActionsColumnType<DataFrameAnalyticsListRow>['actions'][number] => ({
export const getViewAction = (): EuiTableActionsColumnType<
DataFrameAnalyticsListRow
>['actions'][number] => ({
isPrimary: true,
render: (item: DataFrameAnalyticsListRow) => (
<ViewButton item={item} isManagementTable={isManagementTable} />
),
render: (item: DataFrameAnalyticsListRow) => <ViewButton item={item} />,
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,23 @@ import { i18n } from '@kbn/i18n';
import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui';

import { getAnalysisType } from '../../../../common/analytics';
import { useMlKibana } from '../../../../../contexts/kibana';
import { useNavigateToPath } from '../../../../../contexts/kibana';

import { getResultsUrl, DataFrameAnalyticsListRow } from '../analytics_list/common';

import { getViewLinkStatus } from './get_view_link_status';

interface ViewButtonProps {
item: DataFrameAnalyticsListRow;
isManagementTable: boolean;
}

export const ViewButton: FC<ViewButtonProps> = ({ item, isManagementTable }) => {
const {
services: {
application: { navigateToUrl, navigateToApp },
},
} = useMlKibana();
export const ViewButton: FC<ViewButtonProps> = ({ item }) => {
const navigateToPath = useNavigateToPath();

const { disabled, tooltipContent } = getViewLinkStatus(item);
const analysisType = getAnalysisType(item.config.analysis);

const url = getResultsUrl(item.id, analysisType);
const navigator = isManagementTable
? () => navigateToApp('ml', { path: url })
: () => navigateToUrl(url);
const onClickHandler = () => navigateToPath(getResultsUrl(item.id, analysisType));

const buttonText = i18n.translate('xpack.ml.dataframe.analyticsList.viewActionName', {
defaultMessage: 'View',
Expand All @@ -47,7 +39,7 @@ export const ViewButton: FC<ViewButtonProps> = ({ item, isManagementTable }) =>
flush="left"
iconType="visTable"
isDisabled={disabled}
onClick={navigator}
onClick={onClickHandler}
size="xs"
>
{buttonText}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const useActions = (
let modals: JSX.Element | null = null;

const actions: EuiTableActionsColumnType<DataFrameAnalyticsListRow>['actions'] = [
getViewAction(isManagementTable),
getViewAction(),
];

// isManagementTable will be the same for the lifecycle of the component
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
} from '@elastic/eui';

import { SavedObjectFinderUi } from '../../../../../../../../../../src/plugins/saved_objects/public';
import { useMlKibana } from '../../../../../contexts/kibana';
import { useMlKibana, useNavigateToPath } from '../../../../../contexts/kibana';

const fixedPageSize: number = 8;

Expand All @@ -27,16 +27,13 @@ interface Props {

export const SourceSelection: FC<Props> = ({ onClose }) => {
const {
services: {
application: { navigateToUrl },
savedObjects,
uiSettings,
},
services: { savedObjects, uiSettings },
} = useMlKibana();
const navigateToPath = useNavigateToPath();

const onSearchSelected = async (id: string, type: string) => {
await navigateToUrl(
`ml#/data_frame_analytics/new_job?${
await navigateToPath(
`/data_frame_analytics/new_job?${
type === 'index-pattern' ? 'index' : 'savedSearchId'
}=${encodeURIComponent(id)}`
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n';

import { FormattedMessage } from '@kbn/i18n/react';
import { isFullLicense } from '../license';
import { useTimefilter, useMlKibana } from '../contexts/kibana';
import { useTimefilter, useMlKibana, useNavigateToPath } from '../contexts/kibana';

import { NavigationMenu } from '../components/navigation_menu';
import { getMaxBytesFormatted } from './file_based/components/utils';
Expand Down Expand Up @@ -54,6 +54,7 @@ export const DatavisualizerSelector: FC = () => {
const {
services: { licenseManagement },
} = useMlKibana();
const navigateToPath = useNavigateToPath();

const startTrialVisible =
licenseManagement !== undefined &&
Expand Down Expand Up @@ -124,7 +125,7 @@ export const DatavisualizerSelector: FC = () => {
footer={
<EuiButton
target="_self"
href="#/filedatavisualizer"
onClick={() => navigateToPath('/filedatavisualizer')}
data-test-subj="mlDataVisualizerUploadFileButton"
>
<FormattedMessage
Expand Down Expand Up @@ -154,7 +155,7 @@ export const DatavisualizerSelector: FC = () => {
footer={
<EuiButton
target="_self"
href="#/datavisualizer_index_select"
onClick={() => navigateToPath('/datavisualizer_index_select')}
data-test-subj="mlDataVisualizerSelectIndexButton"
>
<FormattedMessage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export interface CustomUrlListProps {
*/
export const CustomUrlList: FC<CustomUrlListProps> = ({ job, customUrls, setCustomUrls }) => {
const {
services: { notifications },
services: { http, notifications },
} = useMlKibana();
const [expandedUrlIndex, setExpandedUrlIndex] = useState<number | null>(null);

Expand Down Expand Up @@ -103,7 +103,7 @@ export const CustomUrlList: FC<CustomUrlListProps> = ({ job, customUrls, setCust
if (index < customUrls.length) {
getTestUrl(job, customUrls[index])
.then((testUrl) => {
openCustomUrlWindow(testUrl, customUrls[index]);
openCustomUrlWindow(testUrl, customUrls[index], http.basePath.get());
})
.catch((resp) => {
// eslint-disable-next-line no-console
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,16 @@ class CustomUrlsUI extends Component<CustomUrlsProps, CustomUrlsState> {
};

onTestButtonClick = () => {
const { toasts } = this.props.kibana.services.notifications;
const {
http: { basePath },
notifications: { toasts },
} = this.props.kibana.services;
const job = this.props.job;
buildCustomUrlFromSettings(this.state.editorSettings as CustomUrlSettings)
.then((customUrl) => {
getTestUrl(job, customUrl)
.then((testUrl) => {
openCustomUrlWindow(testUrl, customUrl);
openCustomUrlWindow(testUrl, customUrl, basePath.get());
})
.catch((resp) => {
// eslint-disable-next-line no-console
Expand Down
Loading

0 comments on commit 145f2ee

Please sign in to comment.