diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/described_form_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/described_form_field.tsx deleted file mode 100644 index f5d13a443c883..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/described_form_field.tsx +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * 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, { FunctionComponent } from 'react'; -import { EuiDescribedFormGroup, EuiDescribedFormGroupProps } from '@elastic/eui'; - -import { ToggleableField, Props as ToggleableFieldProps } from './toggleable_field'; - -type Props = EuiDescribedFormGroupProps & { - children: (() => JSX.Element) | JSX.Element | JSX.Element[] | undefined; - switchProps?: Omit<ToggleableFieldProps, 'children'>; -}; - -export const DescribedFormField: FunctionComponent<Props> = ({ - children, - switchProps, - ...restDescribedFormProps -}) => { - return ( - <EuiDescribedFormGroup {...restDescribedFormProps}> - {switchProps ? ( - <ToggleableField {...switchProps}>{children}</ToggleableField> - ) : typeof children === 'function' ? ( - children() - ) : ( - children - )} - </EuiDescribedFormGroup> - ); -}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/described_form_row/described_form_row.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/described_form_row/described_form_row.tsx new file mode 100644 index 0000000000000..98c63437659fd --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/described_form_row/described_form_row.tsx @@ -0,0 +1,95 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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, { FunctionComponent, useState } from 'react'; +import { + EuiDescribedFormGroup, + EuiDescribedFormGroupProps, + EuiSwitchProps, + EuiSwitch, + EuiSpacer, +} from '@elastic/eui'; + +export interface SwitchProps + extends Omit<EuiSwitchProps, 'children' | 'checked' | 'value' | 'onChange'> { + /** + * use initialValue to specify an uncontrolled component + */ + initialValue?: boolean; + + /** + * checked and onChange together specify a controlled component + */ + checked?: boolean; + onChange?: (nextValue: boolean) => void; +} + +export type Props = EuiDescribedFormGroupProps & { + children: (() => JSX.Element) | JSX.Element | JSX.Element[] | undefined; + + switchProps?: SwitchProps; + + /** + * Use this prop to pass down components that should be rendered below the toggle like + * warnings or notices. + */ + fieldNotices?: React.ReactNode; +}; + +export const DescribedFormRow: FunctionComponent<Props> = ({ + children, + switchProps, + description, + fieldNotices, + ...restDescribedFormProps +}) => { + if ( + switchProps && + !(typeof switchProps.checked === 'boolean' || typeof switchProps.initialValue === 'boolean') + ) { + throw new Error('Must specify controlled, uncontrolled component. See SwitchProps interface!'); + } + const [uncontrolledIsContentVisible, setUncontrolledIsContentVisible] = useState<boolean>( + () => switchProps?.initialValue ?? false + ); + const isContentVisible = Boolean(switchProps?.checked ?? uncontrolledIsContentVisible); + + const renderToggle = () => { + if (!switchProps) { + return null; + } + const { onChange, checked, initialValue, ...restSwitchProps } = switchProps; + + return ( + <EuiSwitch + {...restSwitchProps} + checked={isContentVisible} + onChange={(e) => { + const nextValue = e.target.checked; + setUncontrolledIsContentVisible(nextValue); + if (onChange) { + onChange(nextValue); + } + }} + /> + ); + }; + return ( + <EuiDescribedFormGroup + {...restDescribedFormProps} + description={ + <> + {description} + <EuiSpacer size="m" /> + {renderToggle()} + {fieldNotices} + </> + } + > + {isContentVisible ? (typeof children === 'function' ? children() : children) : null} + </EuiDescribedFormGroup> + ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/described_form_row/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/described_form_row/index.ts new file mode 100644 index 0000000000000..89b77a213215e --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/described_form_row/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { DescribedFormRow } from './described_form_row'; + +export { ToggleFieldWithDescribedFormRow } from './toggle_field_with_described_form_row'; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/described_form_row/toggle_field_with_described_form_row.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/described_form_row/toggle_field_with_described_form_row.tsx new file mode 100644 index 0000000000000..779dbe47914a1 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/described_form_row/toggle_field_with_described_form_row.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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, { FunctionComponent } from 'react'; + +import { UseField } from '../../../../../shared_imports'; + +import { + DescribedFormRow, + Props as DescribedFormRowProps, + SwitchProps, +} from './described_form_row'; + +type Props = Omit<DescribedFormRowProps, 'switchProps'> & { + switchProps: Omit<SwitchProps, 'label'> & { path: string }; +}; + +export const ToggleFieldWithDescribedFormRow: FunctionComponent<Props> = ({ + switchProps, + ...passThroughProps +}) => ( + <UseField<boolean> path={switchProps.path}> + {(field) => { + return ( + <DescribedFormRow + {...passThroughProps} + switchProps={{ + ...switchProps, + label: field.label, + checked: field.value, + onChange: field.setValue, + }} + /> + ); + }} + </UseField> +); diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/index.ts index 265996c650024..fa550214db477 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/index.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/index.ts @@ -9,7 +9,7 @@ export { ErrableFormRow } from './form_errors'; export { LearnMoreLink } from './learn_more_link'; export { OptionalLabel } from './optional_label'; export { PolicyJsonFlyout } from './policy_json_flyout'; -export { DescribedFormField } from './described_form_field'; +export { DescribedFormRow, ToggleFieldWithDescribedFormRow } from './described_form_row'; export { FieldLoadingError } from './field_loading_error'; export * from './phases'; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/cold_phase/cold_phase.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/cold_phase/cold_phase.tsx index 2f5be3e45cbe7..addad5e572b70 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/cold_phase/cold_phase.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/cold_phase/cold_phase.tsx @@ -18,7 +18,12 @@ import { useFormData, UseField, ToggleField, NumericField } from '../../../../.. import { useEditPolicyContext } from '../../../edit_policy_context'; import { useConfigurationIssues } from '../../../form'; -import { LearnMoreLink, ActiveBadge, DescribedFormField } from '../../'; +import { + LearnMoreLink, + ActiveBadge, + DescribedFormRow, + ToggleFieldWithDescribedFormRow, +} from '../../'; import { MinAgeInputField, @@ -115,7 +120,7 @@ export const ColdPhase: FunctionComponent = () => { { /* Replicas section */ showReplicasField && ( - <DescribedFormField + <DescribedFormRow title={ <h3> {i18n.translate('xpack.indexLifecycleMgmt.coldPhase.replicasTitle', { @@ -153,13 +158,13 @@ export const ColdPhase: FunctionComponent = () => { }, }} /> - </DescribedFormField> + </DescribedFormRow> ) } {/* Freeze section */} {!isUsingSearchableSnapshotInHotPhase && ( - <EuiDescribedFormGroup + <ToggleFieldWithDescribedFormRow title={ <h3> <FormattedMessage @@ -179,17 +184,13 @@ export const ColdPhase: FunctionComponent = () => { } fullWidth titleSize="xs" + switchProps={{ + 'data-test-subj': 'freezeSwitch', + path: '_meta.cold.freezeEnabled', + }} > - <UseField - path="_meta.cold.freezeEnabled" - component={ToggleField} - componentProps={{ - euiFieldProps: { - 'data-test-subj': 'freezeSwitch', - }, - }} - /> - </EuiDescribedFormGroup> + <div /> + </ToggleFieldWithDescribedFormRow> )} {/* Data tier allocation section */} <DataTierAllocationField diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/hot_phase/hot_phase.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/hot_phase/hot_phase.tsx index d358fdeb25194..3ca592af85a2a 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/hot_phase/hot_phase.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/hot_phase/hot_phase.tsx @@ -20,13 +20,7 @@ import { import { Phases } from '../../../../../../../common/types'; -import { - useFormData, - UseField, - SelectField, - ToggleField, - NumericField, -} from '../../../../../../shared_imports'; +import { useFormData, UseField, SelectField, NumericField } from '../../../../../../shared_imports'; import { i18nTexts } from '../../../i18n_texts'; @@ -36,7 +30,7 @@ import { useEditPolicyContext } from '../../../edit_policy_context'; import { ROLLOVER_FORM_PATHS } from '../../../constants'; -import { LearnMoreLink, ActiveBadge, DescribedFormField } from '../../'; +import { LearnMoreLink, ActiveBadge, ToggleFieldWithDescribedFormRow } from '../../'; import { ForcemergeField, @@ -95,7 +89,7 @@ export const HotPhase: FunctionComponent = () => { })} paddingSize="m" > - <DescribedFormField + <ToggleFieldWithDescribedFormRow title={ <h3> {i18n.translate('xpack.indexLifecycleMgmt.hotPhase.rolloverFieldTitle', { @@ -123,19 +117,12 @@ export const HotPhase: FunctionComponent = () => { </p> </EuiTextColor> } + switchProps={{ + path: '_meta.hot.useRollover', + 'data-test-subj': 'rolloverSwitch', + }} fullWidth > - <UseField<boolean> - key="_meta.hot.useRollover" - path="_meta.hot.useRollover" - component={ToggleField} - componentProps={{ - fullWidth: false, - euiFieldProps: { - 'data-test-subj': 'rolloverSwitch', - }, - }} - /> {isRolloverEnabled && ( <> <EuiSpacer size="m" /> @@ -246,7 +233,7 @@ export const HotPhase: FunctionComponent = () => { </EuiFlexGroup> </> )} - </DescribedFormField> + </ToggleFieldWithDescribedFormRow> {license.canUseSearchableSnapshot() && <SearchableSnapshotField phase="hot" />} {isRolloverEnabled && !isUsingSearchableSnapshotInHotPhase && ( <ForcemergeField phase="hot" /> diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/forcemerge_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/forcemerge_field.tsx index fb7f93a42e491..dd5cc1fbc8c87 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/forcemerge_field.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/forcemerge_field.tsx @@ -14,7 +14,7 @@ import { i18nTexts } from '../../../i18n_texts'; import { useEditPolicyContext } from '../../../edit_policy_context'; -import { LearnMoreLink, DescribedFormField } from '../../'; +import { LearnMoreLink, DescribedFormRow } from '../../'; interface Props { phase: 'hot' | 'warm'; @@ -28,7 +28,7 @@ export const ForcemergeField: React.FunctionComponent<Props> = ({ phase }) => { }, [policy, phase]); return ( - <DescribedFormField + <DescribedFormRow title={ <h3> <FormattedMessage @@ -82,6 +82,6 @@ export const ForcemergeField: React.FunctionComponent<Props> = ({ phase }) => { }} /> </div> - </DescribedFormField> + </DescribedFormRow> ); }; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/searchable_snapshot_field/searchable_snapshot_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/searchable_snapshot_field/searchable_snapshot_field.tsx index e5ab5fb6a2c71..38eb743075411 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/searchable_snapshot_field/searchable_snapshot_field.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/searchable_snapshot_field/searchable_snapshot_field.tsx @@ -29,7 +29,7 @@ import { useConfigurationIssues } from '../../../../form'; import { i18nTexts } from '../../../../i18n_texts'; -import { FieldLoadingError, DescribedFormField, LearnMoreLink } from '../../../index'; +import { FieldLoadingError, DescribedFormRow, LearnMoreLink } from '../../../index'; import { SearchableSnapshotDataProvider } from './searchable_snapshot_data_provider'; @@ -297,7 +297,7 @@ export const SearchableSnapshotField: FunctionComponent<Props> = ({ phase }) => }; return ( - <DescribedFormField + <DescribedFormRow data-test-subj={`searchableSnapshotField-${phase}`} switchProps={{ checked: isFieldToggleChecked, @@ -327,12 +327,12 @@ export const SearchableSnapshotField: FunctionComponent<Props> = ({ phase }) => }} /> </EuiTextColor> - {renderInfoCallout()} </> } + fieldNotices={renderInfoCallout()} fullWidth > {isDisabled ? <div /> : renderField} - </DescribedFormField> + </DescribedFormRow> ); }; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/warm_phase/warm_phase.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/warm_phase/warm_phase.tsx index 604a36c2d08e4..9ccfcd58f4d85 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/warm_phase/warm_phase.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/warm_phase/warm_phase.tsx @@ -25,7 +25,7 @@ import { Phases } from '../../../../../../../common/types'; import { useEditPolicyContext } from '../../../edit_policy_context'; import { useConfigurationIssues } from '../../../form'; -import { LearnMoreLink, ActiveBadge, DescribedFormField } from '../../'; +import { LearnMoreLink, ActiveBadge, DescribedFormRow } from '../../'; import { useRolloverPath, @@ -141,7 +141,7 @@ export const WarmPhase: FunctionComponent = () => { )} paddingSize="m" > - <DescribedFormField + <DescribedFormRow title={ <h3> {i18n.translate('xpack.indexLifecycleMgmt.warmPhase.replicasTitle', { @@ -177,9 +177,9 @@ export const WarmPhase: FunctionComponent = () => { }, }} /> - </DescribedFormField> + </DescribedFormRow> {!isUsingSearchableSnapshotInHotPhase && ( - <DescribedFormField + <DescribedFormRow title={ <h3> <FormattedMessage @@ -225,7 +225,7 @@ export const WarmPhase: FunctionComponent = () => { </EuiFlexGroup> <EuiSpacer /> </div> - </DescribedFormField> + </DescribedFormRow> )} {!isUsingSearchableSnapshotInHotPhase && <ForcemergeField phase="warm" />} diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/toggleable_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/toggleable_field.tsx deleted file mode 100644 index fb5e636902780..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/toggleable_field.tsx +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * 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, { FunctionComponent, useState } from 'react'; -import { EuiSpacer, EuiSwitch, EuiSwitchProps } from '@elastic/eui'; - -export interface Props extends Omit<EuiSwitchProps, 'checked' | 'onChange'> { - children: (() => JSX.Element) | JSX.Element | JSX.Element[] | undefined; - checked?: boolean; - initialValue?: boolean; - onChange?: (nextValue: boolean) => void; -} - -export const ToggleableField: FunctionComponent<Props> = ({ - initialValue, - checked, - onChange, - children, - ...restProps -}) => { - const [uncontrolledIsContentVisible, setUncontrolledIsContentVisible] = useState<boolean>( - initialValue ?? false - ); - - const isContentVisible = Boolean(checked ?? uncontrolledIsContentVisible); - - return ( - <> - <EuiSwitch - {...restProps} - checked={isContentVisible} - onChange={(e) => { - const nextValue = e.target.checked; - setUncontrolledIsContentVisible(nextValue); - if (onChange) { - onChange(nextValue); - } - }} - /> - <EuiSpacer size="m" /> - {isContentVisible ? (typeof children === 'function' ? children() : children) : null} - </> - ); -};