From 158e1e3c28d7c123b467660a06dc26bc828f0913 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Tue, 17 Nov 2020 19:35:40 +0000 Subject: [PATCH] fixed typing in alert flyouts and race condition on alertType id update --- x-pack/plugins/alerts/common/alert.ts | 3 +- .../expressions/entity_index_expression.tsx | 8 +- .../public/application/lib/capabilities.ts | 7 +- .../action_connector_form/action_form.tsx | 4 +- .../action_type_form.tsx | 4 +- .../sections/alert_form/alert_add.tsx | 50 +++++---- .../sections/alert_form/alert_edit.tsx | 12 ++- .../sections/alert_form/alert_form.tsx | 41 +++++-- .../sections/alert_form/alert_reducer.ts | 100 ++++++++++++++---- .../triggers_actions_ui/public/types.ts | 6 +- .../alerts/uptime_alerts_flyout_wrapper.tsx | 23 ++-- 11 files changed, 172 insertions(+), 86 deletions(-) diff --git a/x-pack/plugins/alerts/common/alert.ts b/x-pack/plugins/alerts/common/alert.ts index 97a9a58400e38..88f6090d20737 100644 --- a/x-pack/plugins/alerts/common/alert.ts +++ b/x-pack/plugins/alerts/common/alert.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectAttributes } from 'kibana/server'; +import { SavedObjectAttribute, SavedObjectAttributes } from 'kibana/server'; // eslint-disable-next-line @typescript-eslint/no-explicit-any export type AlertTypeState = Record; @@ -37,6 +37,7 @@ export interface AlertExecutionStatus { } export type AlertActionParams = SavedObjectAttributes; +export type AlertActionParam = SavedObjectAttribute; export interface AlertAction { group: string; diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_index_expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_index_expression.tsx index e5e43210d1e6b..0a722734ffc5a 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_index_expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_index_expression.tsx @@ -8,7 +8,11 @@ import React, { Fragment, FunctionComponent, useEffect, useRef } from 'react'; import { EuiFormRow } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { IErrorObject, AlertsContextValue } from '../../../../../../triggers_actions_ui/public'; +import { + IErrorObject, + AlertsContextValue, + AlertTypeParamsExpressionProps, +} from '../../../../../../triggers_actions_ui/public'; import { ES_GEO_FIELD_TYPES } from '../../types'; import { GeoIndexPatternSelect } from '../util_components/geo_index_pattern_select'; import { SingleFieldSelect } from '../util_components/single_field_select'; @@ -23,7 +27,7 @@ interface Props { errors: IErrorObject; setAlertParamsDate: (date: string) => void; setAlertParamsGeoField: (geoField: string) => void; - setAlertProperty: (alertProp: string, alertParams: unknown) => void; + setAlertProperty: AlertTypeParamsExpressionProps['setAlertProperty']; setIndexPattern: (indexPattern: IIndexPattern) => void; indexPattern: IIndexPattern; isInvalid: boolean; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts index 9e89a38377a4d..7fb50eaab7d7d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Alert, AlertType } from '../../types'; +import { AlertType } from '../../types'; +import { InitialAlert } from '../sections/alert_form/alert_reducer'; /** * NOTE: Applications that want to show the alerting UIs will need to add @@ -21,9 +22,9 @@ export const hasExecuteActionsCapability = (capabilities: Capabilities) => export const hasDeleteActionsCapability = (capabilities: Capabilities) => capabilities?.actions?.delete; -export function hasAllPrivilege(alert: Alert, alertType?: AlertType): boolean { +export function hasAllPrivilege(alert: InitialAlert, alertType?: AlertType): boolean { return alertType?.authorizedConsumers[alert.consumer]?.all ?? false; } -export function hasReadPrivilege(alert: Alert, alertType?: AlertType): boolean { +export function hasReadPrivilege(alert: InitialAlert, alertType?: AlertType): boolean { return alertType?.authorizedConsumers[alert.consumer]?.read ?? false; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx index 50f5167b9e5c2..83e6386122eb2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx @@ -36,7 +36,7 @@ import { AddConnectorInline } from './connector_add_inline'; import { actionTypeCompare } from '../../lib/action_type_compare'; import { checkActionFormActionTypeEnabled } from '../../lib/check_action_type_enabled'; import { VIEW_LICENSE_OPTIONS_LINK, DEFAULT_HIDDEN_ACTION_TYPES } from '../../../common/constants'; -import { ActionGroup } from '../../../../../alerts/common'; +import { ActionGroup, AlertActionParam } from '../../../../../alerts/common'; export interface ActionAccordionFormProps { actions: AlertAction[]; @@ -45,7 +45,7 @@ export interface ActionAccordionFormProps { setActionIdByIndex: (id: string, index: number) => void; setActionGroupIdByIndex?: (group: string, index: number) => void; setAlertProperty: (actions: AlertAction[]) => void; - setActionParamsProperty: (key: string, value: any, index: number) => void; + setActionParamsProperty: (key: string, value: AlertActionParam, index: number) => void; http: HttpSetup; actionTypeRegistry: ActionTypeRegistryContract; toastNotifications: ToastsSetup; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx index bd40d35b15b2d..5f1798d101d94 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx @@ -25,7 +25,7 @@ import { EuiLoadingSpinner, EuiBadge, } from '@elastic/eui'; -import { ResolvedActionGroup } from '../../../../../alerts/common'; +import { AlertActionParam, ResolvedActionGroup } from '../../../../../alerts/common'; import { IErrorObject, AlertAction, @@ -50,7 +50,7 @@ export type ActionTypeFormProps = { onAddConnector: () => void; onConnectorSelected: (id: string) => void; onDeleteAction: () => void; - setActionParamsProperty: (key: string, value: any, index: number) => void; + setActionParamsProperty: (key: string, value: AlertActionParam, index: number) => void; actionTypesIndex: ActionTypeIndex; connectors: ActionConnector[]; } & Pick< diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx index 89deb4b26f012..8af8ddb3fcfe1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx @@ -4,14 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ import React, { useCallback, useReducer, useState, useEffect } from 'react'; -import { isObject } from 'lodash'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiTitle, EuiFlyoutHeader, EuiFlyout, EuiFlyoutBody, EuiPortal } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { useAlertsContext } from '../../context/alerts_context'; import { Alert, AlertAction, IErrorObject } from '../../../types'; -import { AlertForm, validateBaseProperties } from './alert_form'; -import { alertReducer } from './alert_reducer'; +import { AlertForm, isValidAlert, validateBaseProperties } from './alert_form'; +import { alertReducer, InitialAlert, InitialAlertReducer } from './alert_reducer'; import { createAlert } from '../../lib/alert_api'; import { HealthCheck } from '../../components/health_check'; import { ConfirmAlertSave } from './confirm_alert_save'; @@ -36,7 +35,7 @@ export const AlertAdd = ({ alertTypeId, initialValues, }: AlertAddProps) => { - const initialAlert = ({ + const initialAlert: InitialAlert = { params: {}, consumer, alertTypeId, @@ -46,16 +45,19 @@ export const AlertAdd = ({ actions: [], tags: [], ...(initialValues ? initialValues : {}), - } as unknown) as Alert; + }; - const [{ alert }, dispatch] = useReducer(alertReducer, { alert: initialAlert }); + const [{ alert }, dispatch] = useReducer(alertReducer as InitialAlertReducer, { + alert: initialAlert, + }); const [isSaving, setIsSaving] = useState(false); const [isConfirmAlertSaveModalOpen, setIsConfirmAlertSaveModalOpen] = useState(false); - const setAlert = (value: any) => { + const setAlert = (value: InitialAlert) => { dispatch({ command: { type: 'setAlert' }, payload: { key: 'alert', value } }); }; - const setAlertProperty = (key: string, value: any) => { + + const setAlertProperty = (key: Key, value: Alert[Key] | null) => { dispatch({ command: { type: 'setProperty' }, payload: { key, value } }); }; @@ -72,7 +74,7 @@ export const AlertAdd = ({ const canShowActions = hasShowActionsCapability(capabilities); useEffect(() => { - setAlertProperty('alertTypeId', alertTypeId); + setAlertProperty('alertTypeId', alertTypeId ?? null); }, [alertTypeId]); const closeFlyout = useCallback(() => { @@ -100,7 +102,7 @@ export const AlertAdd = ({ ...(alertType ? alertType.validate(alert.params).errors : []), ...validateBaseProperties(alert).errors, } as IErrorObject; - const hasErrors = parseErrors(errors); + const hasErrors = !isValidAlert(alert, errors); const actionsErrors: Array<{ errors: IErrorObject; @@ -120,16 +122,18 @@ export const AlertAdd = ({ async function onSaveAlert(): Promise { try { - const newAlert = await createAlert({ http, alert }); - toastNotifications.addSuccess( - i18n.translate('xpack.triggersActionsUI.sections.alertAdd.saveSuccessNotificationText', { - defaultMessage: 'Created alert "{alertName}"', - values: { - alertName: newAlert.name, - }, - }) - ); - return newAlert; + if (isValidAlert(alert, errors)) { + const newAlert = await createAlert({ http, alert }); + toastNotifications.addSuccess( + i18n.translate('xpack.triggersActionsUI.sections.alertAdd.saveSuccessNotificationText', { + defaultMessage: 'Created alert "{alertName}"', + values: { + alertName: newAlert.name, + }, + }) + ); + return newAlert; + } } catch (errorRes) { toastNotifications.addDanger( errorRes.body?.message ?? @@ -206,11 +210,5 @@ export const AlertAdd = ({ ); }; -const parseErrors: (errors: IErrorObject) => boolean = (errors) => - !!Object.values(errors).find((errorList) => { - if (isObject(errorList)) return parseErrors(errorList as IErrorObject); - return errorList.length >= 1; - }); - // eslint-disable-next-line import/no-default-export export { AlertAdd as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx index 5eadc742a9dc8..a1ad04ea9104a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx @@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n'; import { useAlertsContext } from '../../context/alerts_context'; import { Alert, AlertAction, IErrorObject } from '../../../types'; import { AlertForm, validateBaseProperties } from './alert_form'; -import { alertReducer } from './alert_reducer'; +import { alertReducer, ConcreteAlertReducer } from './alert_reducer'; import { updateAlert } from '../../lib/alert_api'; import { HealthCheck } from '../../components/health_check'; import { HealthContextProvider } from '../../context/health_context'; @@ -34,14 +34,16 @@ interface AlertEditProps { } export const AlertEdit = ({ initialAlert, onClose }: AlertEditProps) => { - const [{ alert }, dispatch] = useReducer(alertReducer, { alert: initialAlert }); + const [{ alert }, dispatch] = useReducer(alertReducer as ConcreteAlertReducer, { + alert: initialAlert, + }); const [isSaving, setIsSaving] = useState(false); const [hasActionsDisabled, setHasActionsDisabled] = useState(false); const [hasActionsWithBrokenConnector, setHasActionsWithBrokenConnector] = useState( false ); - const setAlert = (key: string, value: any) => { - dispatch({ command: { type: 'setAlert' }, payload: { key, value } }); + const setAlert = (value: Alert) => { + dispatch({ command: { type: 'setAlert' }, payload: { key: 'alert', value } }); }; const { @@ -55,7 +57,7 @@ export const AlertEdit = ({ initialAlert, onClose }: AlertEditProps) => { const closeFlyout = useCallback(() => { onClose(); - setAlert('alert', initialAlert); + setAlert(initialAlert); // eslint-disable-next-line react-hooks/exhaustive-deps }, [onClose]); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx index 98db6f46d1497..b06fb3c39ea45 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx @@ -33,14 +33,14 @@ import { } from '@elastic/eui'; import { some, filter, map, fold } from 'fp-ts/lib/Option'; import { pipe } from 'fp-ts/lib/pipeable'; -import { capitalize } from 'lodash'; +import { capitalize, isObject } from 'lodash'; import { KibanaFeature } from '../../../../../features/public'; import { getDurationNumberInItsUnit, getDurationUnitValue, } from '../../../../../alerts/common/parse_duration'; import { loadAlertTypes } from '../../lib/alert_api'; -import { AlertReducerAction } from './alert_reducer'; +import { AlertReducerAction, InitialAlert } from './alert_reducer'; import { AlertTypeModel, Alert, @@ -48,18 +48,19 @@ import { AlertAction, AlertTypeIndex, AlertType, + ValidationResult, } from '../../../types'; import { getTimeOptions } from '../../../common/lib/get_time_options'; import { useAlertsContext } from '../../context/alerts_context'; import { ActionForm } from '../action_connector_form'; -import { ALERTS_FEATURE_ID } from '../../../../../alerts/common'; +import { AlertActionParam, ALERTS_FEATURE_ID } from '../../../../../alerts/common'; import { hasAllPrivilege, hasShowActionsCapability } from '../../lib/capabilities'; import { SolutionFilter } from './solution_filter'; import './alert_form.scss'; const ENTER_KEY = 13; -export function validateBaseProperties(alertObject: Alert) { +export function validateBaseProperties(alertObject: InitialAlert): ValidationResult { const validationResult = { errors: {} }; const errors = { name: new Array(), @@ -92,12 +93,25 @@ export function validateBaseProperties(alertObject: Alert) { return validationResult; } +const hasErrors: (errors: IErrorObject) => boolean = (errors) => + !!Object.values(errors).find((errorList) => { + if (isObject(errorList)) return hasErrors(errorList as IErrorObject); + return errorList.length >= 1; + }); + +export function isValidAlert( + alertObject: InitialAlert | Alert, + validationResult: IErrorObject +): alertObject is Alert { + return !hasErrors(validationResult); +} + function getProducerFeatureName(producer: string, kibanaFeatures: KibanaFeature[]) { return kibanaFeatures.find((featureItem) => featureItem.id === producer)?.name; } interface AlertFormProps { - alert: Alert; + alert: InitialAlert; dispatch: React.Dispatch; errors: IErrorObject; canChangeTrigger?: boolean; // to hide Change trigger button @@ -203,10 +217,13 @@ export const AlertForm = ({ useEffect(() => { setAlertTypeModel(alert.alertTypeId ? alertTypeRegistry.get(alert.alertTypeId) : null); - }, [alert, alertTypeRegistry]); + if (alert.alertTypeId && alertTypesIndex && alertTypesIndex.has(alert.alertTypeId)) { + setDefaultActionGroupId(alertTypesIndex.get(alert.alertTypeId)!.defaultActionGroupId); + } + }, [alert, alert.alertTypeId, alertTypesIndex, alertTypeRegistry]); const setAlertProperty = useCallback( - (key: string, value: any) => { + (key: Key, value: Alert[Key] | null) => { dispatch({ command: { type: 'setProperty' }, payload: { key, value } }); }, [dispatch] @@ -225,12 +242,16 @@ export const AlertForm = ({ dispatch({ command: { type: 'setScheduleProperty' }, payload: { key, value } }); }; - const setActionProperty = (key: string, value: any, index: number) => { + const setActionProperty = ( + key: Key, + value: AlertAction[Key] | null, + index: number + ) => { dispatch({ command: { type: 'setAlertActionProperty' }, payload: { key, value, index } }); }; const setActionParamsProperty = useCallback( - (key: string, value: any, index: number) => { + (key: string, value: AlertActionParam, index: number) => { dispatch({ command: { type: 'setAlertActionParams' }, payload: { key, value, index } }); }, [dispatch] @@ -438,6 +459,7 @@ export const AlertForm = ({ {AlertParamsExpressionComponent && defaultActionGroupId && + alert.alertTypeId && alertTypesIndex?.has(alert.alertTypeId) ? ( }> & + Pick; + +interface CommandType< + T extends | 'setAlert' | 'setProperty' | 'setScheduleProperty' | 'setAlertParams' | 'setAlertActionParams' - | 'setAlertActionProperty'; + | 'setAlertActionProperty' +> { + type: T; } export interface AlertState { - alert: any; + alert: InitialAlert; +} + +interface Payload { + key: Keys; + value: Value; + index?: number; +} + +interface AlertPayload { + key: Key; + value: Alert[Key] | null; + index?: number; +} + +interface AlertActionPayload { + key: Key; + value: AlertAction[Key] | null; + index?: number; } -export interface AlertReducerAction { - command: CommandType; - payload: { - key: string; - value: {}; - index?: number; - }; +interface AlertSchedulePayload { + key: Key; + value: IntervalSchedule[Key]; + index?: number; } -export const alertReducer = (state: any, action: AlertReducerAction) => { - const { command, payload } = action; +export type AlertReducerAction = + | { + command: CommandType<'setAlert'>; + payload: Payload<'alert', InitialAlert>; + } + | { + command: CommandType<'setProperty'>; + payload: AlertPayload; + } + | { + command: CommandType<'setScheduleProperty'>; + payload: AlertSchedulePayload; + } + | { + command: CommandType<'setAlertParams'>; + payload: Payload; + } + | { + command: CommandType<'setAlertActionParams'>; + payload: Payload; + } + | { + command: CommandType<'setAlertActionProperty'>; + payload: AlertActionPayload; + }; + +export type InitialAlertReducer = Reducer<{ alert: InitialAlert }, AlertReducerAction>; +export type ConcreteAlertReducer = Reducer<{ alert: Alert }, AlertReducerAction>; + +export const alertReducer = ( + state: { alert: AlertPhase }, + action: AlertReducerAction +) => { const { alert } = state; - switch (command.type) { + switch (action.command.type) { case 'setAlert': { - const { key, value } = payload; + const { key, value } = action.payload as Payload<'alert', AlertPhase>; if (key === 'alert') { return { ...state, @@ -45,7 +100,7 @@ export const alertReducer = (state: any, action: AlertReducerAction) => { } } case 'setProperty': { - const { key, value } = payload; + const { key, value } = action.payload as AlertPayload; if (isEqual(alert[key], value)) { return state; } else { @@ -59,8 +114,8 @@ export const alertReducer = (state: any, action: AlertReducerAction) => { } } case 'setScheduleProperty': { - const { key, value } = payload; - if (isEqual(alert.schedule[key], value)) { + const { key, value } = action.payload as AlertSchedulePayload; + if (alert.schedule && isEqual(alert.schedule[key], value)) { return state; } else { return { @@ -76,7 +131,7 @@ export const alertReducer = (state: any, action: AlertReducerAction) => { } } case 'setAlertParams': { - const { key, value } = payload; + const { key, value } = action.payload as Payload>; if (isEqual(alert.params[key], value)) { return state; } else { @@ -93,7 +148,10 @@ export const alertReducer = (state: any, action: AlertReducerAction) => { } } case 'setAlertActionParams': { - const { key, value, index } = payload; + const { key, value, index } = action.payload as Payload< + keyof AlertAction, + SavedObjectAttribute + >; if (index === undefined || isEqual(alert.actions[index][key], value)) { return state; } else { @@ -116,7 +174,7 @@ export const alertReducer = (state: any, action: AlertReducerAction) => { } } case 'setAlertActionProperty': { - const { key, value, index } = payload; + const { key, value, index } = action.payload as AlertActionPayload; if (index === undefined || isEqual(alert.actions[index][key], value)) { return state; } else { diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index ebd5dd4a28968..cc0522eeb52a1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -6,7 +6,7 @@ import type { PublicMethodsOf } from '@kbn/utility-types'; import type { HttpSetup, DocLinksStart, ToastsSetup } from 'kibana/public'; import { ComponentType } from 'react'; -import { ActionGroup } from '../../alerts/common'; +import { ActionGroup, AlertActionParam } from '../../alerts/common'; import { ActionType } from '../../actions/common'; import { TypeRegistry } from './application/type_registry'; import { @@ -52,7 +52,7 @@ export interface ActionConnectorFieldsProps { export interface ActionParamsProps { actionParams: TParams; index: number; - editAction: (property: string, value: any, index: number) => void; + editAction: (key: string, value: AlertActionParam, index: number) => void; errors: IErrorObject; messageVariables?: ActionVariable[]; defaultMessage?: string; @@ -166,7 +166,7 @@ export interface AlertTypeParamsExpressionProps< alertInterval: string; alertThrottle: string; setAlertParams: (property: string, value: any) => void; - setAlertProperty: (key: string, value: any) => void; + setAlertProperty: (key: Key, value: Alert[Key] | null) => void; errors: IErrorObject; alertsContext: AlertsContextValue; defaultActionGroupId: string; diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx index 9caf0a62a4049..de4e67cfe8edc 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx @@ -17,15 +17,14 @@ export const UptimeAlertsFlyoutWrapperComponent = ({ alertFlyoutVisible, alertTypeId, setAlertFlyoutVisibility, -}: Props) => - alertFlyoutVisible && alertTypeId ? ( - - ) : null; +}: Props) => ( + +);