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

[Endpoint] Add Endpoint empty states for onboarding #69626

Merged
merged 25 commits into from
Jun 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f9982f1
add policy empty state to Host list, placeholder for add endpoint prompt
kevinlog Jun 19, 2020
a319fc1
add empty endpoint state
kevinlog Jun 22, 2020
d203642
fix types
kevinlog Jun 22, 2020
08afb5a
fix test, hide total when empty state
kevinlog Jun 22, 2020
899f0cf
Merge branch 'master' of github.com:kevinlog/kibana into task/host-li…
kevinlog Jun 22, 2020
4316454
fix types
kevinlog Jun 22, 2020
08bbc88
fix test
kevinlog Jun 22, 2020
6260e5e
add policy selection to onboarding
kevinlog Jun 23, 2020
1c55b23
begin unit tests
kevinlog Jun 23, 2020
f5e3999
additional tests
kevinlog Jun 24, 2020
eb4b259
add URL and roundtrip
kevinlog Jun 24, 2020
b9129eb
fix bug, add type
kevinlog Jun 24, 2020
a93a1fa
refactor middleware
kevinlog Jun 24, 2020
3206882
fix selection bug
kevinlog Jun 24, 2020
439e8e9
Merge branch 'master' of github.com:kevinlog/kibana into task/host-li…
kevinlog Jun 24, 2020
e683508
refactor route path check
kevinlog Jun 25, 2020
7f656f8
make loading smoother, fix types
kevinlog Jun 25, 2020
71690bf
Merge branch 'master' into task/host-list-empty-states
elasticmachine Jun 25, 2020
310b8ef
address comments, fix tests
kevinlog Jun 25, 2020
3529c1c
Merge branch 'master' of github.com:kevinlog/kibana into task/host-li…
kevinlog Jun 25, 2020
f364e74
address comments
kevinlog Jun 25, 2020
8399f0e
fix test
kevinlog Jun 25, 2020
18dd954
use URL, take out baseRoute
kevinlog Jun 26, 2020
9689d04
Merge branch 'master' of github.com:kevinlog/kibana into task/host-li…
kevinlog Jun 26, 2020
d425f46
Merge branch 'master' into task/host-list-empty-states
elasticmachine Jun 26, 2020
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
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ export const IntraAppStateProvider = memo<{
}>(({ kibanaScopedHistory, children }) => {
const internalAppToAppState = useMemo<IntraAppState>(() => {
return {
forRoute: kibanaScopedHistory.location.hash.substr(1),
forRoute: new URL(`${kibanaScopedHistory.location.hash.substr(1)}`, 'http://localhost')
.pathname,
routeState: kibanaScopedHistory.location.state as AnyIntraAppRouteState,
};
}, [kibanaScopedHistory.location.hash, kibanaScopedHistory.location.state]);
}, [kibanaScopedHistory.location.state, kibanaScopedHistory.location.hash]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spiderman_meme.jpg

return (
<IntraAppStateContext.Provider value={internalAppToAppState}>
{children}
Expand All @@ -57,6 +58,7 @@ export function useIntraAppState<S = AnyIntraAppRouteState>():
// once so that it does not impact navigation to the page from within the
// ingest app. side affect is that the browser back button would not work
// consistently either.

if (location.pathname === intraAppState.forRoute && !wasHandled.has(intraAppState)) {
wasHandled.add(intraAppState);
return intraAppState.routeState as S;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React, { memo, useState } from 'react';
import React, { memo, useState, useMemo } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiContextMenuItem, EuiPortal } from '@elastic/eui';
import { AgentConfig } from '../../../types';
Expand All @@ -17,86 +17,106 @@ export const AgentConfigActionMenu = memo<{
config: AgentConfig;
onCopySuccess?: (newAgentConfig: AgentConfig) => void;
fullButton?: boolean;
}>(({ config, onCopySuccess, fullButton = false }) => {
const hasWriteCapabilities = useCapabilities().write;
const [isYamlFlyoutOpen, setIsYamlFlyoutOpen] = useState<boolean>(false);
const [isEnrollmentFlyoutOpen, setIsEnrollmentFlyoutOpen] = useState<boolean>(false);
enrollmentFlyoutOpenByDefault?: boolean;
onCancelEnrollment?: () => void;
}>(
({
config,
onCopySuccess,
fullButton = false,
enrollmentFlyoutOpenByDefault = false,
onCancelEnrollment,
}) => {
const hasWriteCapabilities = useCapabilities().write;
const [isYamlFlyoutOpen, setIsYamlFlyoutOpen] = useState<boolean>(false);
const [isEnrollmentFlyoutOpen, setIsEnrollmentFlyoutOpen] = useState<boolean>(
enrollmentFlyoutOpenByDefault
);

return (
<AgentConfigCopyProvider>
{(copyAgentConfigPrompt) => {
return (
<>
{isYamlFlyoutOpen ? (
<EuiPortal>
<ConfigYamlFlyout configId={config.id} onClose={() => setIsYamlFlyoutOpen(false)} />
</EuiPortal>
) : null}
{isEnrollmentFlyoutOpen && (
<EuiPortal>
<AgentEnrollmentFlyout
agentConfigs={[config]}
onClose={() => setIsEnrollmentFlyoutOpen(false)}
/>
</EuiPortal>
)}
<ContextMenuActions
button={
fullButton
? {
props: {
iconType: 'arrowDown',
iconSide: 'right',
},
children: (
<FormattedMessage
id="xpack.ingestManager.agentConfigActionMenu.buttonText"
defaultMessage="Actions"
/>
),
}
: undefined
}
items={[
<EuiContextMenuItem
disabled={!hasWriteCapabilities}
icon="plusInCircle"
onClick={() => setIsEnrollmentFlyoutOpen(true)}
key="enrollAgents"
>
<FormattedMessage
id="xpack.ingestManager.agentConfigActionMenu.enrollAgentActionText"
defaultMessage="Enroll agent"
/>
</EuiContextMenuItem>,
<EuiContextMenuItem
icon="inspect"
onClick={() => setIsYamlFlyoutOpen(!isYamlFlyoutOpen)}
key="viewConfig"
>
<FormattedMessage
id="xpack.ingestManager.agentConfigActionMenu.viewConfigText"
defaultMessage="View config"
/>
</EuiContextMenuItem>,
<EuiContextMenuItem
disabled={!hasWriteCapabilities}
icon="copy"
onClick={() => {
copyAgentConfigPrompt(config, onCopySuccess);
}}
key="copyConfig"
>
<FormattedMessage
id="xpack.ingestManager.agentConfigActionMenu.copyConfigActionText"
defaultMessage="Copy config"
const onClose = useMemo(() => {
if (onCancelEnrollment) {
return onCancelEnrollment;
} else {
return () => setIsEnrollmentFlyoutOpen(false);
}
}, [onCancelEnrollment, setIsEnrollmentFlyoutOpen]);

return (
<AgentConfigCopyProvider>
{(copyAgentConfigPrompt) => {
return (
<>
{isYamlFlyoutOpen ? (
<EuiPortal>
<ConfigYamlFlyout
configId={config.id}
onClose={() => setIsYamlFlyoutOpen(false)}
/>
</EuiContextMenuItem>,
]}
/>
</>
);
}}
</AgentConfigCopyProvider>
);
});
</EuiPortal>
) : null}
{isEnrollmentFlyoutOpen && (
<EuiPortal>
<AgentEnrollmentFlyout agentConfigs={[config]} onClose={onClose} />
</EuiPortal>
)}
<ContextMenuActions
button={
fullButton
? {
props: {
iconType: 'arrowDown',
iconSide: 'right',
},
children: (
<FormattedMessage
id="xpack.ingestManager.agentConfigActionMenu.buttonText"
defaultMessage="Actions"
/>
),
}
: undefined
}
items={[
<EuiContextMenuItem
disabled={!hasWriteCapabilities}
icon="plusInCircle"
onClick={() => setIsEnrollmentFlyoutOpen(true)}
key="enrollAgents"
>
<FormattedMessage
id="xpack.ingestManager.agentConfigActionMenu.enrollAgentActionText"
defaultMessage="Enroll agent"
/>
</EuiContextMenuItem>,
<EuiContextMenuItem
icon="inspect"
onClick={() => setIsYamlFlyoutOpen(!isYamlFlyoutOpen)}
key="viewConfig"
>
<FormattedMessage
id="xpack.ingestManager.agentConfigActionMenu.viewConfigText"
defaultMessage="View config"
/>
</EuiContextMenuItem>,
<EuiContextMenuItem
disabled={!hasWriteCapabilities}
icon="copy"
onClick={() => {
copyAgentConfigPrompt(config, onCopySuccess);
}}
key="copyConfig"
>
<FormattedMessage
id="xpack.ingestManager.agentConfigActionMenu.copyConfigActionText"
defaultMessage="Copy config"
/>
</EuiContextMenuItem>,
]}
/>
</>
);
}}
</AgentConfigCopyProvider>
);
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React, { useMemo, useState } from 'react';
import { Redirect, useRouteMatch, Switch, Route, useHistory } from 'react-router-dom';
import React, { useMemo, useState, useCallback } from 'react';
import { Redirect, useRouteMatch, Switch, Route, useHistory, useLocation } from 'react-router-dom';
import { i18n } from '@kbn/i18n';
import { FormattedMessage, FormattedDate } from '@kbn/i18n/react';
import {
Expand All @@ -21,14 +21,15 @@ import {
} from '@elastic/eui';
import { Props as EuiTabProps } from '@elastic/eui/src/components/tabs/tab';
import styled from 'styled-components';
import { AgentConfig } from '../../../types';
import { AgentConfig, AgentConfigDetailsDeployAgentAction } from '../../../types';
import { PAGE_ROUTING_PATHS } from '../../../constants';
import { useGetOneAgentConfig, useLink, useBreadcrumbs } from '../../../hooks';
import { useGetOneAgentConfig, useLink, useBreadcrumbs, useCore } from '../../../hooks';
import { Loading } from '../../../components';
import { WithHeaderLayout } from '../../../layouts';
import { ConfigRefreshContext, useGetAgentStatus, AgentStatusRefreshContext } from './hooks';
import { LinkedAgentCount, AgentConfigActionMenu } from '../components';
import { ConfigDatasourcesView, ConfigSettingsView } from './components';
import { useIntraAppState } from '../../../hooks/use_intra_app_state';

const Divider = styled.div`
width: 0;
Expand All @@ -48,7 +49,13 @@ export const AgentConfigDetailsPage: React.FunctionComponent = () => {
const [redirectToAgentConfigList] = useState<boolean>(false);
const agentStatusRequest = useGetAgentStatus(configId);
const { refreshAgentStatus } = agentStatusRequest;
const {
application: { navigateToApp },
} = useCore();
const routeState = useIntraAppState<AgentConfigDetailsDeployAgentAction>();
const agentStatus = agentStatusRequest.data?.results;
const queryParams = new URLSearchParams(useLocation().search);
const openEnrollmentFlyoutOpenByDefault = queryParams.get('openEnrollmentFlyout') === 'true';

const headerLeftContent = useMemo(
() => (
Expand Down Expand Up @@ -95,6 +102,12 @@ export const AgentConfigDetailsPage: React.FunctionComponent = () => {
[getHref, agentConfig, configId]
);

const enrollmentCancelClickHandler = useCallback(() => {
if (routeState && routeState.onDoneNavigateTo) {
navigateToApp(routeState.onDoneNavigateTo[0], routeState.onDoneNavigateTo[1]);
}
}, [routeState, navigateToApp]);

const headerRightContent = useMemo(
() => (
<EuiFlexGroup justifyContent={'flexEnd'} direction="row">
Expand Down Expand Up @@ -155,6 +168,12 @@ export const AgentConfigDetailsPage: React.FunctionComponent = () => {
onCopySuccess={(newAgentConfig: AgentConfig) => {
history.push(getPath('configuration_details', { configId: newAgentConfig.id }));
}}
enrollmentFlyoutOpenByDefault={openEnrollmentFlyoutOpenByDefault}
onCancelEnrollment={
routeState && routeState.onDoneNavigateTo
? enrollmentCancelClickHandler
: undefined
}
/>
),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,17 @@ export interface CreateDatasourceRouteState {
onCancelUrl?: string;
}

/**
* Supported routing state for the agent config details page routes with deploy agents action
*/
export interface AgentConfigDetailsDeployAgentAction {
/** On done, navigate to the given app */
onDoneNavigateTo?: Parameters<ApplicationStart['navigateToApp']>;
}

/**
* All possible Route states.
*/
export type AnyIntraAppRouteState = CreateDatasourceRouteState;
export type AnyIntraAppRouteState =
| CreateDatasourceRouteState
| AgentConfigDetailsDeployAgentAction;
Loading