[ILM] Reposition form toggles (#85143)

* merged toggleable field and described form component and moved toggle to left

* moved all toggles to left, renamed DescribedFormField -> DescribedFormRow and added new ToggleFieldWithDescribedFormRow component

* added new prop fieldNotices to render callouts in correct position on the left
This commit is contained in:
Jean-Louis Leysens 2020-12-08 08:47:26 +01:00 committed by GitHub
parent 213b934386
commit fac1f1204d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 179 additions and 128 deletions

View file

@ -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>
);
};

View file

@ -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>
);
};

View file

@ -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';

View file

@ -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>
);

View file

@ -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';

View file

@ -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

View file

@ -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" />

View file

@ -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>
);
};

View file

@ -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>
);
};

View file

@ -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" />}

View file

@ -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}
</>
);
};