-
Notifications
You must be signed in to change notification settings - Fork 8.2k
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
[Endpoint] Add Endpoint empty states for onboarding #69626
Conversation
if (location.pathname === intraAppState.forRoute && !wasHandled.has(intraAppState)) { | ||
|
||
if ( | ||
location.pathname + location.search === intraAppState.forRoute && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking for feedback on this, I'm thinking when we need search params, we pass them in separately and then keep this comparison to be by pathname
only
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that this should not trigger based off of .search
string - only pathname
. The page component is the one that should handle retrieving any URL .search
params and process on them.
@@ -252,6 +325,54 @@ export const HostList = () => { | |||
]; | |||
}, [formatUrl, queryParams, search]); | |||
|
|||
const renderTableOrEmptyState = useMemo(() => { | |||
if (listData && listData.length > 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO, the flow works, but some oddities in loading cause "pop ins", gonna introduce some additional loading states
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some feedback :)
if (location.pathname === intraAppState.forRoute && !wasHandled.has(intraAppState)) { | ||
|
||
if ( | ||
location.pathname + location.search === intraAppState.forRoute && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that this should not trigger based off of .search
string - only pathname
. The page component is the one that should handle retrieving any URL .search
params and process on them.
*/ | ||
export interface AgentConfigDetailsDeployAgentAction { | ||
/** On cancel, navigate to the given app */ | ||
onCancelNavigateTo?: Parameters<ApplicationStart['navigateToApp']>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a few buttons/action on the flyout, but you only have onCancel
defined here. Is this right? they probably would (in our case) do the same thing, but what about the "proceed" button?
If there is really no need for more than one param, then I would rename it to something else - maybe onDoneNavigateTo
??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yea, right now, everything is just controlled by "Cancel", including the "Continue" option. That name makes sense, will change.
const handleCreatePolicyClick = useNavigateToAppEventHandler<CreateDatasourceRouteState>( | ||
'ingestManager', | ||
{ | ||
path: `#/integrations${packageInfo ? `/endpoint-${packageInfo.version}/add-datasource` : ''}`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just FYI - I'm changing some of this in my PR that is up, so you may end up with some conflicts when the time comes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I saw your PR, will take care of it 👍
Pinging @elastic/ingest-management (Team:Ingest Management) |
Pinging @elastic/endpoint-management (Team:Endpoint Management) |
@elasticmachine merge upstream |
* Supported routing state for the agent config details page routes with deploy agents action | ||
*/ | ||
export interface AgentConfigDetailsDeployAgentAction { | ||
/** On cancel, navigate to the given app */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update comment (remove "cancel")
() => [ | ||
{ | ||
title: i18n.translate('xpack.securitySolution.endpoint.policyList.stepOneTitle', { | ||
defaultMessage: 'Head over to Ingest Manager.', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know we're still nailing down the labels, but remove period at the end? ( 😬 ) (some for labels below)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll update all of the labels after we finalize the content for the empty states
return ( | ||
<div data-test-subj={dataTestSubj}> | ||
{loading ? ( | ||
<EuiProgress size="xs" color="accent" className="essentialAnimation" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I take back my prior comment about using the Progress bar - I thought you had content inside of it, so that it would just show a small animation a top of the content that scrolled across the width of the area (similar to the animation on tables while loading)
For this use case, where all the user sees is a "busy animation", I would suggest going with the Loading component
|
||
// No hosts, so we should check to see if there are policies for onboarding | ||
if (hostResponse && hostResponse.hosts.length === 0) { | ||
const http = coreStart.http; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe this should be defined at top - below line 17 - and used trough out this module
@@ -132,6 +165,8 @@ export const hostListReducer: ImmutableReducer<HostState, AppAction> = ( | |||
error: undefined, | |||
detailsError: undefined, | |||
policyResponseError: undefined, | |||
loading: true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm actually confused by this return here (unrelated to your change), which may be causing the state to be updated on every URL change.
I think the return here should be to reset the state for hosts if the user is leaving the Endpoints page (list and details) and if not leaving, then do nothing (just return state
).
so maybe something like:
if (
(wasPreviouslyOnListPage || wasPreviouslyOnDetailsPage) &&
!isCurrentlyOnListPage &&
!iscurrentlyOnDetailsPage
) {
return initialHostListState();
}
return state;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm actually removing this as it's what's causing the test failures
if (location.pathname === intraAppState.forRoute && !wasHandled.has(intraAppState)) { | ||
|
||
// Compare the routes only, strip any query params | ||
const forBaseRoute = intraAppState.forRoute.split('?')[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe move the parsing of the hash to line 31 since it would mean it only gets parsed once
@@ -28,7 +28,7 @@ export const IntraAppStateProvider = memo<{ | |||
}>(({ kibanaScopedHistory, children }) => { | |||
const internalAppToAppState = useMemo<IntraAppState>(() => { | |||
return { | |||
forRoute: kibanaScopedHistory.location.hash.substr(1), | |||
forRoute: kibanaScopedHistory.location.hash.substr(1).split('?')[0], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not clear to me what's happening here, and I'm a bit uneasy with the chain of string operations on the url.
After playing around a bit I think I see the intent and, IIUC, I'd prefer something more explicit/safe like URL
which is available in the browser and node
const route = kibanaScopedHistory.location.hash.substr(1);
const url = new URL(route, kibanaScopedHistory.location);
...
forRoute: url.pathname
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jfsiii @paul-tavares I solved this by just passing in the url base path as well from the state so that we can control it from there. It saves us from having to parse potential query params and is more exact. let me know your thoughts
const openEnrollmentFlyoutOpenByDefault = | ||
queryString.parse(useLocation().search).openEnrollmentFlyout === 'true'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use URLSearchParams
here, instead?
const openEnrollmentFlyoutOpenByDefault = | |
queryString.parse(useLocation().search).openEnrollmentFlyout === 'true'; | |
const queryParams = new URLSearchParams(useLocation().search); | |
const openEnrollmentFlyoutOpenByDefault = queryParams.get('openEnrollmentFlyout') === 'true'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI - If I remember correctly, the last time I tried to use URLSearchParams I had issues in the Jest UT environment because I think jsdom
did support it. I don't believe we have UT for these UIR components, so it maybe find to use it for now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just pulled in the URLSearchParams
as opposed to query-string
and yes this doesn't run in Jest UT right now afaik. I plan to use functional tests when our host tests are re-enabled, so we'll see what happens.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did an initial scan and left some notes about how we deal with URLs. I can come back later and do a deeper review.
@jfsiii addressed your initial comments, let me know what else you find. Thanks for helping out! |
return { | ||
forRoute: kibanaScopedHistory.location.hash.substr(1), | ||
routeState: kibanaScopedHistory.location.state as AnyIntraAppRouteState, | ||
forRoute: intraAppState && intraAppState.baseRoute ? intraAppState.baseRoute : '', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll defer to @jfsiii here, but personally I think we should parse the hash and remove the search params from it (if any), for a few reason:
- This "bridge" solution here is temporary and should be looked at as a work-around to the fact that Hash router is still being used. So when this is removed (when Ingest moves to using BrowserRouter and the
ScoppedHistory
provided by Kibana's mount params), everything should just work normally (™️ famous words 😬 ) - It feels wrong to have the Route interface state params define what
basePath
the state applies to, being that we're already redirecting to a specific location. I feel that the Route state params should be meant for the View that it displays, not for the intermediate process that bridges the two routing approaches - Adding this param in the state will further couple the provider with Ingest in having to know/define the
baseRoute
for the Route
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jfsiii @paul-tavares my original apprehension to using new URL()
is that I didn't see a way to get the protocol
required to create the object without accessing window.location
or just faking one. I wasn't seeing it in the kibanaScopedHistory
object anywhere.
After thinking about it, my understanding is that our goal here is to just use a library for URL parsing to be more safe, which makes complete sense. So, I propose we just fake the protocol so that we're able to properly create the URL object and strip out the pathname
I'm doing this locally which is working right now. As @paul-tavares said, we could back this out when we're no longer using a hashRouter.
forRoute: (new URL(`https://localhost${kibanaScopedHistory.location.hash.substr(1)}`)).pathname
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kevinlog The URL constructor has the signature
const url = new URL(url [, base])
If we have a relative URL and are only after the pathname I think we can follow the example from
Lines 10 to 11 in 7c22204
const removeRelativePath = (relativePath: string): string => | |
new URL(relativePath, 'http://example.com').pathname; |
e.g.
const route = kibanaScopedHistory.location.hash.substr(1);
// url will have protocol, port, etc from second arg, but we're ignoring them
const url = new URL(route, 'http://any.tld.works');
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jfsiii sounds good, I built the URL as above - it's all working as expected
routeState: kibanaScopedHistory.location.state as AnyIntraAppRouteState, | ||
}; | ||
}, [kibanaScopedHistory.location.hash, kibanaScopedHistory.location.state]); | ||
}, [kibanaScopedHistory.location.state, kibanaScopedHistory.location.hash]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spiderman_meme.jpg
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approving in the spirit of iterative improvement and since my concerns were addressed but I only did a "linter"-type review. I didn't check it out and run it locally or think much about the way the parts interact.
@elasticmachine merge upstream |
💚 Build SucceededBuild metrics
History
To update your PR or re-run it, just comment with: |
* master: (59 commits) [Lens] Fix broken test (elastic#70117) [SIEM] Import timeline fix (elastic#65448) [SECURITY SOLUTION][INGEST] UX update for ingest manager edit/create datasource for endpoint (elastic#70079) [Telemetry] Collector Schema (elastic#64942) [Endpoint] Add Endpoint empty states for onboarding (elastic#69626) Hide unused resolver buttons (elastic#70112) [Security] `Investigate in Resolver` Timeline Integration (elastic#70111) [Discover] Improve styling of graphs in sidebar (elastic#69440) [Metrics UI] Fix EuiTheme type issue (elastic#69735) skip failing suite (elastic#70104) (elastic#70103) [ENDPOINT] Hide the Timeline Flyout while on the Management Pages (elastic#69998) [SIEM][CASE] Persist callout when dismissed (elastic#68372) [SIEM][Exceptions] - Cleaned up and updated exception list item comment structure (elastic#69532) [Maps] remove indexing state from redux (elastic#69765) Add API integration test for deleting data streams. (elastic#70020) renames SIEM to Security Solution (elastic#70070) Adding saved_objects_page in OSS (elastic#69900) [Lens] Use accordion menus in field list for available and empty fields (elastic#68871) Dynamic uiActions & license support (elastic#68507) [SIEM] Update readme for timeline apis (elastic#67038) ...
…bana into alerting/consumer-based-rbac * 'alerting/consumer-based-rbac' of github.com:gmmorris/kibana: (25 commits) [Lens] Fix broken test (elastic#70117) [SIEM] Import timeline fix (elastic#65448) [SECURITY SOLUTION][INGEST] UX update for ingest manager edit/create datasource for endpoint (elastic#70079) [Telemetry] Collector Schema (elastic#64942) [Endpoint] Add Endpoint empty states for onboarding (elastic#69626) Hide unused resolver buttons (elastic#70112) [Security] `Investigate in Resolver` Timeline Integration (elastic#70111) [Discover] Improve styling of graphs in sidebar (elastic#69440) [Metrics UI] Fix EuiTheme type issue (elastic#69735) skip failing suite (elastic#70104) (elastic#70103) [ENDPOINT] Hide the Timeline Flyout while on the Management Pages (elastic#69998) [SIEM][CASE] Persist callout when dismissed (elastic#68372) [SIEM][Exceptions] - Cleaned up and updated exception list item comment structure (elastic#69532) [Maps] remove indexing state from redux (elastic#69765) Add API integration test for deleting data streams. (elastic#70020) renames SIEM to Security Solution (elastic#70070) Adding saved_objects_page in OSS (elastic#69900) [Lens] Use accordion menus in field list for available and empty fields (elastic#68871) Dynamic uiActions & license support (elastic#68507) [SIEM] Update readme for timeline apis (elastic#67038) ...
Summary
This PR adds an onboarding flow for the empty Host list.
On Endpoint:
On Ingest:
openEnrollmentFlyout
to the Agent config details page to open the deployment flyout to allow us to deep link to itTesting:
No Policy prompt
Add Policy in Ingest
No Endpoint prompt
Enroll Agent in Ingest
Endpoint
Checklist
Delete any items that are not applicable to this PR.
For maintainers