mirror of
https://github.com/ansible/awx.git
synced 2026-05-08 01:47:35 -02:30
Do not render setting field if config is empty
This commit is contained in:
@@ -68,12 +68,24 @@ describe('<ActivityStreamEdit />', () => {
|
|||||||
|
|
||||||
test('should successfully send request to api on form submission', async () => {
|
test('should successfully send request to api on form submission', async () => {
|
||||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||||
|
expect(
|
||||||
|
wrapper.find('Switch#ACTIVITY_STREAM_ENABLED').prop('isChecked')
|
||||||
|
).toEqual(false);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('Switch#ACTIVITY_STREAM_ENABLED').invoke('onChange')(true);
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(
|
||||||
|
wrapper.find('Switch#ACTIVITY_STREAM_ENABLED').prop('isChecked')
|
||||||
|
).toEqual(true);
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper.find('Form').invoke('onSubmit')();
|
wrapper.find('Form').invoke('onSubmit')();
|
||||||
});
|
});
|
||||||
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||||
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
||||||
ACTIVITY_STREAM_ENABLED: false,
|
ACTIVITY_STREAM_ENABLED: true,
|
||||||
ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC: true,
|
ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
InputField,
|
InputField,
|
||||||
ObjectField,
|
ObjectField,
|
||||||
} from '../../shared/SharedFields';
|
} from '../../shared/SharedFields';
|
||||||
|
import { formatJson } from '../../shared/settingUtils';
|
||||||
import useModal from '../../../../util/useModal';
|
import useModal from '../../../../util/useModal';
|
||||||
import useRequest from '../../../../util/useRequest';
|
import useRequest from '../../../../util/useRequest';
|
||||||
import { SettingsAPI } from '../../../../api';
|
import { SettingsAPI } from '../../../../api';
|
||||||
@@ -57,10 +58,10 @@ function AzureADEdit() {
|
|||||||
const handleSubmit = async form => {
|
const handleSubmit = async form => {
|
||||||
await submitForm({
|
await submitForm({
|
||||||
...form,
|
...form,
|
||||||
SOCIAL_AUTH_AZUREAD_OAUTH2_TEAM_MAP: JSON.parse(
|
SOCIAL_AUTH_AZUREAD_OAUTH2_TEAM_MAP: formatJson(
|
||||||
form.SOCIAL_AUTH_AZUREAD_OAUTH2_TEAM_MAP
|
form.SOCIAL_AUTH_AZUREAD_OAUTH2_TEAM_MAP
|
||||||
),
|
),
|
||||||
SOCIAL_AUTH_AZUREAD_OAUTH2_ORGANIZATION_MAP: JSON.parse(
|
SOCIAL_AUTH_AZUREAD_OAUTH2_ORGANIZATION_MAP: formatJson(
|
||||||
form.SOCIAL_AUTH_AZUREAD_OAUTH2_ORGANIZATION_MAP
|
form.SOCIAL_AUTH_AZUREAD_OAUTH2_ORGANIZATION_MAP
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
} from '../../shared';
|
} from '../../shared';
|
||||||
import useModal from '../../../../util/useModal';
|
import useModal from '../../../../util/useModal';
|
||||||
import useRequest, { useDismissableError } from '../../../../util/useRequest';
|
import useRequest, { useDismissableError } from '../../../../util/useRequest';
|
||||||
|
import { formatJson } from '../../shared/settingUtils';
|
||||||
import { SettingsAPI } from '../../../../api';
|
import { SettingsAPI } from '../../../../api';
|
||||||
|
|
||||||
function LoggingEdit({ i18n }) {
|
function LoggingEdit({ i18n }) {
|
||||||
@@ -39,6 +40,9 @@ function LoggingEdit({ i18n }) {
|
|||||||
const { data } = await SettingsAPI.readCategory('logging');
|
const { data } = await SettingsAPI.readCategory('logging');
|
||||||
const mergedData = {};
|
const mergedData = {};
|
||||||
Object.keys(data).forEach(key => {
|
Object.keys(data).forEach(key => {
|
||||||
|
if (!options[key]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
mergedData[key] = options[key];
|
mergedData[key] = options[key];
|
||||||
mergedData[key].value = data[key];
|
mergedData[key].value = data[key];
|
||||||
});
|
});
|
||||||
@@ -65,7 +69,7 @@ function LoggingEdit({ i18n }) {
|
|||||||
const handleSubmit = async form => {
|
const handleSubmit = async form => {
|
||||||
await submitForm({
|
await submitForm({
|
||||||
...form,
|
...form,
|
||||||
LOG_AGGREGATOR_LOGGERS: JSON.parse(form.LOG_AGGREGATOR_LOGGERS),
|
LOG_AGGREGATOR_LOGGERS: formatJson(form.LOG_AGGREGATOR_LOGGERS),
|
||||||
LOG_AGGREGATOR_HOST: form.LOG_AGGREGATOR_HOST || null,
|
LOG_AGGREGATOR_HOST: form.LOG_AGGREGATOR_HOST || null,
|
||||||
LOG_AGGREGATOR_TYPE: form.LOG_AGGREGATOR_TYPE || null,
|
LOG_AGGREGATOR_TYPE: form.LOG_AGGREGATOR_TYPE || null,
|
||||||
});
|
});
|
||||||
@@ -127,10 +131,7 @@ function LoggingEdit({ i18n }) {
|
|||||||
{isLoading && <ContentLoading />}
|
{isLoading && <ContentLoading />}
|
||||||
{!isLoading && error && <ContentError error={error} />}
|
{!isLoading && error && <ContentError error={error} />}
|
||||||
{!isLoading && logging && (
|
{!isLoading && logging && (
|
||||||
<Formik
|
<Formik initialValues={initialValues(logging)} onSubmit={handleSubmit}>
|
||||||
initialValues={{ ...initialValues(logging) }}
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
>
|
|
||||||
{formik => {
|
{formik => {
|
||||||
return (
|
return (
|
||||||
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import {
|
|||||||
import useModal from '../../../../util/useModal';
|
import useModal from '../../../../util/useModal';
|
||||||
import useRequest from '../../../../util/useRequest';
|
import useRequest from '../../../../util/useRequest';
|
||||||
import { SettingsAPI } from '../../../../api';
|
import { SettingsAPI } from '../../../../api';
|
||||||
import { pluck } from '../../shared/settingUtils';
|
import { pluck, formatJson } from '../../shared/settingUtils';
|
||||||
|
|
||||||
function MiscSystemEdit({ i18n }) {
|
function MiscSystemEdit({ i18n }) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
@@ -95,6 +95,9 @@ function MiscSystemEdit({ i18n }) {
|
|||||||
|
|
||||||
const mergedData = {};
|
const mergedData = {};
|
||||||
Object.keys(systemData).forEach(key => {
|
Object.keys(systemData).forEach(key => {
|
||||||
|
if (!systemOptions[key]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
mergedData[key] = systemOptions[key];
|
mergedData[key] = systemOptions[key];
|
||||||
mergedData[key].value = systemData[key];
|
mergedData[key].value = systemData[key];
|
||||||
});
|
});
|
||||||
@@ -127,8 +130,8 @@ function MiscSystemEdit({ i18n }) {
|
|||||||
} = form;
|
} = form;
|
||||||
await submitForm({
|
await submitForm({
|
||||||
...formData,
|
...formData,
|
||||||
CUSTOM_VENV_PATHS: JSON.parse(formData.CUSTOM_VENV_PATHS),
|
CUSTOM_VENV_PATHS: formatJson(formData.CUSTOM_VENV_PATHS),
|
||||||
REMOTE_HOST_HEADERS: JSON.parse(formData.REMOTE_HOST_HEADERS),
|
REMOTE_HOST_HEADERS: formatJson(formData.REMOTE_HOST_HEADERS),
|
||||||
OAUTH2_PROVIDER: {
|
OAUTH2_PROVIDER: {
|
||||||
ACCESS_TOKEN_EXPIRE_SECONDS,
|
ACCESS_TOKEN_EXPIRE_SECONDS,
|
||||||
REFRESH_TOKEN_EXPIRE_SECONDS,
|
REFRESH_TOKEN_EXPIRE_SECONDS,
|
||||||
@@ -181,10 +184,7 @@ function MiscSystemEdit({ i18n }) {
|
|||||||
{isLoading && <ContentLoading />}
|
{isLoading && <ContentLoading />}
|
||||||
{!isLoading && error && <ContentError error={error} />}
|
{!isLoading && error && <ContentError error={error} />}
|
||||||
{!isLoading && system && (
|
{!isLoading && system && (
|
||||||
<Formik
|
<Formik initialValues={initialValues(system)} onSubmit={handleSubmit}>
|
||||||
initialValues={{ ...initialValues(system) }}
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
>
|
|
||||||
{formik => {
|
{formik => {
|
||||||
return (
|
return (
|
||||||
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ function RevertButton({ i18n, id, defaultValue, isDisabled = false }) {
|
|||||||
<ButtonWrapper>
|
<ButtonWrapper>
|
||||||
<Button
|
<Button
|
||||||
aria-label={isRevertable ? i18n._(t`Revert`) : i18n._(t`Undo`)}
|
aria-label={isRevertable ? i18n._(t`Revert`) : i18n._(t`Undo`)}
|
||||||
|
data-cy={`${id}-revert`}
|
||||||
isInline
|
isInline
|
||||||
isSmall
|
isSmall
|
||||||
onClick={handleConfirm}
|
onClick={handleConfirm}
|
||||||
|
|||||||
@@ -44,39 +44,36 @@ const SettingGroup = withI18n()(
|
|||||||
label,
|
label,
|
||||||
popoverContent,
|
popoverContent,
|
||||||
validated,
|
validated,
|
||||||
}) => {
|
}) => (
|
||||||
return (
|
<FormGroup
|
||||||
<FormGroup
|
fieldId={fieldId}
|
||||||
fieldId={fieldId}
|
helperTextInvalid={helperTextInvalid}
|
||||||
helperTextInvalid={helperTextInvalid}
|
isRequired={isRequired}
|
||||||
isRequired={isRequired}
|
label={label}
|
||||||
label={label}
|
validated={validated}
|
||||||
validated={validated}
|
labelIcon={
|
||||||
labelIcon={
|
<>
|
||||||
<>
|
<Popover
|
||||||
<Popover
|
content={popoverContent}
|
||||||
content={popoverContent}
|
ariaLabel={`${i18n._(t`More information for`)} ${label}`}
|
||||||
ariaLabel={`${i18n._(t`More information for`)} ${label}`}
|
/>
|
||||||
/>
|
<RevertButton
|
||||||
<RevertButton
|
id={fieldId}
|
||||||
id={fieldId}
|
defaultValue={defaultValue}
|
||||||
defaultValue={defaultValue}
|
isDisabled={isDisabled}
|
||||||
isDisabled={isDisabled}
|
/>
|
||||||
/>
|
</>
|
||||||
</>
|
}
|
||||||
}
|
>
|
||||||
>
|
{children}
|
||||||
{children}
|
</FormGroup>
|
||||||
</FormGroup>
|
)
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const BooleanField = withI18n()(
|
const BooleanField = withI18n()(
|
||||||
({ i18n, ariaLabel = '', name, config, disabled = false }) => {
|
({ i18n, ariaLabel = '', name, config, disabled = false }) => {
|
||||||
const [field, meta, helpers] = useField(name);
|
const [field, meta, helpers] = useField(name);
|
||||||
|
return config ? (
|
||||||
return (
|
|
||||||
<SettingGroup
|
<SettingGroup
|
||||||
defaultValue={config.default ?? false}
|
defaultValue={config.default ?? false}
|
||||||
fieldId={name}
|
fieldId={name}
|
||||||
@@ -95,7 +92,7 @@ const BooleanField = withI18n()(
|
|||||||
aria-label={ariaLabel || config.label}
|
aria-label={ariaLabel || config.label}
|
||||||
/>
|
/>
|
||||||
</SettingGroup>
|
</SettingGroup>
|
||||||
);
|
) : null;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
BooleanField.propTypes = {
|
BooleanField.propTypes = {
|
||||||
@@ -110,7 +107,7 @@ const ChoiceField = withI18n()(({ i18n, name, config, isRequired = false }) => {
|
|||||||
const [field, meta] = useField({ name, validate });
|
const [field, meta] = useField({ name, validate });
|
||||||
const isValid = !meta.error || !meta.touched;
|
const isValid = !meta.error || !meta.touched;
|
||||||
|
|
||||||
return (
|
return config ? (
|
||||||
<SettingGroup
|
<SettingGroup
|
||||||
defaultValue={config.default ?? ''}
|
defaultValue={config.default ?? ''}
|
||||||
fieldId={name}
|
fieldId={name}
|
||||||
@@ -132,7 +129,7 @@ const ChoiceField = withI18n()(({ i18n, name, config, isRequired = false }) => {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</SettingGroup>
|
</SettingGroup>
|
||||||
);
|
) : null;
|
||||||
});
|
});
|
||||||
ChoiceField.propTypes = {
|
ChoiceField.propTypes = {
|
||||||
name: string.isRequired,
|
name: string.isRequired,
|
||||||
@@ -146,7 +143,7 @@ const EncryptedField = withI18n()(
|
|||||||
const [, meta] = useField({ name, validate });
|
const [, meta] = useField({ name, validate });
|
||||||
const isValid = !(meta.touched && meta.error);
|
const isValid = !(meta.touched && meta.error);
|
||||||
|
|
||||||
return (
|
return config ? (
|
||||||
<SettingGroup
|
<SettingGroup
|
||||||
defaultValue={config.default ?? ''}
|
defaultValue={config.default ?? ''}
|
||||||
fieldId={name}
|
fieldId={name}
|
||||||
@@ -166,7 +163,7 @@ const EncryptedField = withI18n()(
|
|||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</SettingGroup>
|
</SettingGroup>
|
||||||
);
|
) : null;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
EncryptedField.propTypes = {
|
EncryptedField.propTypes = {
|
||||||
@@ -177,10 +174,8 @@ EncryptedField.propTypes = {
|
|||||||
|
|
||||||
const InputField = withI18n()(
|
const InputField = withI18n()(
|
||||||
({ i18n, name, config, type = 'text', isRequired = false }) => {
|
({ i18n, name, config, type = 'text', isRequired = false }) => {
|
||||||
const {
|
const min_value = config?.min_value ?? Number.MIN_SAFE_INTEGER;
|
||||||
min_value = Number.MIN_SAFE_INTEGER,
|
const max_value = config?.max_value ?? Number.MAX_SAFE_INTEGER;
|
||||||
max_value = Number.MAX_SAFE_INTEGER,
|
|
||||||
} = config;
|
|
||||||
const validators = [
|
const validators = [
|
||||||
...(isRequired ? [required(null, i18n)] : []),
|
...(isRequired ? [required(null, i18n)] : []),
|
||||||
...(type === 'url' ? [url(i18n)] : []),
|
...(type === 'url' ? [url(i18n)] : []),
|
||||||
@@ -191,7 +186,7 @@ const InputField = withI18n()(
|
|||||||
const [field, meta] = useField({ name, validate: combine(validators) });
|
const [field, meta] = useField({ name, validate: combine(validators) });
|
||||||
const isValid = !(meta.touched && meta.error);
|
const isValid = !(meta.touched && meta.error);
|
||||||
|
|
||||||
return (
|
return config ? (
|
||||||
<SettingGroup
|
<SettingGroup
|
||||||
defaultValue={config.default || ''}
|
defaultValue={config.default || ''}
|
||||||
fieldId={name}
|
fieldId={name}
|
||||||
@@ -213,7 +208,7 @@ const InputField = withI18n()(
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</SettingGroup>
|
</SettingGroup>
|
||||||
);
|
) : null;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
InputField.propTypes = {
|
InputField.propTypes = {
|
||||||
@@ -228,12 +223,12 @@ const ObjectField = withI18n()(({ i18n, name, config, isRequired = false }) => {
|
|||||||
const [field, meta, helpers] = useField({ name, validate });
|
const [field, meta, helpers] = useField({ name, validate });
|
||||||
const isValid = !(meta.touched && meta.error);
|
const isValid = !(meta.touched && meta.error);
|
||||||
|
|
||||||
const emptyDefault = config.type === 'list' ? '[]' : '{}';
|
const emptyDefault = config?.type === 'list' ? '[]' : '{}';
|
||||||
const defaultRevertValue = config.default
|
const defaultRevertValue = config?.default
|
||||||
? JSON.stringify(config.default, null, 2)
|
? JSON.stringify(config.default, null, 2)
|
||||||
: emptyDefault;
|
: emptyDefault;
|
||||||
|
|
||||||
return (
|
return config ? (
|
||||||
<FormFullWidthLayout>
|
<FormFullWidthLayout>
|
||||||
<SettingGroup
|
<SettingGroup
|
||||||
defaultValue={defaultRevertValue}
|
defaultValue={defaultRevertValue}
|
||||||
@@ -254,7 +249,7 @@ const ObjectField = withI18n()(({ i18n, name, config, isRequired = false }) => {
|
|||||||
/>
|
/>
|
||||||
</SettingGroup>
|
</SettingGroup>
|
||||||
</FormFullWidthLayout>
|
</FormFullWidthLayout>
|
||||||
);
|
) : null;
|
||||||
});
|
});
|
||||||
ObjectField.propTypes = {
|
ObjectField.propTypes = {
|
||||||
name: string.isRequired,
|
name: string.isRequired,
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { isJsonString } from '../../../util/yaml';
|
||||||
|
|
||||||
export function sortNestedDetails(obj = {}) {
|
export function sortNestedDetails(obj = {}) {
|
||||||
const nestedTypes = ['nested object', 'list', 'boolean'];
|
const nestedTypes = ['nested object', 'list', 'boolean'];
|
||||||
const notNested = Object.entries(obj).filter(
|
const notNested = Object.entries(obj).filter(
|
||||||
@@ -18,3 +20,10 @@ export function sortNestedDetails(obj = {}) {
|
|||||||
export function pluck(sourceObject, ...keys) {
|
export function pluck(sourceObject, ...keys) {
|
||||||
return Object.assign({}, ...keys.map(key => ({ [key]: sourceObject[key] })));
|
return Object.assign({}, ...keys.map(key => ({ [key]: sourceObject[key] })));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formatJson(jsonString) {
|
||||||
|
if (!jsonString) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return isJsonString(jsonString) ? JSON.parse(jsonString) : jsonString;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user