Skip to content

Commit

Permalink
feat(bannerMessages): issues/502 app messages (#503)
Browse files Browse the repository at this point in the history
* styling, banner messages spacing
* appMessagesSelectors, base selector
* bannerMessages, display app banner messages
* rhelView, openshiftView, apply bannerMessages display
  • Loading branch information
cdcabrera committed Nov 12, 2020
1 parent 88cd412 commit aa35f92
Show file tree
Hide file tree
Showing 13 changed files with 401 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`BannerMessages Component should handle closing messages from state: state messages, OFF 1`] = `""`;

exports[`BannerMessages Component should handle closing messages from state: state messages, ON 1`] = `
<div
className="curiosity-banner-messages"
>
<Alert
actionClose={
<AlertActionCloseButton
onClose={[Function]}
/>
}
key="loremIpsum"
title="Lorem ipsum title"
variant="info"
>
Lorem ipsum message
</Alert>
</div>
`;

exports[`BannerMessages Component should render a non-connected component: non-connected 1`] = `
<div
className="curiosity-banner-messages"
>
<Alert
actionClose={
<AlertActionCloseButton
onClose={[Function]}
/>
}
key="loremIpsum"
title="Lorem ipsum title"
variant="info"
>
Lorem ipsum message
</Alert>
</div>
`;

exports[`BannerMessages Component should render specific messages when the appMessages prop is used: specific messages, OFF 1`] = `""`;

exports[`BannerMessages Component should render specific messages when the appMessages prop is used: specific messages, ON 1`] = `
<div
className="curiosity-banner-messages"
>
<Alert
actionClose={
<AlertActionCloseButton
onClose={[Function]}
/>
}
key="loremIpsum"
title="Lorem ipsum title"
variant="info"
>
Lorem ipsum message
</Alert>
</div>
`;
76 changes: 76 additions & 0 deletions src/components/bannerMessages/__tests__/bannerMessages.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React from 'react';
import { shallow } from 'enzyme';
import { BannerMessages } from '../bannerMessages';

describe('BannerMessages Component', () => {
it('should render a non-connected component', () => {
const props = {
appMessages: {
loremIpsum: true
},
messages: [
{
id: 'loremIpsum',
title: 'Lorem ipsum title',
message: 'Lorem ipsum message'
}
]
};
const component = shallow(<BannerMessages {...props} />);

expect(component).toMatchSnapshot('non-connected');
});

it('should render specific messages when the appMessages prop is used', () => {
const props = {
appMessages: {
loremIpsum: false,
dolorSit: false
},
messages: [
{
id: 'loremIpsum',
title: 'Lorem ipsum title',
message: 'Lorem ipsum message'
},
{
id: 'dolorSit',
title: 'Dolor sit title',
message: 'Dolor sit message'
}
]
};
const component = shallow(<BannerMessages {...props} />);

expect(component).toMatchSnapshot('specific messages, OFF');

component.setProps({
...props,
appMessages: {
loremIpsum: true,
dolorSit: false
}
});
expect(component).toMatchSnapshot('specific messages, ON');
});

it('should handle closing messages from state', () => {
const props = {
appMessages: {
loremIpsum: true
},
messages: [
{
id: 'loremIpsum',
title: 'Lorem ipsum title',
message: 'Lorem ipsum message'
}
]
};
const component = shallow(<BannerMessages {...props} />);
expect(component).toMatchSnapshot('state messages, ON');

component.setState({ loremIpsum: true });
expect(component).toMatchSnapshot('state messages, OFF');
});
});
101 changes: 101 additions & 0 deletions src/components/bannerMessages/bannerMessages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Alert, AlertActionCloseButton, AlertVariant } from '@patternfly/react-core';
import { connect, reduxSelectors } from '../../redux';

/**
* Render banner messages.
*
* @augments React.Component
*/
class BannerMessages extends React.Component {
state = {};

/**
* Apply messages' configuration to alerts.
*
* @returns {Node}
*/
renderAlerts() {
const { state } = this;
const { appMessages, messages } = this.props;
const updatedMessages = [];

if (messages.length) {
Object.entries(appMessages).forEach(([key, value]) => {
if (state[key] !== true && value === true) {
const message = messages.find(({ id }) => id === key);

if (message) {
updatedMessages.push({
key,
...message
});
}
}
});
}

return updatedMessages.map(({ key, message, title, variant = AlertVariant.info }) => {
const actionClose = <AlertActionCloseButton onClose={() => this.setState({ [key]: true })} />;

return (
<Alert actionClose={actionClose} key={key} title={title} variant={variant}>
{message}
</Alert>
);
});
}

/**
* Render a banner messages container.
*
* @returns {Node}
*/
render() {
const alerts = this.renderAlerts();

if (alerts.length) {
return <div className="curiosity-banner-messages">{alerts}</div>;
}

return null;
}
}

/**
* Prop types.
*
* @type {{appMessages: object, messages: Array}}
*/
BannerMessages.propTypes = {
appMessages: PropTypes.object.isRequired,
messages: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
title: PropTypes.node.isRequired,
message: PropTypes.node.isRequired,
variant: PropTypes.oneOf([...Object.values(AlertVariant)])
})
)
};

/**
* Default props.
*
* @type {{messages: Array}}
*/
BannerMessages.defaultProps = {
messages: []
};

/**
* Create a selector from applied state, props.
*
* @type {Function}
*/
const makeMapStateToProps = reduxSelectors.appMessages.makeAppMessages();

const ConnectedBannerMessages = connect(makeMapStateToProps)(BannerMessages);

export { ConnectedBannerMessages as default, ConnectedBannerMessages, BannerMessages };
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ exports[`OpenshiftView Component should display an alternate graph on query-stri
>
t(curiosity-view.title, {"appName":"Subscription Watch","context":"OpenShift"})
</PageHeader>
<PageMessages
className=""
>
<Connect(BannerMessages)
productId="lorem ipsum"
viewId="viewOpenShift"
/>
</PageMessages>
<PageToolbar
className=""
>
Expand Down Expand Up @@ -163,6 +171,14 @@ exports[`OpenshiftView Component should have a fallback title: title 1`] = `
>
t(curiosity-view.title, {"appName":"Subscription Watch","context":"OpenShift"})
</PageHeader>
<PageMessages
className=""
>
<Connect(BannerMessages)
productId="lorem ipsum"
viewId="viewOpenShift"
/>
</PageMessages>
<PageToolbar
className=""
>
Expand Down Expand Up @@ -735,6 +751,14 @@ exports[`OpenshiftView Component should render a non-connected component: non-co
>
t(curiosity-view.title, {"appName":"Subscription Watch","context":"OpenShift"})
</PageHeader>
<PageMessages
className=""
>
<Connect(BannerMessages)
productId="lorem ipsum"
viewId="viewOpenShift"
/>
</PageMessages>
<PageToolbar
className=""
>
Expand Down
6 changes: 5 additions & 1 deletion src/components/openshiftView/openshiftView.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
} from '@patternfly/react-tokens';
import { Button, Label as PfLabel } from '@patternfly/react-core';
import { DateFormat } from '@redhat-cloud-services/frontend-components/components/cjs/DateFormat';
import { PageLayout, PageHeader, PageSection, PageToolbar } from '../pageLayout/pageLayout';
import { PageLayout, PageHeader, PageMessages, PageSection, PageToolbar } from '../pageLayout/pageLayout';
import {
RHSM_API_QUERY_GRANULARITY_TYPES as GRANULARITY_TYPES,
RHSM_API_QUERY_SORT_DIRECTION_TYPES as SORT_DIRECTION_TYPES,
Expand All @@ -20,6 +20,7 @@ import C3GraphCard from '../c3GraphCard/c3GraphCard';
import { Select } from '../form/select';
import Toolbar from '../toolbar/toolbar';
import InventoryList from '../inventoryList/inventoryList';
import BannerMessages from '../bannerMessages/bannerMessages';
import { helpers } from '../../common';
import { translate } from '../i18n/i18n';

Expand Down Expand Up @@ -136,6 +137,9 @@ class OpenshiftView extends React.Component {
<PageHeader productLabel={productLabel}>
{t(`curiosity-view.title`, { appName: helpers.UI_DISPLAY_NAME, context: productLabel })}
</PageHeader>
<PageMessages>
<BannerMessages productId={routeDetail.pathParameter} viewId={viewId} />
</PageMessages>
<PageToolbar>
<Toolbar
filterOptions={initialToolbarFilters}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ exports[`RhelView Component should display an alternate graph on query-string up
>
t(curiosity-view.title, {"appName":"Subscription Watch","context":"RHEL"})
</PageHeader>
<PageMessages
className=""
>
<Connect(BannerMessages)
productId="lorem ipsum"
viewId="viewRHEL"
/>
</PageMessages>
<PageToolbar
className=""
>
Expand Down Expand Up @@ -148,6 +156,14 @@ exports[`RhelView Component should have a fallback title: title 1`] = `
>
t(curiosity-view.title, {"appName":"Subscription Watch","context":"RHEL"})
</PageHeader>
<PageMessages
className=""
>
<Connect(BannerMessages)
productId="lorem ipsum"
viewId="viewRHEL"
/>
</PageMessages>
<PageToolbar
className=""
>
Expand Down Expand Up @@ -677,6 +693,14 @@ exports[`RhelView Component should render a non-connected component: non-connect
>
t(curiosity-view.title, {"appName":"Subscription Watch","context":"RHEL"})
</PageHeader>
<PageMessages
className=""
>
<Connect(BannerMessages)
productId="lorem ipsum"
viewId="viewRHEL"
/>
</PageMessages>
<PageToolbar
className=""
>
Expand Down
6 changes: 5 additions & 1 deletion src/components/rhelView/rhelView.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from '@patternfly/react-tokens';
import { Button, Label as PfLabel } from '@patternfly/react-core';
import { DateFormat } from '@redhat-cloud-services/frontend-components/components/cjs/DateFormat';
import { PageLayout, PageHeader, PageSection, PageToolbar } from '../pageLayout/pageLayout';
import { PageLayout, PageHeader, PageMessages, PageSection, PageToolbar } from '../pageLayout/pageLayout';
import {
RHSM_API_QUERY_SORT_DIRECTION_TYPES as SORT_DIRECTION_TYPES,
RHSM_API_QUERY_GRANULARITY_TYPES as GRANULARITY_TYPES,
Expand All @@ -22,6 +22,7 @@ import GraphCard from '../graphCard/graphCard';
import C3GraphCard from '../c3GraphCard/c3GraphCard';
import Toolbar from '../toolbar/toolbar';
import InventoryList from '../inventoryList/inventoryList';
import BannerMessages from '../bannerMessages/bannerMessages';
import { helpers } from '../../common';
import { translate } from '../i18n/i18n';

Expand Down Expand Up @@ -63,6 +64,9 @@ class RhelView extends React.Component {
<PageHeader productLabel={productLabel}>
{t(`curiosity-view.title`, { appName: helpers.UI_DISPLAY_NAME, context: productLabel })}
</PageHeader>
<PageMessages>
<BannerMessages productId={routeDetail.pathParameter} viewId={viewId} />
</PageMessages>
<PageToolbar>
<Toolbar
filterOptions={initialToolbarFilters}
Expand Down
Loading

0 comments on commit aa35f92

Please sign in to comment.