-
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
[Response Ops] [Rule Form] Add Rule Form Flyout v2 #206685
base: main
Are you sure you want to change the base?
Changes from all commits
1ef801c
1faf112
4cb5b1d
e5b287a
74f59b7
dd6e167
6dd013d
69eb8fa
74d5339
646af0f
d4c91d3
2d61517
1ffea47
19f2fdc
3e9ebe4
70d4dab
9c4d831
547c4da
c2174d6
4719faf
51e33f9
c25c283
dc8c9ab
114e98a
e3a0e7e
dbc9712
fd7abec
4dbc4f0
b0f977b
4b806ac
13429c1
f557eb2
286721f
1fd34d0
c437919
dbbe03a
f9f82ed
f21566b
0efa925
fb70192
4fd1228
f2e01b9
e6befaf
07ee45a
370a2b6
908d36c
52c6414
aa0287f
9ef99b5
bb09a7d
d182820
118a154
b8a2e33
09d456d
c89a707
565c974
def9107
3f16b58
27cb8b3
688c145
c3eff4f
3ef3181
756cfa8
95b14da
aa0abd0
990a73c
c4a2c9b
3772d57
319f869
99c4dba
9298807
0b991bf
732ce23
4539f97
929ce73
e9948a2
944621a
699300b
1f1f1a5
89f8aaa
0249c2d
0a01b81
42e12ee
abb8326
1013d20
4453609
a8ffcf1
619a37b
850bb6b
acaddb9
3ab44d2
fc004b0
c1edbf3
2d229e6
30c3d46
f8b000b
75f6b91
ce5d6d6
7797540
887b65d
cee9258
c72958b
9e7716d
314d14c
e1fac6f
3b0f4aa
35acdb7
feb0c83
8b13750
9c5b11a
b168e01
30b89de
f4aff0f
6e765ec
c015311
85971c3
587f5c2
5e84003
fd7e080
9e3d7e9
20c0821
78cf8db
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
This export path is only for lazy loading the flyout. Importing `@kbn/response-ops-rule-form` directly generally increases a plugin's bundle size unnecessarily. | ||
|
||
Flyout UI is handled at the root of this component to avoid UI glitches. We want to render loading states and the fully loaded flyout body within the same <EuiFlyout> component, otherwise the user will see multiple flyouts and overlay masks flickering in and out. | ||
|
||
This should be the ONLY export path for contexts that use the rule form as a flyout and not as the full page. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/* | ||
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||
* Public License v 1"; you may not use this file except in compliance with, at | ||
* your election, the "Elastic License 2.0", the "GNU Affero General Public | ||
* License v3.0 only", or the "Server Side Public License, v 1". | ||
*/ | ||
|
||
export * from './rule_form_flyout'; |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,72 @@ | ||||||
/* | ||||||
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||||||
* Public License v 1"; you may not use this file except in compliance with, at | ||||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public | ||||||
* License v3.0 only", or the "Server Side Public License, v 1". | ||||||
*/ | ||||||
|
||||||
import { EuiFlyoutResizable, EuiLoadingElastic } from '@elastic/eui'; | ||||||
import React, { Suspense, lazy, useCallback } from 'react'; | ||||||
import type { RuleFormProps } from '../src/rule_form'; | ||||||
import type { RuleTypeMetaData } from '../src/types'; | ||||||
import { | ||||||
RuleFlyoutUIContextProvider, | ||||||
useRuleFlyoutUIContext, | ||||||
RuleFormErrorPromptWrapper, | ||||||
} from '../lib'; | ||||||
|
||||||
const RuleForm: React.LazyExoticComponent<React.FC<RuleFormProps<any>>> = lazy(() => | ||||||
import('../src/rule_form').then((module) => ({ default: module.RuleForm })) | ||||||
); | ||||||
|
||||||
const RuleFormFlyoutRenderer = <MetaData extends RuleTypeMetaData>( | ||||||
props: RuleFormProps<MetaData> | ||||||
) => { | ||||||
const { onClickClose, hideCloseButton } = useRuleFlyoutUIContext(); | ||||||
const onClose = useCallback(() => { | ||||||
// If onClickClose has been initialized, call it instead of onCancel. onClickClose should be used to | ||||||
// determine if the close confirmation modal should be shown. props.onCancel is passed down the component hierarchy | ||||||
// and will be called 1) by onClickClose, if the confirmation modal doesn't need to be shown, or 2) by the confirm | ||||||
// button on the confirmation modal | ||||||
if (onClickClose) { | ||||||
onClickClose(); | ||||||
} else { | ||||||
// ONLY call props.onCancel directly from this level of the component hierarcht if onClickClose has not yet been initialized. | ||||||
// This will only occur if the user tries to close the flyout while the Suspense fallback is still visible | ||||||
props.onCancel?.(); | ||||||
} | ||||||
}, [onClickClose, props]); | ||||||
return ( | ||||||
<EuiFlyoutResizable | ||||||
ownFocus | ||||||
onClose={onClose} | ||||||
aria-labelledby="flyoutTitle" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Shouldn't also translate it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, I was not aware. |
||||||
size={620} | ||||||
minWidth={500} | ||||||
className="ruleFormFlyout__container" | ||||||
hideCloseButton={hideCloseButton} | ||||||
> | ||||||
<Suspense | ||||||
fallback={ | ||||||
<RuleFormErrorPromptWrapper hasBorder={false} hasShadow={false}> | ||||||
<EuiLoadingElastic size="xl" /> | ||||||
</RuleFormErrorPromptWrapper> | ||||||
} | ||||||
> | ||||||
<RuleForm {...props} isFlyout /> | ||||||
</Suspense> | ||||||
</EuiFlyoutResizable> | ||||||
); | ||||||
}; | ||||||
|
||||||
export const RuleFormFlyout = <MetaData extends RuleTypeMetaData>( | ||||||
props: RuleFormProps<MetaData> | ||||||
) => { | ||||||
return ( | ||||||
<RuleFlyoutUIContextProvider> | ||||||
<RuleFormFlyoutRenderer {...props} /> | ||||||
</RuleFlyoutUIContextProvider> | ||||||
); | ||||||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
This export path is for utility functions that might be necessary both for plugins that import from `@kbn/response-ops-rule-form` or from `@kbn/response-ops-rule-form/flyout` to keep their bundle size down. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/* | ||
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||
* Public License v 1"; you may not use this file except in compliance with, at | ||
* your election, the "Elastic License 2.0", the "GNU Affero General Public | ||
* License v3.0 only", or the "Server Side Public License, v 1". | ||
*/ | ||
|
||
export * from './is_valid_rule_form_plugins'; | ||
export * from './rule_flyout_ui_context'; | ||
export * from './rule_form_error_prompt_wrapper'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/* | ||
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||
* Public License v 1"; you may not use this file except in compliance with, at | ||
* your election, the "Elastic License 2.0", the "GNU Affero General Public | ||
* License v3.0 only", or the "Server Side Public License, v 1". | ||
*/ | ||
|
||
import type { RuleFormProps } from '../src/rule_form'; | ||
|
||
const requiredPluginNames = [ | ||
'http', | ||
'i18n', | ||
'theme', | ||
'userProfile', | ||
'application', | ||
'notifications', | ||
'charts', | ||
'settings', | ||
'data', | ||
'unifiedSearch', | ||
'docLinks', | ||
'dataViews', | ||
'fieldsMetadata', | ||
]; | ||
|
||
type RequiredRuleFormPlugins = Omit< | ||
RuleFormProps['plugins'], | ||
'actionTypeRegistry' | 'ruleTypeRegistry' | ||
>; | ||
|
||
export const isValidRuleFormPlugins = (input: unknown): input is RequiredRuleFormPlugins => { | ||
if (typeof input !== 'object' || input === null) { | ||
return false; | ||
} | ||
|
||
requiredPluginNames.forEach((pluginName) => { | ||
if (!(pluginName in input)) { | ||
// eslint-disable-next-line no-console | ||
console.error(`RuleForm plugins is missing required plugin: ${pluginName}`); | ||
return false; | ||
} | ||
}); | ||
|
||
return true; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/* | ||
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||
* Public License v 1"; you may not use this file except in compliance with, at | ||
* your election, the "Elastic License 2.0", the "GNU Affero General Public | ||
* License v3.0 only", or the "Server Side Public License, v 1". | ||
*/ | ||
|
||
import React, { createContext, useState } from 'react'; | ||
|
||
const initialRuleFlyoutUIContext: { | ||
onClickClose: (() => void) | null; | ||
hideCloseButton: boolean; | ||
setOnClickClose: (onClickClose: () => void) => void; | ||
setHideCloseButton: (hideCloseButton: boolean) => void; | ||
} = { | ||
onClickClose: null, | ||
hideCloseButton: false, | ||
setOnClickClose: () => {}, | ||
setHideCloseButton: () => {}, | ||
}; | ||
|
||
export const RuleFlyoutUIContext = createContext(initialRuleFlyoutUIContext); | ||
|
||
export const RuleFlyoutUIContextProvider: React.FC<React.PropsWithChildren> = ({ children }) => { | ||
const [onClickClose, setOnClickClose] = useState<(() => void) | null>(null); | ||
const [hideCloseButton, setHideCloseButton] = useState<boolean>(false); | ||
return ( | ||
<RuleFlyoutUIContext.Provider | ||
value={{ | ||
onClickClose, | ||
hideCloseButton, | ||
setOnClickClose, | ||
setHideCloseButton, | ||
}} | ||
> | ||
{children} | ||
</RuleFlyoutUIContext.Provider> | ||
); | ||
}; | ||
|
||
export const useRuleFlyoutUIContext = () => { | ||
return React.useContext(RuleFlyoutUIContext); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/* | ||
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||
* Public License v 1"; you may not use this file except in compliance with, at | ||
* your election, the "Elastic License 2.0", the "GNU Affero General Public | ||
* License v3.0 only", or the "Server Side Public License, v 1". | ||
*/ | ||
|
||
export * from './confirm_create_rule'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
/* | ||
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||
* Public License v 1"; you may not use this file except in compliance with, at | ||
* your election, the "Elastic License 2.0", the "GNU Affero General Public | ||
* License v3.0 only", or the "Server Side Public License, v 1". | ||
*/ | ||
|
||
import React from 'react'; | ||
import { EuiConfirmModal, EuiText } from '@elastic/eui'; | ||
import { | ||
RULE_FORM_CANCEL_MODAL_TITLE, | ||
RULE_FORM_CANCEL_MODAL_CONFIRM, | ||
RULE_FORM_CANCEL_MODAL_CANCEL, | ||
RULE_FORM_CANCEL_MODAL_DESCRIPTION, | ||
} from '../../translations'; | ||
|
||
export interface ConfirmRuleCloseRuleProps { | ||
onCancel: () => void; | ||
onConfirm: () => void; | ||
} | ||
|
||
export const ConfirmRuleClose = (props: ConfirmRuleCloseRuleProps) => { | ||
const { onCancel, onConfirm } = props; | ||
|
||
return ( | ||
<EuiConfirmModal | ||
onCancel={onCancel} | ||
onConfirm={onConfirm} | ||
data-test-subj="confirmRuleCloseModal" | ||
buttonColor="danger" | ||
defaultFocusedButton="confirm" | ||
title={RULE_FORM_CANCEL_MODAL_TITLE} | ||
confirmButtonText={RULE_FORM_CANCEL_MODAL_CONFIRM} | ||
cancelButtonText={RULE_FORM_CANCEL_MODAL_CANCEL} | ||
> | ||
<EuiText> | ||
<p>{RULE_FORM_CANCEL_MODAL_DESCRIPTION}</p> | ||
</EuiText> | ||
</EuiConfirmModal> | ||
); | ||
}; |
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.
Shouldn't we call
props.onCancel
either way? For example: