-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
[Security Solution][Detections] Update read-only callouts from warning to info so they persist when dismissed #84904
[Security Solution][Detections] Update read-only callouts from warning to info so they persist when dismissed #84904
Conversation
850e429
to
a9bd663
Compare
Further rework towards cases-like implementation?In Cases, they have a slightly more sophisticated implementation of callouts. It's 5% bound to specifics of Cases and 95% generic from the code standpoint. Maybe there's an opportunity to create a 100% generic implementation for use across the whole
Here's a screenshot. The first two callouts are our, from Code-wise, here the page renders: <NoWriteAlertsCallOut /> // 'You cannot change alert states'
<ReadOnlyCallOut /> // 'Rule permissions required'
<DemoCaseCallOut /> // 'Callout group title' where const message1 = {
errorType: 'primary' as const,
id: 'msg1',
title: 'You cannot change alert states',
description: <>{'You only have permissions to view alerts. If you need to update alert states (open or close alerts), contact your Kibana administrator.'}</>,
};
const message2 = {
errorType: 'primary' as const,
id: 'msg2',
title: 'Rule permissions required',
description: <>{'You are currently missing the required permissions to create/edit detection engine rule. Please contact your administrator for further assistance.'}</>,
};
const message3 = {
errorType: 'warning' as const,
id: 'msg3',
title: 'You cannot do nothing',
description: <>{'You have no permissions...'}</>,
};
export const DemoCaseCallOut = () => {
return <CaseCallOut title="Callout group title" messages={[message1, message2, message3]} />;
}; So do you think it might make sense to (eventually) rework/standardise callouts this way? Or we might never need it? |
x-pack/plugins/security_solution/public/detections/components/callouts/index.ts
Outdated
Show resolved
Hide resolved
dismiss: (message: CallOutMessage) => void; | ||
} | ||
|
||
export const useCallOutStorage = ( |
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.
Is this a right place for this kind of code?
We have some containers
folders in the plugin, and they contain hooks (mostly, I think).
On the other hand, this guy useCallOutStorage
is tightly coupled to the components (dumb and smart) represented in this folder.
More broad question: how do we structure components and business logic they use?
const [visibilityState, setVisibilityState] = useMap(visibilityStateInitial); | ||
|
||
const { getMessages, addMessage } = useMessagesStorage(); | ||
|
||
const dismissedMessagesKey = getDismissedMessagesStorageKey(namespace); | ||
|
||
const getVisibleMessageIds = useCallback(() => { | ||
return Object.entries(visibilityState) | ||
.filter(([messageId, isVisible]) => isVisible) | ||
.map(([messageId, isVisible]) => messageId); | ||
}, [visibilityState]); |
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.
Here I implemented it with normal state and callback hooks, because at this point this is the approach I saw in the codebase.
Question: do we use useReducer
, redux or maybe even rx or sagas to build app logic? I think I saw some redux usage somewhere within security_colution
...
Do we have some conventions/agreements on how to build it?
...ugins/security_solution/public/detections/components/no_write_alerts_callout/translations.ts
Outdated
Show resolved
Hide resolved
return shouldRender ? <CallOut message={message} onDismiss={dismiss} /> : null; | ||
}; | ||
|
||
export const CallOutSwitcher = memo(CallOutSwitcherComponent); |
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.
What's our approach to memoizing components?
I can see it's used pretty extensively throughout the code. The docs say "This method only exists as a performance optimization. Do not rely on it to “prevent” a render, as this can lead to bugs."
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 believe we typically memoize any exported component whether it be inline or on a new line like you've done here except in certain cases where they might be memoized elsewhere already (like some of the components in forms where the components they're passed to are memoized. Someone with a more in depth knowledge of the entire codebase might be able to give you more edge case examples)
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.
Thank you, Davis 👍
x-pack/plugins/security_solution/public/detections/components/callouts/callout_types.ts
Outdated
Show resolved
Hide resolved
...k/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx
Outdated
Show resolved
Hide resolved
a9bd663
to
8e06ca4
Compare
x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.test.tsx
Outdated
Show resolved
Hide resolved
I'm in agreement with all the above points @banderror. From a UX perspective seeing stacked callouts (of like |
fc5faf7
to
2092641
Compare
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.
Code and behavior looks great so far! I still see a lot of questions, skipped tests, and TODOs so I don't think this is ready to approve/merge, but once that's cleaned up I think this'll be a quick LGTM 👍
}); | ||
}); | ||
|
||
const loadPageAsReadOnlyUser = (url: string) => { |
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 move all these methods to the tasks folder?
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.
Hmm 🤔 , to be honest I'm not sure!
I moved functions that seem to me truly reusable, like some actions on callouts.
This code, on the other hand, is quite coupled to the test suite:
const loadPageAsReadOnlyUser = (url: string) => {
waitForPageWithoutDateRange(url, ROLES.reader);
waitForPageTitleToBeShown();
};
const reloadPage = () => {
cy.reload();
waitForPageTitleToBeShown();
};
const waitForPageTitleToBeShown = () => {
cy.get(PAGE_TITLE).should('be.visible');
};
Here, for example, it waits for the page title. This looks natural for this particular suite only because callouts are rendered on top of the page title. Other suites might have different needs in terms of what to wait for when we reloadPage()
- they might need to wait less or more to pass and also be resilient.
Please let me know if you have any strong opinion on that!
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 completely understand your point :)
We are used to work in the other way, so, if for whatever reason another colleague wants to extend the tests, will probably create a new spec in order to don't overload the current one. And most likely is going to check the tasks folder for the methods he can use rather than check the spec for them.
If you think that the scenario I mentioned is not going to happen and you want to keep it on the spec file I would suggest you to move it at the top of the file after the imports to give a bit of visibility ;)
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.
Sure thing, easy! Did it. Thank you @MadameSheema !
x-pack/plugins/security_solution/cypress/tasks/detections/callouts.ts
Outdated
Show resolved
Hide resolved
2092641
to
b336286
Compare
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.
This looks great! Truly appreciate the thought toward reusability here, and the comprehensive review materials are incredibly helpful!
It'll be nice to add some unit tests to these new pieces since the old ones weren't testing much, but in the meantime I think that the cypress tests cover us at the high level 👍
|
||
export const waitForPageWithoutDateRange = (url: string, role?: RolesType) => { | ||
cy.visit(role ? getUrlWithRoute(role, url) : url); | ||
cy.get('[data-test-subj="headerGlobalNav"]', { timeout: 120000 }); |
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.
Nit: We should capture this selector in a constant, I think.
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.
Yes +1, there's a room for cleanup in this file, I just didn't want to touch too many things in this PR.
b336286
to
c99615b
Compare
|
||
export const waitForPageWithoutDateRange = (url: string, role?: RolesType) => { | ||
cy.visit(role ? getUrlWithRoute(role, url) : url); | ||
cy.get('[data-test-subj="headerGlobalNav"]', { timeout: 120000 }); |
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.
Any reason this timeout is set so high?
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.
Not sure. What I did is took an existing function loginAndWaitForPageWithoutDateRange
(it's above this one) and removed the login
part from it to be able to implement the tests.
loginAndWaitForPageWithoutDateRange
with this timeout was introduced by @XavierM along with the "defaultCommandTimeout": 120000
in cypress.json
. See here:
I guess that's some legacy, because the current "defaultCommandTimeout": 60000
import { CallOutDescription } from './callout_description'; | ||
import { CallOutDismissButton } from './callout_dismiss_button'; | ||
|
||
export interface CallOutProps { |
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.
Probably don't need to export all these prop interfaces
import { CallOutMessage } from './callout_types'; | ||
import * as i18n from './translations'; | ||
|
||
export interface CallOutDismissButtonProps { |
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.
same export comment as above
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.
Mostly nits and questions. Great job!
💚 Build SucceededMetrics [docs]Module Count
Async chunks
Distributable file count
History
To update your PR or re-run it, just comment with: |
…g to info so they persist when dismissed (elastic#84904) **Addresses:** elastic#76587 ## Summary In this PR I'm doing basically 2 things: 1. Making readonly callouts we have in Detections `primary` instead of `warning` and thus persistable in local storage (if a callout is dismissed, we remember it there) 2. Creating a reusable implementation for that. TODO: - [x] Adjust all callouts used in Detections to be of type `primary` - [x] Implement the local storage logic (dumb) - [x] Implement the local storage logic (reusable) - [x] Add a new user role: "Reader" (read-only user) - [x] Add Cypress tests Out of scope: - Add unit tests. I'll probably address this in a follow-up PR. Do you think it's worth it or better to wait until the rework (see the next point)? - Rework callouts to standardise them across Detections, Cases and other parts of the Security app? See my comment below in this PR. ## Screen recordings Quick demo of how this implementation works: - [primary callouts](https://drive.google.com/file/d/1SYQd_ihKPvzlVUxELI8qNEqLBOkL18Gd/view?usp=sharing) - [warning, danger](https://drive.google.com/file/d/1lrAFPyXNjOYSiEsUXxY_fjXsvmyDcdWY/view?usp=sharing) (callout types here were manually adjusted) ## Additional notes Cypress tests are based on the work done in elastic#81866. ![](https://puu.sh/GXwOd/1c855cb03f.png)
…g to info so they persist when dismissed (#84904) (#86047) **Addresses:** #76587 ## Summary In this PR I'm doing basically 2 things: 1. Making readonly callouts we have in Detections `primary` instead of `warning` and thus persistable in local storage (if a callout is dismissed, we remember it there) 2. Creating a reusable implementation for that. TODO: - [x] Adjust all callouts used in Detections to be of type `primary` - [x] Implement the local storage logic (dumb) - [x] Implement the local storage logic (reusable) - [x] Add a new user role: "Reader" (read-only user) - [x] Add Cypress tests Out of scope: - Add unit tests. I'll probably address this in a follow-up PR. Do you think it's worth it or better to wait until the rework (see the next point)? - Rework callouts to standardise them across Detections, Cases and other parts of the Security app? See my comment below in this PR. ## Screen recordings Quick demo of how this implementation works: - [primary callouts](https://drive.google.com/file/d/1SYQd_ihKPvzlVUxELI8qNEqLBOkL18Gd/view?usp=sharing) - [warning, danger](https://drive.google.com/file/d/1lrAFPyXNjOYSiEsUXxY_fjXsvmyDcdWY/view?usp=sharing) (callout types here were manually adjusted) ## Additional notes Cypress tests are based on the work done in #81866. ![](https://puu.sh/GXwOd/1c855cb03f.png)
Addresses: #76587
Summary
In this PR I'm doing basically 2 things:
primary
instead ofwarning
and thus persistable in local storage (if a callout is dismissed, we remember it there)TODO:
primary
Out of scope:
Screen recordings
Quick demo of how this implementation works:
Additional notes
Cypress tests are based on the work done in #81866.
Checklist
Delete any items that are not applicable to this PR.
Found issues:
For maintainers