diff --git a/__tests__/components/CapitalizeText.test.jsx b/__tests__/components/CapitalizeText.test.jsx
new file mode 100644
index 0000000000..30e3fcb7ce
--- /dev/null
+++ b/__tests__/components/CapitalizeText.test.jsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import CapitalizeText from '../../src/components/CapitalizeText';
+
+describe('', () => {
+ let capitalizeTextWrapper;
+
+ test('initially renders without crashing', () => {
+ capitalizeTextWrapper = mount(
+
+ );
+ expect(capitalizeTextWrapper.length).toBe(1);
+ expect(capitalizeTextWrapper.text()).toEqual('Foo');
+ capitalizeTextWrapper.unmount();
+ });
+});
diff --git a/__tests__/components/DataListToolbar.test.jsx b/__tests__/components/DataListToolbar.test.jsx
index facc3b1128..958573e2f1 100644
--- a/__tests__/components/DataListToolbar.test.jsx
+++ b/__tests__/components/DataListToolbar.test.jsx
@@ -36,6 +36,7 @@ describe('', () => {
onSearch={onSearch}
onSort={onSort}
onSelectAll={onSelectAll}
+ showSelectAll
/>
);
diff --git a/__tests__/components/NotificationList.test.jsx b/__tests__/components/NotificationList.test.jsx
new file mode 100644
index 0000000000..bbe6a912dc
--- /dev/null
+++ b/__tests__/components/NotificationList.test.jsx
@@ -0,0 +1,160 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import { MemoryRouter } from 'react-router-dom';
+import { I18nProvider } from '@lingui/react';
+import Notifications from '../../src/components/NotificationsList/Notifications.list';
+
+describe('', () => {
+ test('initially renders succesfully', () => {
+ mount(
+
+
+
+
+
+ );
+ });
+ test('fetches notifications on mount', () => {
+ const spy = jest.spyOn(Notifications.prototype, 'fetchNotifications');
+ mount(
+
+
+
+
+
+ );
+ expect(spy).toHaveBeenCalled();
+ });
+ test('toggle success calls post', () => {
+ const spy = jest.spyOn(Notifications.prototype, 'postToSuccess');
+ const wrapper = mount(
+
+
+
+
+
+ ).find('Notifications');
+ wrapper.instance().toggleSuccess(1, true);
+ expect(spy).toHaveBeenCalledWith(1, true);
+ });
+ test('post success makes request and updates state properly', async () => {
+ const postSuccessFn = jest.fn();
+ const wrapper = mount(
+
+
+
+
+
+ ).find('Notifications');
+ wrapper.setState({ successTemplateIds: [44] });
+ await wrapper.instance().postToSuccess(44, true);
+ expect(postSuccessFn).toHaveBeenCalledWith(1, { id: 44, disassociate: true });
+ expect(wrapper.state('successTemplateIds')).not.toContain(44);
+ await wrapper.instance().postToSuccess(44, false);
+ expect(postSuccessFn).toHaveBeenCalledWith(1, { id: 44 });
+ expect(wrapper.state('successTemplateIds')).toContain(44);
+ });
+ test('toggle error calls post', () => {
+ const spy = jest.spyOn(Notifications.prototype, 'postToError');
+ const wrapper = mount(
+
+
+
+
+
+ ).find('Notifications');
+ wrapper.instance().toggleError(1, true);
+ expect(spy).toHaveBeenCalledWith(1, true);
+ });
+ test('post error makes request and updates state properly', async () => {
+ const postErrorFn = jest.fn();
+ const wrapper = mount(
+
+
+
+
+
+ ).find('Notifications');
+ wrapper.setState({ errorTemplateIds: [44] });
+ await wrapper.instance().postToError(44, true);
+ expect(postErrorFn).toHaveBeenCalledWith(1, { id: 44, disassociate: true });
+ expect(wrapper.state('errorTemplateIds')).not.toContain(44);
+ await wrapper.instance().postToError(44, false);
+ expect(postErrorFn).toHaveBeenCalledWith(1, { id: 44 });
+ expect(wrapper.state('errorTemplateIds')).toContain(44);
+ });
+ test('fetchNotifications', async () => {
+ const mockQueryParams = {
+ page: 44,
+ page_size: 10,
+ order_by: 'name'
+ };
+ const getNotificationsFn = jest.fn().mockResolvedValue({
+ data: {
+ results: [
+ { id: 1 },
+ { id: 2 },
+ { id: 3 }
+ ]
+ }
+ });
+ const getSuccessFn = jest.fn().mockResolvedValue({
+ data: {
+ results: [
+ { id: 1 }
+ ]
+ }
+ });
+ const getErrorFn = jest.fn().mockResolvedValue({
+ data: {
+ results: [
+ { id: 2 }
+ ]
+ }
+ });
+ const wrapper = mount(
+
+
+
+
+
+ ).find('Notifications');
+ wrapper.instance().updateUrl = jest.fn();
+ await wrapper.instance().fetchNotifications(mockQueryParams);
+ expect(getNotificationsFn).toHaveBeenCalledWith(1, mockQueryParams);
+ expect(getSuccessFn).toHaveBeenCalledWith(1, {
+ id__in: '1,2,3'
+ });
+ expect(getErrorFn).toHaveBeenCalledWith(1, {
+ id__in: '1,2,3'
+ });
+ expect(wrapper.state('successTemplateIds')).toContain(1);
+ expect(wrapper.state('errorTemplateIds')).toContain(2);
+ });
+});
diff --git a/__tests__/components/NotificationListItem.test.jsx b/__tests__/components/NotificationListItem.test.jsx
new file mode 100644
index 0000000000..d184b0a28e
--- /dev/null
+++ b/__tests__/components/NotificationListItem.test.jsx
@@ -0,0 +1,103 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import { MemoryRouter } from 'react-router-dom';
+import { I18nProvider } from '@lingui/react';
+import NotificationListItem from '../../src/components/NotificationsList/NotificationListItem';
+
+describe('', () => {
+ let wrapper;
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.unmount();
+ wrapper = null;
+ }
+ });
+
+ test('initially renders succesfully', () => {
+ wrapper = mount(
+
+
+
+
+
+ );
+ expect(wrapper.length).toBe(1);
+ });
+
+ test('handles success click when toggle is on', () => {
+ const successToggleClickSpy = jest.spyOn(NotificationListItem.prototype, 'successToggleClick');
+ const toggleSuccessPropFn = jest.fn();
+ wrapper = mount(
+
+
+
+
+
+ );
+ wrapper.find('Switch').first().find('input').simulate('change');
+ expect(successToggleClickSpy).toHaveBeenCalledWith(true);
+ expect(toggleSuccessPropFn).toHaveBeenCalledWith(9000, true);
+ });
+
+ test('handles success click when toggle is off', () => {
+ const successToggleClickSpy = jest.spyOn(NotificationListItem.prototype, 'successToggleClick');
+ const toggleSuccessPropFn = jest.fn();
+ wrapper = mount(
+
+
+
+
+
+ );
+ wrapper.find('Switch').first().find('input').simulate('change');
+ expect(successToggleClickSpy).toHaveBeenCalledWith(false);
+ expect(toggleSuccessPropFn).toHaveBeenCalledWith(9000, false);
+ });
+
+ test('handles error click when toggle is on', () => {
+ const errorToggleClickSpy = jest.spyOn(NotificationListItem.prototype, 'errorToggleClick');
+ const toggleErrorPropFn = jest.fn();
+ wrapper = mount(
+
+
+
+
+
+ );
+ wrapper.find('Switch').at(1).find('input').simulate('change');
+ expect(errorToggleClickSpy).toHaveBeenCalledWith(true);
+ expect(toggleErrorPropFn).toHaveBeenCalledWith(9000, true);
+ });
+
+ test('handles error click when toggle is off', () => {
+ const errorToggleClickSpy = jest.spyOn(NotificationListItem.prototype, 'errorToggleClick');
+ const toggleErrorPropFn = jest.fn();
+ wrapper = mount(
+
+
+
+
+
+ );
+ wrapper.find('Switch').at(1).find('input').simulate('change');
+ expect(errorToggleClickSpy).toHaveBeenCalledWith(false);
+ expect(toggleErrorPropFn).toHaveBeenCalledWith(9000, false);
+ });
+});
diff --git a/__tests__/pages/Organizations/screens/Organization/OrganizationDetail.test.jsx b/__tests__/pages/Organizations/screens/Organization/OrganizationDetail.test.jsx
index aaaa7adde4..7262ae85a9 100644
--- a/__tests__/pages/Organizations/screens/Organization/OrganizationDetail.test.jsx
+++ b/__tests__/pages/Organizations/screens/Organization/OrganizationDetail.test.jsx
@@ -12,6 +12,7 @@ describe('', () => {
diff --git a/src/api.js b/src/api.js
index ce915bd136..bc8821dce3 100644
--- a/src/api.js
+++ b/src/api.js
@@ -70,6 +70,36 @@ class APIClient {
return this.http.get(endpoint);
}
+ getOrganizationNotifications (id, params = {}) {
+ const endpoint = `${API_ORGANIZATIONS}${id}/notification_templates/`;
+
+ return this.http.get(endpoint, { params });
+ }
+
+ getOrganizationNotificationSuccess (id, params = {}) {
+ const endpoint = `${API_ORGANIZATIONS}${id}/notification_templates_success/`;
+
+ return this.http.get(endpoint, { params });
+ }
+
+ getOrganizationNotificationError (id, params = {}) {
+ const endpoint = `${API_ORGANIZATIONS}${id}/notification_templates_error/`;
+
+ return this.http.get(endpoint, { params });
+ }
+
+ postOrganizationNotificationSuccess (id, data) {
+ const endpoint = `${API_ORGANIZATIONS}${id}/notification_templates_success/`;
+
+ return this.http.post(endpoint, data);
+ }
+
+ postOrganizationNotificationError (id, data) {
+ const endpoint = `${API_ORGANIZATIONS}${id}/notification_templates_error/`;
+
+ return this.http.post(endpoint, data);
+ }
+
getInstanceGroups () {
return this.http.get(API_INSTANCE_GROUPS);
}
diff --git a/src/app.scss b/src/app.scss
index 5d2f72d701..b4b176f3d3 100644
--- a/src/app.scss
+++ b/src/app.scss
@@ -82,6 +82,10 @@
--pf-c-data-list__item--PaddingTop: 16px;
--pf-c-data-list__item--PaddingBottom: 16px;
+
+ .pf-c-badge:not(:last-child), .pf-c-switch:not(:last-child) {
+ margin-right: 18px;
+ }
}
.pf-c-data-list__item {
@@ -107,10 +111,6 @@
margin-right: 8px;
}
-.pf-c-data-list__cell span {
- margin-right: 18px;
-}
-
//
// about modal overrides
//
@@ -119,13 +119,6 @@
--pf-c-about-modal-box--MaxWidth: 63rem;
}
-.pf-c-list {
- li {
- list-style-type: none;
- margin: 0;
- padding: 0;
- }
-}
.awx-lookup {
min-height: 36px;
@@ -157,6 +150,12 @@
.awx-c-list {
border-top: 1px solid #d7d7d7;
border-bottom: 1px solid #d7d7d7;
+
+ li {
+ list-style-type: none;
+ margin: 0;
+ padding: 0;
+ }
}
//
// pf modal overrides
diff --git a/src/components/CapitalizeText.jsx b/src/components/CapitalizeText.jsx
new file mode 100644
index 0000000000..9411f395e6
--- /dev/null
+++ b/src/components/CapitalizeText.jsx
@@ -0,0 +1,14 @@
+import React from 'react';
+
+class CapitalizeText extends React.Component {
+ upperCaseFirstLetter = (string) => (typeof string === 'string' ? string.charAt(0).toUpperCase() + string.slice(1) : '');
+
+ render () {
+ const { text } = this.props;
+ return (
+ this.upperCaseFirstLetter(text)
+ );
+ }
+}
+
+export default CapitalizeText;
diff --git a/src/components/DataListToolbar/DataListToolbar.jsx b/src/components/DataListToolbar/DataListToolbar.jsx
index 0936e7bdc8..8ea46641a8 100644
--- a/src/components/DataListToolbar/DataListToolbar.jsx
+++ b/src/components/DataListToolbar/DataListToolbar.jsx
@@ -106,7 +106,9 @@ class DataListToolbar extends React.Component {
sortedColumnKey,
sortOrder,
addUrl,
- showExpandCollapse
+ showExpandCollapse,
+ showDelete,
+ showSelectAll
} = this.props;
const {
// isActionDropdownOpen,
@@ -149,19 +151,19 @@ class DataListToolbar extends React.Component {
-
-
-
-
-
-
+
+ { showSelectAll && (
+
+
+
+
+
+ )}
@@ -248,17 +250,19 @@ class DataListToolbar extends React.Component {
-
-
-
+
+
+ )}
{addUrl && (