diff --git a/awx/ui_next/src/components/DetailList/ArrayDetail.jsx b/awx/ui_next/src/components/DetailList/ArrayDetail.jsx
new file mode 100644
index 0000000000..a946a5976f
--- /dev/null
+++ b/awx/ui_next/src/components/DetailList/ArrayDetail.jsx
@@ -0,0 +1,35 @@
+import 'styled-components/macro';
+import React from 'react';
+import styled from 'styled-components';
+import { TextListItemVariants } from '@patternfly/react-core';
+import { DetailName, DetailValue } from './Detail';
+
+const Value = styled(DetailValue)`
+ margin-top: var(--pf-global--spacer--xs);
+ padding: var(--pf-global--spacer--xs);
+ border: 1px solid var(--pf-global--BorderColor--100);
+ max-height: 5.5em;
+ overflow: auto;
+`;
+
+function ArrayDetail({ label, value, dataCy }) {
+ const labelCy = dataCy ? `${dataCy}-label` : null;
+ const valueCy = dataCy ? `${dataCy}-value` : null;
+
+ const vals = Array.isArray(value) ? value : [value];
+
+ return (
+
+
+ {label}
+
+
+ {vals.map(v => (
+ {v}
+ ))}
+
+
+ );
+}
+
+export default ArrayDetail;
diff --git a/awx/ui_next/src/components/DetailList/ObjectDetail.jsx b/awx/ui_next/src/components/DetailList/CodeDetail.jsx
similarity index 77%
rename from awx/ui_next/src/components/DetailList/ObjectDetail.jsx
rename to awx/ui_next/src/components/DetailList/CodeDetail.jsx
index bf008866a8..9b9a2f0568 100644
--- a/awx/ui_next/src/components/DetailList/ObjectDetail.jsx
+++ b/awx/ui_next/src/components/DetailList/CodeDetail.jsx
@@ -1,11 +1,11 @@
import 'styled-components/macro';
import React from 'react';
-import { shape, node, number } from 'prop-types';
+import { shape, node, number, oneOf } from 'prop-types';
import { TextListItemVariants } from '@patternfly/react-core';
import { DetailName, DetailValue } from './Detail';
import CodeMirrorInput from '../CodeMirrorInput';
-function ObjectDetail({ value, label, rows, fullHeight }) {
+function CodeDetail({ value, label, mode, rows, fullHeight }) {
return (
<>
);
}
-ObjectDetail.propTypes = {
+CodeDetail.propTypes = {
value: shape.isRequired,
label: node.isRequired,
rows: number,
+ mode: oneOf(['json', 'yaml', 'jinja2']).isRequired,
};
-ObjectDetail.defaultProps = {
+CodeDetail.defaultProps = {
rows: null,
};
-export default ObjectDetail;
+export default CodeDetail;
diff --git a/awx/ui_next/src/components/DetailList/DetailList.jsx b/awx/ui_next/src/components/DetailList/DetailList.jsx
index 59185b0931..85bde3494a 100644
--- a/awx/ui_next/src/components/DetailList/DetailList.jsx
+++ b/awx/ui_next/src/components/DetailList/DetailList.jsx
@@ -11,7 +11,7 @@ const DetailList = ({ children, stacked, ...props }) => (
export default styled(DetailList)`
display: grid;
grid-gap: 20px;
- align-items: center;
+ align-items: start;
${props =>
props.stacked
? `
diff --git a/awx/ui_next/src/components/DetailList/index.js b/awx/ui_next/src/components/DetailList/index.js
index 8bebb27ce4..d5e2ccd8a4 100644
--- a/awx/ui_next/src/components/DetailList/index.js
+++ b/awx/ui_next/src/components/DetailList/index.js
@@ -3,8 +3,9 @@ export { default as Detail, DetailName, DetailValue } from './Detail';
export { default as DeletedDetail } from './DeletedDetail';
export { default as UserDateDetail } from './UserDateDetail';
export { default as DetailBadge } from './DetailBadge';
+export { default as ArrayDetail } from './ArrayDetail';
/*
- NOTE: ObjectDetail cannot be imported here, as it causes circular
+ NOTE: CodeDetail cannot be imported here, as it causes circular
dependencies in testing environment. Import it directly from
DetailList/ObjectDetail
*/
diff --git a/awx/ui_next/src/components/ResourceAccessList/__snapshots__/ResourceAccessListItem.test.jsx.snap b/awx/ui_next/src/components/ResourceAccessList/__snapshots__/ResourceAccessListItem.test.jsx.snap
index 7c8126fb4d..d9bb8bed4e 100644
--- a/awx/ui_next/src/components/ResourceAccessList/__snapshots__/ResourceAccessListItem.test.jsx.snap
+++ b/awx/ui_next/src/components/ResourceAccessList/__snapshots__/ResourceAccessListItem.test.jsx.snap
@@ -407,12 +407,12 @@ exports[` initially renders succesfully 1`] = `
"componentStyle": ComponentStyle {
"componentId": "sc-bwzfXH",
"isStatic": false,
- "lastClassName": "kVCDmm",
+ "lastClassName": "gAzXep",
"rules": Array [
"
display: grid;
grid-gap: 20px;
- align-items: center;
+ align-items: start;
",
[Function],
"
@@ -433,15 +433,15 @@ exports[` initially renders succesfully 1`] = `
stacked={true}
>
initially renders succesfully 1`] = `
"componentStyle": ComponentStyle {
"componentId": "sc-bwzfXH",
"isStatic": false,
- "lastClassName": "kVCDmm",
+ "lastClassName": "gAzXep",
"rules": Array [
"
display: grid;
grid-gap: 20px;
- align-items: center;
+ align-items: start;
",
[Function],
"
@@ -656,15 +656,15 @@ exports[` initially renders succesfully 1`] = `
stacked={true}
>
diff --git a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateDetail/NotificationTemplateDetail.jsx b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateDetail/NotificationTemplateDetail.jsx
index a70f691e02..5761e737a3 100644
--- a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateDetail/NotificationTemplateDetail.jsx
+++ b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateDetail/NotificationTemplateDetail.jsx
@@ -7,22 +7,25 @@ import AlertModal from '../../../components/AlertModal';
import { CardBody, CardActionsRow } from '../../../components/Card';
import {
Detail,
+ ArrayDetail,
DetailList,
DeletedDetail,
} from '../../../components/DetailList';
-import ObjectDetail from '../../../components/DetailList/ObjectDetail';
+import CodeDetail from '../../../components/DetailList/CodeDetail';
import DeleteButton from '../../../components/DeleteButton';
import ErrorDetail from '../../../components/ErrorDetail';
import { NotificationTemplatesAPI } from '../../../api';
import useRequest, { useDismissableError } from '../../../util/useRequest';
+import hasCustomMessages from '../shared/hasCustomMessages';
import { NOTIFICATION_TYPES } from '../constants';
-function NotificationTemplateDetail({ i18n, template }) {
+function NotificationTemplateDetail({ i18n, template, defaultMessages }) {
const history = useHistory();
const {
notification_configuration: configuration,
summary_fields,
+ messages,
} = template;
const { request: deleteTemplate, isLoading, error: deleteError } = useRequest(
@@ -33,6 +36,7 @@ function NotificationTemplateDetail({ i18n, template }) {
);
const { error, dismissError } = useDismissableError(deleteError);
+ const typeMessageDefaults = defaultMessages[template.notification_type];
return (
@@ -81,9 +85,9 @@ function NotificationTemplateDetail({ i18n, template }) {
value={configuration.host}
dataCy="nt-detail-host"
/>
-
-
-
-
-
-
>
)}
+ {hasCustomMessages(messages, typeMessageDefaults) && (
+
+ )}
{summary_fields.user_capabilities &&
@@ -358,4 +371,164 @@ function NotificationTemplateDetail({ i18n, template }) {
);
}
+function CustomMessageDetails({ messages, defaults, type, i18n }) {
+ const showMessages = type !== 'webhook';
+ const showBodies = ['email', 'pagerduty', 'webhook'].includes(type);
+
+ return (
+ <>
+ {showMessages && (
+
+ )}
+ {showBodies && (
+
+ )}
+ {showMessages && (
+
+ )}
+ {showBodies && (
+
+ )}
+ {showMessages && (
+
+ )}
+ {showBodies && (
+
+ )}
+ {showMessages && (
+
+ )}
+ {showBodies && (
+
+ )}
+ {showMessages && (
+
+ )}
+ {showBodies && (
+
+ )}
+ {showMessages && (
+
+ )}
+ {showBodies && (
+
+ )}
+ {showMessages && (
+
+ )}
+ {showBodies && (
+
+ )}
+ >
+ );
+}
+
export default withI18n()(NotificationTemplateDetail);
diff --git a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateList.jsx b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateList.jsx
index 3dac16f6a2..8b2b721b04 100644
--- a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateList.jsx
+++ b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateList.jsx
@@ -124,7 +124,7 @@ function NotificationTemplatesList({ i18n }) {
{...props}
showSelectAll
isAllSelected={isAllSelected}
- onSelectAll={() => setSelected([...templates])}
+ onSelectAll={set => setSelected(set ? [...templates] : [])}
qsConfig={QS_CONFIG}
additionalControls={[
...(canAdd
diff --git a/awx/ui_next/src/screens/NotificationTemplate/shared/NotificationTemplateForm.jsx b/awx/ui_next/src/screens/NotificationTemplate/shared/NotificationTemplateForm.jsx
index 50477fabeb..bc668d7686 100644
--- a/awx/ui_next/src/screens/NotificationTemplate/shared/NotificationTemplateForm.jsx
+++ b/awx/ui_next/src/screens/NotificationTemplate/shared/NotificationTemplateForm.jsx
@@ -13,6 +13,7 @@ import { required } from '../../../util/validators';
import { FormColumnLayout } from '../../../components/FormLayout';
import TypeInputsSubForm from './TypeInputsSubForm';
import CustomMessagesSubForm from './CustomMessagesSubForm';
+import hasCustomMessages from './hasCustomMessages';
import typeFieldNames, { initialConfigValues } from './typeFieldNames';
function NotificationTemplateFormFields({ i18n, defaultMessages }) {
@@ -117,8 +118,8 @@ function NotificationTemplateForm({
const defs = defaultMessages[template.notification_type || 'email'];
const mergeDefaultMessages = (templ = {}, def) => {
return {
- message: templ.message || def.message || '',
- body: templ.body || def.body || '',
+ message: templ?.message || def.message || '',
+ body: templ?.body || def.body || '',
};
};
@@ -144,25 +145,25 @@ function NotificationTemplateForm({
workflow_approval: {
approved: {
...mergeDefaultMessages(
- messages.workflow_approval.approved,
+ messages.workflow_approval?.approved,
defs.workflow_approval.approved
),
},
denied: {
...mergeDefaultMessages(
- messages.workflow_approval.denied,
+ messages.workflow_approval?.denied,
defs.workflow_approval.denied
),
},
running: {
...mergeDefaultMessages(
- messages.workflow_approval.running,
+ messages.workflow_approval?.running,
defs.workflow_approval.running
),
},
timed_out: {
...mergeDefaultMessages(
- messages.workflow_approval.timed_out,
+ messages.workflow_approval?.timed_out,
defs.workflow_approval.timed_out
),
},
@@ -210,42 +211,6 @@ NotificationTemplateForm.defaultProps = {
export default withI18n()(NotificationTemplateForm);
-function hasCustomMessages(messages, defaults) {
- return (
- isCustomized(messages.started, defaults.started) ||
- isCustomized(messages.success, defaults.success) ||
- isCustomized(messages.error, defaults.error) ||
- isCustomized(
- messages.workflow_approval.approved,
- defaults.workflow_approval.approved
- ) ||
- isCustomized(
- messages.workflow_approval.denied,
- defaults.workflow_approval.denied
- ) ||
- isCustomized(
- messages.workflow_approval.running,
- defaults.workflow_approval.running
- ) ||
- isCustomized(
- messages.workflow_approval.timed_out,
- defaults.workflow_approval.timed_out
- )
- );
-}
-function isCustomized(message, defaultMessage) {
- if (!message) {
- return false;
- }
- if (message.message && message.message !== defaultMessage.message) {
- return true;
- }
- if (message.body && message.body !== defaultMessage.body) {
- return true;
- }
- return false;
-}
-
function normalizeFields(values, defaultMessages) {
return normalizeTypeFields(normalizeMessageFields(values, defaultMessages));
}
diff --git a/awx/ui_next/src/screens/NotificationTemplate/shared/hasCustomMessages.js b/awx/ui_next/src/screens/NotificationTemplate/shared/hasCustomMessages.js
new file mode 100644
index 0000000000..418adaf852
--- /dev/null
+++ b/awx/ui_next/src/screens/NotificationTemplate/shared/hasCustomMessages.js
@@ -0,0 +1,40 @@
+export default function hasCustomMessages(messages, defaults) {
+ if (!messages) {
+ return false;
+ }
+
+ return (
+ isCustomized(messages.started, defaults.started) ||
+ isCustomized(messages.success, defaults.success) ||
+ isCustomized(messages.error, defaults.error) ||
+ isCustomized(
+ messages.workflow_approval?.approved,
+ defaults.workflow_approval.approved
+ ) ||
+ isCustomized(
+ messages.workflow_approval?.denied,
+ defaults.workflow_approval.denied
+ ) ||
+ isCustomized(
+ messages.workflow_approval?.running,
+ defaults.workflow_approval.running
+ ) ||
+ isCustomized(
+ messages.workflow_approval?.timed_out,
+ defaults.workflow_approval.timed_out
+ )
+ );
+}
+
+function isCustomized(message, defaultMessage) {
+ if (!message) {
+ return false;
+ }
+ if (message.message && message.message !== defaultMessage.message) {
+ return true;
+ }
+ if (message.body && message.body !== defaultMessage.body) {
+ return true;
+ }
+ return false;
+}