1
0
mirror of https://github.com/ansible/awx.git synced 2024-10-31 23:51:09 +03:00

Merge pull request #4199 from mabashian/264-notification-type-column

Show notification type in its own column

Reviewed-by: https://github.com/softwarefactory-project-zuul[bot]
This commit is contained in:
softwarefactory-project-zuul[bot] 2019-07-01 22:57:50 +00:00 committed by GitHub
commit 4fb055345d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 305 additions and 202 deletions

View File

@ -1,5 +1,9 @@
const NotificationsMixin = parent =>
class extends parent {
readOptionsNotificationTemplates(id) {
return this.http.options(`${this.baseUrl}${id}/notification_templates/`);
}
readNotificationTemplates(id, params = {}) {
return this.http.get(`${this.baseUrl}${id}/notification_templates/`, {
params,

View File

@ -4,7 +4,6 @@ import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { Link } from 'react-router-dom';
import {
Badge,
Switch as PFSwitch,
DataListItem,
DataListItemRow,
@ -39,6 +38,7 @@ function NotificationListItem(props) {
errorTurnedOn,
toggleNotification,
i18n,
typeLabels,
} = props;
return (
@ -60,9 +60,9 @@ function NotificationListItem(props) {
{notification.name}
</b>
</Link>
<Badge css="text-transform: capitalize;" isRead>
{notification.notification_type}
</Badge>
</DataListCell>,
<DataListCell key="type">
{typeLabels[notification.notification_type]}
</DataListCell>,
<DataListCell righthalf="true" key="toggles">
<Switch
@ -108,6 +108,7 @@ NotificationListItem.propTypes = {
errorTurnedOn: bool,
successTurnedOn: bool,
toggleNotification: func.isRequired,
typeLabels: shape().isRequired,
};
NotificationListItem.defaultProps = {

View File

@ -6,6 +6,16 @@ describe('<NotificationListItem canToggleNotifications />', () => {
let wrapper;
let toggleNotification;
const mockNotif = {
id: 9000,
name: 'Foo',
notification_type: 'slack',
};
const typeLabels = {
slack: 'Slack',
};
beforeEach(() => {
toggleNotification = jest.fn();
});
@ -18,34 +28,45 @@ describe('<NotificationListItem canToggleNotifications />', () => {
jest.clearAllMocks();
});
test('initially renders succesfully', () => {
test('initially renders succesfully and displays correct label', () => {
wrapper = mountWithContexts(
<NotificationListItem
notification={{
id: 9000,
name: 'Foo',
notification_type: 'slack',
}}
notification={mockNotif}
toggleNotification={toggleNotification}
detailUrl="/foo"
canToggleNotifications
typeLabels={typeLabels}
/>
);
expect(wrapper.find('NotificationListItem')).toMatchSnapshot();
});
test('displays correct label in correct column', () => {
wrapper = mountWithContexts(
<NotificationListItem
notification={mockNotif}
toggleNotification={toggleNotification}
detailUrl="/foo"
canToggleNotifications
typeLabels={typeLabels}
/>
);
const typeCell = wrapper
.find('DataListCell')
.at(1)
.find('div');
expect(typeCell.text()).toBe('Slack');
});
test('handles success click when toggle is on', () => {
wrapper = mountWithContexts(
<NotificationListItem
notification={{
id: 9000,
name: 'Foo',
notification_type: 'slack',
}}
notification={mockNotif}
successTurnedOn
toggleNotification={toggleNotification}
detailUrl="/foo"
canToggleNotifications
typeLabels={typeLabels}
/>
);
wrapper
@ -59,15 +80,12 @@ describe('<NotificationListItem canToggleNotifications />', () => {
test('handles success click when toggle is off', () => {
wrapper = mountWithContexts(
<NotificationListItem
notification={{
id: 9000,
name: 'Foo',
notification_type: 'slack',
}}
notification={mockNotif}
successTurnedOn={false}
toggleNotification={toggleNotification}
detailUrl="/foo"
canToggleNotifications
typeLabels={typeLabels}
/>
);
wrapper
@ -81,15 +99,12 @@ describe('<NotificationListItem canToggleNotifications />', () => {
test('handles error click when toggle is on', () => {
wrapper = mountWithContexts(
<NotificationListItem
notification={{
id: 9000,
name: 'Foo',
notification_type: 'slack',
}}
notification={mockNotif}
errorTurnedOn
toggleNotification={toggleNotification}
detailUrl="/foo"
canToggleNotifications
typeLabels={typeLabels}
/>
);
wrapper
@ -103,15 +118,12 @@ describe('<NotificationListItem canToggleNotifications />', () => {
test('handles error click when toggle is off', () => {
wrapper = mountWithContexts(
<NotificationListItem
notification={{
id: 9000,
name: 'Foo',
notification_type: 'slack',
}}
notification={mockNotif}
errorTurnedOn={false}
toggleNotification={toggleNotification}
detailUrl="/foo"
canToggleNotifications
typeLabels={typeLabels}
/>
);
wrapper

View File

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<NotificationListItem canToggleNotifications /> initially renders succesfully 1`] = `
exports[`<NotificationListItem canToggleNotifications /> initially renders succesfully and displays correct label 1`] = `
<NotificationListItem
canToggleNotifications={true}
detailUrl="/foo"
@ -15,6 +15,11 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
}
successTurnedOn={false}
toggleNotification={[MockFunction]}
typeLabels={
Object {
"slack": "Slack",
}
}
>
<DataListItem
aria-labelledby="items-list-item-9000"
@ -52,11 +57,9 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
Foo
</b>
</ForwardRef>
<ForwardRef
isRead={true}
>
slack
</ForwardRef>
</ForwardRef>,
<ForwardRef>
Slack
</ForwardRef>,
<ForwardRef
righthalf="true"
@ -189,47 +192,55 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
</Link>
</StyledComponent>
</Styled(Link)>
<Styled(Badge)
isRead={true}
>
<StyledComponent
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "sc-bwzfXH",
"isStatic": true,
"lastClassName": "chTbOZ",
"rules": Array [
"text-transform: capitalize;",
],
},
"displayName": "Styled(Badge)",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "sc-bwzfXH",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
isRead={true}
>
<Badge
className="sc-bwzfXH chTbOZ"
isRead={true}
>
<span
className="pf-c-badge pf-m-read sc-bwzfXH chTbOZ"
>
slack
</span>
</Badge>
</StyledComponent>
</Styled(Badge)>
</div>
</DataListCell>
</StyledComponent>
</NotificationListItem__DataListCell>
<NotificationListItem__DataListCell
key="type"
>
<StyledComponent
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "NotificationListItem__DataListCell-j7c411-0",
"isStatic": false,
"lastClassName": "hoXOpW",
"rules": Array [
"display:flex;justify-content:",
[Function],
";padding-bottom:",
[Function],
";@media screen and (min-width:768px){justify-content:",
[Function],
";padding-bottom:0;}",
],
},
"displayName": "NotificationListItem__DataListCell",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "NotificationListItem__DataListCell-j7c411-0",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
>
<DataListCell
alignRight={false}
className="NotificationListItem__DataListCell-j7c411-0 kIdLtz"
isFilled={true}
isIcon={false}
width={1}
>
<div
className="pf-c-data-list__cell NotificationListItem__DataListCell-j7c411-0 kIdLtz"
>
Slack
</div>
</DataListCell>
</StyledComponent>

View File

@ -35,6 +35,7 @@ class OrganizationNotifications extends Component {
notifications: [],
successTemplateIds: [],
errorTemplateIds: [],
typeLabels: null,
};
this.handleNotificationToggle = this.handleNotificationToggle.bind(this);
this.handleNotificationErrorClose = this.handleNotificationErrorClose.bind(
@ -56,13 +57,23 @@ class OrganizationNotifications extends Component {
async loadNotifications() {
const { id, location } = this.props;
const { typeLabels } = this.state;
const params = parseNamespacedQueryString(QS_CONFIG, location.search);
const promises = [OrganizationsAPI.readNotificationTemplates(id, params)];
if (!typeLabels) {
promises.push(OrganizationsAPI.readOptionsNotificationTemplates(id));
}
this.setState({ contentError: null, hasContentLoading: true });
try {
const {
data: { count: itemCount = 0, results: notifications = [] },
} = await OrganizationsAPI.readNotificationTemplates(id, params);
const [
{
data: { count: itemCount = 0, results: notifications = [] },
},
optionsResponse,
] = await Promise.all(promises);
let idMatchParams;
if (notifications.length > 0) {
@ -79,12 +90,31 @@ class OrganizationNotifications extends Component {
OrganizationsAPI.readNotificationTemplatesError(id, idMatchParams),
]);
this.setState({
const stateToUpdate = {
itemCount,
notifications,
successTemplateIds: successTemplates.results.map(s => s.id),
errorTemplateIds: errorTemplates.results.map(e => e.id),
});
};
if (!typeLabels) {
const {
data: {
actions: {
GET: {
notification_type: { choices },
},
},
},
} = optionsResponse;
// The structure of choices looks like [['slack', 'Slack'], ['email', 'Email'], ...]
stateToUpdate.typeLabels = choices.reduce(
(map, notifType) => ({ ...map, [notifType[0]]: notifType[1] }),
{}
);
}
this.setState(stateToUpdate);
} catch (err) {
this.setState({ contentError: err });
} finally {
@ -148,6 +178,7 @@ class OrganizationNotifications extends Component {
notifications,
successTemplateIds,
errorTemplateIds,
typeLabels,
} = this.state;
return (
@ -169,6 +200,7 @@ class OrganizationNotifications extends Component {
toggleNotification={this.handleNotificationToggle}
errorTurnedOn={errorTemplateIds.includes(notification.id)}
successTurnedOn={successTemplateIds.includes(notification.id)}
typeLabels={typeLabels}
/>
)}
/>

View File

@ -8,26 +8,37 @@ import OrganizationNotifications from './OrganizationNotifications';
jest.mock('@api');
describe('<OrganizationNotifications />', () => {
let data;
const data = {
count: 2,
results: [
{
id: 1,
name: 'Notification one',
url: '/api/v2/notification_templates/1/',
notification_type: 'email',
},
{
id: 2,
name: 'Notification two',
url: '/api/v2/notification_templates/2/',
notification_type: 'email',
},
],
};
OrganizationsAPI.readOptionsNotificationTemplates.mockReturnValue({
data: {
actions: {
GET: {
notification_type: {
choices: [['email', 'Email']],
},
},
},
},
});
beforeEach(() => {
data = {
count: 2,
results: [
{
id: 1,
name: 'Notification one',
url: '/api/v2/notification_templates/1/',
notification_type: 'email',
},
{
id: 2,
name: 'Notification two',
url: '/api/v2/notification_templates/2/',
notification_type: 'email',
},
],
};
OrganizationsAPI.readNotificationTemplates.mockReturnValue({ data });
OrganizationsAPI.readNotificationTemplatesSuccess.mockReturnValue({
data: { results: [{ id: 1 }] },

View File

@ -410,9 +410,9 @@ exports[`<OrganizationNotifications /> initially renders succesfully 1`] = `
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "sc-bxivhb",
"componentId": "sc-htpNat",
"isStatic": true,
"lastClassName": "gYEJOJ",
"lastClassName": "dqEVhr",
"rules": Array [
"flex-grow: 1;",
],
@ -420,7 +420,7 @@ exports[`<OrganizationNotifications /> initially renders succesfully 1`] = `
"displayName": "Styled(ToolbarItem)",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "sc-bxivhb",
"styledComponentId": "sc-htpNat",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
@ -430,10 +430,10 @@ exports[`<OrganizationNotifications /> initially renders succesfully 1`] = `
forwardedRef={null}
>
<ToolbarItem
className="sc-bxivhb gYEJOJ"
className="sc-htpNat dqEVhr"
>
<div
className="pf-l-toolbar__item sc-bxivhb gYEJOJ"
className="pf-l-toolbar__item sc-htpNat dqEVhr"
>
<WithI18n
columns={
@ -1441,9 +1441,9 @@ exports[`<OrganizationNotifications /> initially renders succesfully 1`] = `
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "sc-htpNat",
"componentId": "sc-bwzfXH",
"isStatic": true,
"lastClassName": "jWbbwS",
"lastClassName": "iNPUwu",
"rules": Array [
"padding: 0;",
],
@ -1451,7 +1451,7 @@ exports[`<OrganizationNotifications /> initially renders succesfully 1`] = `
"displayName": "Styled(Button)",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "sc-htpNat",
"styledComponentId": "sc-bwzfXH",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
@ -1464,7 +1464,7 @@ exports[`<OrganizationNotifications /> initially renders succesfully 1`] = `
>
<Button
aria-label="Sort"
className="sc-htpNat jWbbwS"
className="sc-bwzfXH iNPUwu"
component="button"
isActive={false}
isBlock={false}
@ -1479,7 +1479,7 @@ exports[`<OrganizationNotifications /> initially renders succesfully 1`] = `
<button
aria-disabled={null}
aria-label="Sort"
className="pf-c-button pf-m-plain sc-htpNat jWbbwS"
className="pf-c-button pf-m-plain sc-bwzfXH iNPUwu"
disabled={false}
onClick={[Function]}
tabIndex={null}
@ -1619,6 +1619,11 @@ exports[`<OrganizationNotifications /> initially renders succesfully 1`] = `
}
successTurnedOn={true}
toggleNotification={[Function]}
typeLabels={
Object {
"email": "Email",
}
}
>
<I18n
update={true}
@ -1639,6 +1644,11 @@ exports[`<OrganizationNotifications /> initially renders succesfully 1`] = `
}
successTurnedOn={true}
toggleNotification={[Function]}
typeLabels={
Object {
"email": "Email",
}
}
>
<DataListItem
aria-labelledby="items-list-item-1"
@ -1676,11 +1686,9 @@ exports[`<OrganizationNotifications /> initially renders succesfully 1`] = `
Notification one
</b>
</ForwardRef>
<ForwardRef
isRead={true}
>
email
</ForwardRef>
</ForwardRef>,
<ForwardRef>
Email
</ForwardRef>,
<ForwardRef
righthalf="true"
@ -1813,47 +1821,55 @@ exports[`<OrganizationNotifications /> initially renders succesfully 1`] = `
</Link>
</StyledComponent>
</Styled(Link)>
<Styled(Badge)
isRead={true}
>
<StyledComponent
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "sc-bwzfXH",
"isStatic": true,
"lastClassName": "chTbOZ",
"rules": Array [
"text-transform: capitalize;",
],
},
"displayName": "Styled(Badge)",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "sc-bwzfXH",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
isRead={true}
>
<Badge
className="sc-bwzfXH chTbOZ"
isRead={true}
>
<span
className="pf-c-badge pf-m-read sc-bwzfXH chTbOZ"
>
email
</span>
</Badge>
</StyledComponent>
</Styled(Badge)>
</div>
</DataListCell>
</StyledComponent>
</NotificationListItem__DataListCell>
<NotificationListItem__DataListCell
key="type"
>
<StyledComponent
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "NotificationListItem__DataListCell-j7c411-0",
"isStatic": false,
"lastClassName": "hoXOpW",
"rules": Array [
"display:flex;justify-content:",
[Function],
";padding-bottom:",
[Function],
";@media screen and (min-width:768px){justify-content:",
[Function],
";padding-bottom:0;}",
],
},
"displayName": "NotificationListItem__DataListCell",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "NotificationListItem__DataListCell-j7c411-0",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
>
<DataListCell
alignRight={false}
className="NotificationListItem__DataListCell-j7c411-0 kIdLtz"
isFilled={true}
isIcon={false}
width={1}
>
<div
className="pf-c-data-list__cell NotificationListItem__DataListCell-j7c411-0 kIdLtz"
>
Email
</div>
</DataListCell>
</StyledComponent>
@ -2094,6 +2110,11 @@ exports[`<OrganizationNotifications /> initially renders succesfully 1`] = `
}
successTurnedOn={false}
toggleNotification={[Function]}
typeLabels={
Object {
"email": "Email",
}
}
>
<I18n
update={true}
@ -2114,6 +2135,11 @@ exports[`<OrganizationNotifications /> initially renders succesfully 1`] = `
}
successTurnedOn={false}
toggleNotification={[Function]}
typeLabels={
Object {
"email": "Email",
}
}
>
<DataListItem
aria-labelledby="items-list-item-2"
@ -2151,11 +2177,9 @@ exports[`<OrganizationNotifications /> initially renders succesfully 1`] = `
Notification two
</b>
</ForwardRef>
<ForwardRef
isRead={true}
>
email
</ForwardRef>
</ForwardRef>,
<ForwardRef>
Email
</ForwardRef>,
<ForwardRef
righthalf="true"
@ -2288,47 +2312,55 @@ exports[`<OrganizationNotifications /> initially renders succesfully 1`] = `
</Link>
</StyledComponent>
</Styled(Link)>
<Styled(Badge)
isRead={true}
>
<StyledComponent
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "sc-bwzfXH",
"isStatic": true,
"lastClassName": "chTbOZ",
"rules": Array [
"text-transform: capitalize;",
],
},
"displayName": "Styled(Badge)",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "sc-bwzfXH",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
isRead={true}
>
<Badge
className="sc-bwzfXH chTbOZ"
isRead={true}
>
<span
className="pf-c-badge pf-m-read sc-bwzfXH chTbOZ"
>
email
</span>
</Badge>
</StyledComponent>
</Styled(Badge)>
</div>
</DataListCell>
</StyledComponent>
</NotificationListItem__DataListCell>
<NotificationListItem__DataListCell
key="type"
>
<StyledComponent
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "NotificationListItem__DataListCell-j7c411-0",
"isStatic": false,
"lastClassName": "hoXOpW",
"rules": Array [
"display:flex;justify-content:",
[Function],
";padding-bottom:",
[Function],
";@media screen and (min-width:768px){justify-content:",
[Function],
";padding-bottom:0;}",
],
},
"displayName": "NotificationListItem__DataListCell",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "NotificationListItem__DataListCell-j7c411-0",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
>
<DataListCell
alignRight={false}
className="NotificationListItem__DataListCell-j7c411-0 kIdLtz"
isFilled={true}
isIcon={false}
width={1}
>
<div
className="pf-c-data-list__cell NotificationListItem__DataListCell-j7c411-0 kIdLtz"
>
Email
</div>
</DataListCell>
</StyledComponent>