Skip to content

Commit

Permalink
feat(bannerMessages): issues/502 cloud meter messaging
Browse files Browse the repository at this point in the history
* build, dotenv flag for recommendations link
* locale, cloud meter related banner message strings
* styling, banner messages spacing
* appMessagesSelectors, apply tally cloud meter data, mismatch
* bannerMessages, display banner messages
* graphCardSelectors, expose cloud meter flags
* rhelView, openshiftView, apply bannerMessages display
* rhsmApiTypes, cloud meter data and mismatch flags
* rhsmServices, mock update for cloud meter data
  • Loading branch information
cdcabrera committed Nov 12, 2020
1 parent 1d75d72 commit a53b336
Show file tree
Hide file tree
Showing 24 changed files with 1,036 additions and 24 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ PUBLIC_URL=${UI_DEPLOY_PATH_PREFIX}/apps/subscriptions/

REACT_APP_UI_LINK_CONTACT_US=https://access.redhat.com/account-team
REACT_APP_UI_LINK_LEARN_MORE=https://access.redhat.com/documentation/en-us/subscription_central/2020-10/html/getting_started_with_subscription_watch/con-how-does-subscriptionwatch-show-data_assembly-opening-subscriptionwatch-ctxt/
REACT_APP_UI_LINK_REPORT_ACCURACY_RECOMMENDATIONS=

REACT_APP_UI_DISABLED=false
REACT_APP_UI_DISABLED_TOOLBAR=false
Expand Down
5 changes: 5 additions & 0 deletions public/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
"pending": "Authenticating...",
"maintenanceCopy": "We are currently undergoing scheduled maintenance and will be back shortly. Thank you for your patience."
},
"curiosity-banner": {
"dataMismatchTitle": "Improve reporting accuracy",
"dataMismatchMessage": "Action required in order to improve {{appName}} reporting.",
"dataMismatchMessage_cloudigradeMismatch": "<0>View recommmend actions</0> to take in order to improve {{appName}} reporting."
},
"curiosity-graph": {
"cardHeading": "CPU usage",
"coresHeading": "CPU core usage",
Expand Down
3 changes: 3 additions & 0 deletions src/common/__tests__/__snapshots__/helpers.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Object {
"UI_DISPLAY_START_NAME": "Subscription Watch",
"UI_LINK_CONTACT_US": "https://access.redhat.com/account-team",
"UI_LINK_LEARN_MORE": "https://access.redhat.com/documentation/en-us/subscription_central/2020-10/html/getting_started_with_subscription_watch/con-how-does-subscriptionwatch-show-data_assembly-opening-subscriptionwatch-ctxt/",
"UI_LINK_REPORT_ACCURACY_RECOMMENDATIONS": "",
"UI_LOCALE_DEFAULT": "en-US",
"UI_LOCALE_DEFAULT_DESC": "English",
"UI_LOGGER_ID": "curiosity",
Expand Down Expand Up @@ -50,6 +51,7 @@ Object {
"UI_DISPLAY_START_NAME": "Subscription Watch",
"UI_LINK_CONTACT_US": "https://access.redhat.com/account-team",
"UI_LINK_LEARN_MORE": "https://access.redhat.com/documentation/en-us/subscription_central/2020-10/html/getting_started_with_subscription_watch/con-how-does-subscriptionwatch-show-data_assembly-opening-subscriptionwatch-ctxt/",
"UI_LINK_REPORT_ACCURACY_RECOMMENDATIONS": "",
"UI_LOCALE_DEFAULT": "en-US",
"UI_LOCALE_DEFAULT_DESC": "English",
"UI_LOGGER_ID": "curiosity",
Expand Down Expand Up @@ -84,6 +86,7 @@ Object {
"UI_DISPLAY_START_NAME": "Subscription Watch",
"UI_LINK_CONTACT_US": "https://access.redhat.com/account-team",
"UI_LINK_LEARN_MORE": "https://access.redhat.com/documentation/en-us/subscription_central/2020-10/html/getting_started_with_subscription_watch/con-how-does-subscriptionwatch-show-data_assembly-opening-subscriptionwatch-ctxt/",
"UI_LINK_REPORT_ACCURACY_RECOMMENDATIONS": "",
"UI_LOCALE_DEFAULT": "en-US",
"UI_LOCALE_DEFAULT_DESC": "English",
"UI_LOGGER_ID": "curiosity",
Expand Down
8 changes: 8 additions & 0 deletions src/common/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,13 @@ const UI_LINK_CONTACT_US = process.env.REACT_APP_UI_LINK_CONTACT_US;
*/
const UI_LINK_LEARN_MORE = process.env.REACT_APP_UI_LINK_LEARN_MORE;

/**
* A url, or uri, for "recommend actions"
*
* @type {string}
*/
const UI_LINK_REPORT_ACCURACY_RECOMMENDATIONS = process.env.REACT_APP_UI_LINK_REPORT_ACCURACY_RECOMMENDATIONS;

/**
* UI locale default.
*
Expand Down Expand Up @@ -252,6 +259,7 @@ const helpers = {
UI_DISPLAY_START_NAME,
UI_LINK_CONTACT_US,
UI_LINK_LEARN_MORE,
UI_LINK_REPORT_ACCURACY_RECOMMENDATIONS,
UI_LOCALE_DEFAULT,
UI_LOCALE_DEFAULT_DESC,
UI_LOGGER_ID,
Expand Down
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');
});
});
127 changes: 127 additions & 0 deletions src/components/bannerMessages/bannerMessages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Alert, AlertActionCloseButton, AlertVariant, Button } from '@patternfly/react-core';
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
import { connect, reduxSelectors } from '../../redux';
import { translate } from '../i18n/i18n';
import { helpers } from '../../common';

/**
* 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: [
{
id: 'cloudigradeMismatch',
title: translate('curiosity-banner.dataMismatchTitle'),
message: translate(
'curiosity-banner.dataMismatchMessage',
{
context: helpers.UI_LINK_REPORT_ACCURACY_RECOMMENDATIONS !== '' && 'cloudigradeMismatch',
appName: helpers.UI_DISPLAY_NAME
},
[
<Button
isInline
component="a"
variant="link"
icon={<ExternalLinkAltIcon />}
iconPosition="right"
target="_blank"
href={helpers.UI_LINK_REPORT_ACCURACY_RECOMMENDATIONS}
/>
]
)
}
]
};

/**
* 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 };
13 changes: 13 additions & 0 deletions src/components/i18n/__tests__/__snapshots__/i18n.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@ Array [
},
],
},
Object {
"file": "./src/components/bannerMessages/bannerMessages.js",
"keys": Array [
Object {
"key": "curiosity-banner.dataMismatchTitle",
"match": "translate('curiosity-banner.dataMismatchTitle')",
},
Object {
"key": "curiosity-banner.dataMismatchMessage",
"match": "translate( 'curiosity-banner.dataMismatchMessage', { context: helpers.UI_LINK_REPORT_ACCURACY_RECOMMENDATIONS !== '' && 'cloudigradeMismatch', appName: helpers.UI_DISPLAY_NAME }, [ <Button isInline component=\\"a\\" variant=\\"link\\" icon={<ExternalLinkAltIcon />} iconPosition=\\"right\\" target=\\"_blank\\" href={helpers.UI_LINK_REPORT_ACCURACY_RECOMMENDATIONS} /> ] )",
},
],
},
Object {
"file": "./src/components/c3GraphCard/c3GraphCard.js",
"keys": Array [
Expand Down
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
Loading

0 comments on commit a53b336

Please sign in to comment.