diff --git a/.eslintrc b/.eslintrc
index 92b6a6ad1..1d0739076 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -9,6 +9,7 @@
"plugins": [
"import",
"jest",
+ "jsdoc",
"json",
"prettier",
"react"
@@ -18,7 +19,8 @@
"airbnb",
"airbnb/hooks",
"prettier",
- "plugin:jest/recommended"
+ "plugin:jest/recommended",
+ "plugin:jsdoc/recommended"
],
"rules": {
"arrow-parens": [
@@ -46,6 +48,24 @@
"import/no-named-as-default-member": 0,
"jest/no-test-callback": 0,
"jest/prefer-to-have-length": 0,
+ "jsdoc/check-tag-names": [
+ 2,
+ {
+ "definedTags": [
+ "api",
+ "apiDescription",
+ "apiSuccess",
+ "apiSuccessExample",
+ "apiError",
+ "apiErrorExample",
+ "apiMock",
+ "apiParam"
+ ]
+ }
+ ],
+ "jsdoc/require-param-description": 0,
+ "jsdoc/require-property-description": 0,
+ "jsdoc/require-returns-description": 0,
"max-len": [
"error",
{
diff --git a/package.json b/package.json
index acef87265..8cbdcc17f 100644
--- a/package.json
+++ b/package.json
@@ -121,6 +121,7 @@
"eslint-config-prettier": "^6.10.0",
"eslint-plugin-import": "^2.20.1",
"eslint-plugin-jest": "^23.8.2",
+ "eslint-plugin-jsdoc": "^22.0.1",
"eslint-plugin-json": "^2.1.1",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-prettier": "^3.1.2",
diff --git a/src/common/dateHelpers.js b/src/common/dateHelpers.js
index b231eaac6..f31b7c212 100644
--- a/src/common/dateHelpers.js
+++ b/src/common/dateHelpers.js
@@ -2,9 +2,23 @@ import moment from 'moment/moment';
import { helpers } from './helpers';
import { RHSM_API_QUERY_GRANULARITY_TYPES as GRANULARITY_TYPES } from '../types/rhsmApiTypes';
+/**
+ * Return a date.
+ *
+ * @returns {string|Date}
+ */
const getCurrentDate = () =>
(helpers.TEST_MODE && '20190720') || (helpers.DEV_MODE && process.env.REACT_APP_DEBUG_DEFAULT_DATETIME) || new Date();
+/**
+ * Set a date range based on a granularity type.
+ *
+ * @param {object} params
+ * @property {Date} date Start date, typically the current date.
+ * @property {number} subtract Number of granularity type to subtract from the current date.
+ * @property {string} measurement Granularity type.
+ * @returns {{endDate: Date, startDate: Date}}
+ */
const setRangedDateTime = ({ date, subtract, measurement }) => ({
startDate: moment
.utc(date)
@@ -24,7 +38,7 @@ const monthlyDateTime = setRangedDateTime({ date: getCurrentDate(), subtract: 12
const quarterlyDateTime = setRangedDateTime({ date: getCurrentDate(), subtract: 36, measurement: 'months' });
/**
- * Return a range of time based on granularity.
+ * Return a range of time based on known granularity types.
*
* @param {string} granularity
* @returns {{endDate: Date, startDate: Date}}
@@ -43,6 +57,11 @@ const getRangedDateTime = granularity => {
}
};
+/**
+ * Consistent timestamp day formats.
+ *
+ * @type {{short: string, yearShort: string, yearLong: string, long: string}}
+ */
const timestampDayFormats = {
long: 'MMMM D',
yearLong: 'MMMM D YYYY',
@@ -50,6 +69,11 @@ const timestampDayFormats = {
yearShort: 'MMM D YYYY'
};
+/**
+ * Consistent timestamp month formats.
+ *
+ * @type {{short: string, yearShort: string, yearLong: string, long: string}}
+ */
const timestampMonthFormats = {
long: 'MMMM',
yearLong: 'MMMM YYYY',
@@ -57,6 +81,11 @@ const timestampMonthFormats = {
yearShort: 'MMM YYYY'
};
+/**
+ * Consistent timestamp quarter formats.
+ *
+ * @type {{short: string, yearShort: string, yearLong: string, long: string}}
+ */
const timestampQuarterFormats = {
...timestampMonthFormats
};
diff --git a/src/common/helpers.js b/src/common/helpers.js
index 6e2fb2df3..d6166b110 100644
--- a/src/common/helpers.js
+++ b/src/common/helpers.js
@@ -1,44 +1,171 @@
+/**
+ * Generate a random'ish ID.
+ *
+ * @param {string} prefix
+ * @returns {string}
+ */
const generateId = prefix =>
`${prefix || 'generatedid'}-${(process.env.REACT_APP_ENV !== 'test' && Math.ceil(1e5 * Math.random())) || ''}`;
+// ToDo: expand to include "async" check in scenarios where async/await are utilized.
+/**
+ * Check if "is a Promise".
+ *
+ * @param {Promise|*} obj
+ * @returns {boolean}
+ */
const isPromise = obj => Object.prototype.toString.call(obj) === '[object Promise]';
+/**
+ * An empty function.
+ * Typically used as a default prop.
+ */
const noop = Function.prototype;
+/**
+ * An empty promise.
+ * Typically used as a default prop, or during testing.
+ *
+ * @type {Promise<{}>}
+ */
const noopPromise = Promise.resolve({});
+/**
+ * A placeholder for "t", translation method.
+ * Associated with the i18n package, and typically used as a default prop.
+ *
+ * @param {string} key
+ * @param {string} value
+ * @returns {string}
+ */
const noopTranslate = (key, value) => `t(${key}${(value && `, ${value}`) || ''})`;
+/**
+ * Is dev mode active.
+ * Associated with using the NPM script "start". See dotenv config files for activation.
+ *
+ * @type {boolean}
+ */
const DEV_MODE = process.env.REACT_APP_ENV === 'development';
+/**
+ * Is prod mode active.
+ * Associated with production builds. See dotenv config files for activation.
+ *
+ * @type {boolean}
+ */
const PROD_MODE = process.env.REACT_APP_ENV === 'production';
+/**
+ * Is review/proxy mode active.
+ * Associated with using the NPM script "start:proxy". See dotenv config files for activation.
+ *
+ * @type {boolean}
+ */
const REVIEW_MODE = process.env.REACT_APP_ENV === 'review';
+/**
+ * Is test mode active.
+ * Associated with running unit tests. See dotenv config files for activation.
+ *
+ * @type {boolean}
+ */
const TEST_MODE = process.env.REACT_APP_ENV === 'test';
+/**
+ * Apply a path prefix for routing.
+ * Typically associated with applying a "beta" path prefix. See dotenv config files for updating. See build scripts for generated prefix.
+ *
+ * @type {string}
+ */
const UI_DEPLOY_PATH_PREFIX = process.env.REACT_APP_UI_DEPLOY_PATH_PREFIX;
+/**
+ * Disable an aspect of the UI.
+ * Typically associated with disabling views through route settings. See dotenv config files for activation.
+ *
+ * @type {boolean}
+ */
const UI_DISABLED = process.env.REACT_APP_UI_DISABLED === 'true';
+/**
+ * Disable the graph card aspect of the UI.
+ * See dotenv config files for activation.
+ *
+ * @type {boolean}
+ */
const UI_DISABLED_GRAPH = process.env.REACT_APP_UI_DISABLED_GRAPH === 'true';
+/**
+ * Disable the inventory/table aspect of the UI.
+ * See dotenv config files for activation.
+ *
+ * @type {boolean}
+ */
const UI_DISABLED_TABLE = process.env.REACT_APP_UI_DISABLED_TABLE === 'true';
+/**
+ * Disable the filter toolbar aspect of the UI.
+ * See dotenv config files for activation.
+ *
+ * @type {boolean}
+ */
const UI_DISABLED_TOOLBAR = process.env.REACT_APP_UI_DISABLED_TOOLBAR === 'true';
+/**
+ * UI application name.
+ * See dotenv config files for updating.
+ *
+ * @type {string}
+ */
const UI_DISPLAY_NAME = process.env.REACT_APP_UI_DISPLAY_NAME;
+/**
+ * UI application configuration name.
+ * See dotenv config files for updating.
+ *
+ * @type {string}
+ */
const UI_DISPLAY_CONFIG_NAME = process.env.REACT_APP_UI_DISPLAY_CONFIG_NAME;
+/**
+ * UI application sentence start name.
+ * See dotenv config files for updating.
+ *
+ * @type {string}
+ */
const UI_DISPLAY_START_NAME = process.env.REACT_APP_UI_DISPLAY_START_NAME;
+/**
+ * UI state logging name/id.
+ * See dotenv config files for updating.
+ *
+ * @type {string}
+ */
const UI_LOGGER_ID = process.env.REACT_APP_UI_LOGGER_ID || 'GUI';
+/**
+ * UI packaged application name.
+ * See dotenv config files for updating.
+ *
+ * @type {string}
+ */
const UI_NAME = process.env.REACT_APP_UI_NAME;
+/**
+ * UI packaged application path, with generated prefix.
+ * See dotenv config files for updating. See build scripts for generated prefix.
+ *
+ * @type {string}
+ */
const UI_PATH = process.env.PUBLIC_URL || '/';
+/**
+ * UI packaged application version, with generated hash.
+ * See dotenv config files for updating. See build scripts for generated hash.
+ *
+ * @type {string}
+ */
const UI_VERSION = process.env.REACT_APP_UI_VERSION;
const helpers = {
@@ -65,6 +192,17 @@ const helpers = {
UI_VERSION
};
+/**
+ * Expose an application specific type.
+ * Associated with access on a browser's developer console.
+ *
+ * @type {{UI_DISABLED_TOOLBAR: boolean, UI_DISPLAY_CONFIG_NAME: string, generateId: function(string): string,
+ * REVIEW_MODE: boolean, UI_LOGGER_ID: string, UI_DISABLED_GRAPH: boolean, UI_DISPLAY_START_NAME: string,
+ * UI_DEPLOY_PATH_PREFIX: string, UI_DISPLAY_NAME: string, noopPromise: Promise<{}>, DEV_MODE: boolean,
+ * TEST_MODE: boolean, noop: Function, isPromise: function((Promise|*)): boolean, UI_PATH: string,
+ * UI_NAME: string, UI_DISABLED: boolean, UI_DISABLED_TABLE: boolean, UI_VERSION: string, PROD_MODE: boolean,
+ * noopTranslate: function(string, string): string}}
+ */
window[UI_LOGGER_ID] = { ...helpers };
export { helpers as default, helpers };
diff --git a/src/components/app.js b/src/components/app.js
index d20d0abdb..b6e1af3cd 100644
--- a/src/components/app.js
+++ b/src/components/app.js
@@ -7,12 +7,22 @@ import { I18n } from './i18n/i18n';
import { Router } from './router/router';
import Authentication from './authentication/authentication';
+/**
+ * Application
+ *
+ * @augments React.Component
+ */
class App extends React.Component {
componentDidMount() {
const { getLocale } = this.props;
getLocale();
}
+ /**
+ * Render application.
+ *
+ * @returns {Node}
+ */
render() {
const { locale } = this.props;
@@ -27,6 +37,11 @@ class App extends React.Component {
}
}
+/**
+ * Prop types.
+ *
+ * @type {{locale: object, getLocale: Function}}
+ */
App.propTypes = {
getLocale: PropTypes.func,
locale: PropTypes.shape({
@@ -34,15 +49,32 @@ App.propTypes = {
})
};
+/**
+ * Default props.
+ *
+ * @type {{locale: {}, getLocale: Function}}
+ */
App.defaultProps = {
getLocale: helpers.noop,
locale: {}
};
+/**
+ * Apply actions to props.
+ *
+ * @param {Function} dispatch
+ * @returns {object}
+ */
const mapDispatchToProps = dispatch => ({
getLocale: () => dispatch(reduxActions.user.getLocale())
});
+/**
+ * Apply state to props.
+ *
+ * @param {object} state
+ * @returns {object}
+ */
const mapStateToProps = state => ({ locale: state.user.session.locale });
const ConnectedApp = connectRouter(mapStateToProps, mapDispatchToProps)(App);
diff --git a/src/components/authentication/authentication.js b/src/components/authentication/authentication.js
index c57bdc4fa..256e177f2 100644
--- a/src/components/authentication/authentication.js
+++ b/src/components/authentication/authentication.js
@@ -6,6 +6,11 @@ import { helpers } from '../../common';
import { Redirect, routerHelpers, routerTypes } from '../router/router';
import MessageView from '../messageView/messageView';
+/**
+ * An authentication pass-through component.
+ *
+ * @augments React.Component
+ */
class Authentication extends Component {
appName = routerTypes.appName;
@@ -48,6 +53,11 @@ class Authentication extends Component {
this.removeListeners();
}
+ /**
+ * Render authenticated children or messaging.
+ *
+ * @returns {Node}
+ */
render() {
const { children, session, t } = this.props;
@@ -76,6 +86,13 @@ class Authentication extends Component {
}
}
+/**
+ * Prop types.
+ *
+ * @type {{authorizeUser: Function, onNavigation: Function, setAppName: Function, navigation: Array,
+ * t: Function, children: Node, initializeChrome: Function, session: object, history: object,
+ * setNavigation: Function}}
+ */
Authentication.propTypes = {
authorizeUser: PropTypes.func,
children: PropTypes.node.isRequired,
@@ -102,6 +119,13 @@ Authentication.propTypes = {
t: PropTypes.func
};
+/**
+ * Default props.
+ *
+ * @type {{authorizeUser: Function, onNavigation: Function, setAppName: Function, navigation: Array,
+ * t: Function, initializeChrome: Function, session: {authorized: boolean, pending: boolean,
+ * errorMessage: string, errorStatus: null, error: boolean}, setNavigation: Function}}
+ */
Authentication.defaultProps = {
authorizeUser: helpers.noop,
initializeChrome: helpers.noop,
@@ -119,6 +143,12 @@ Authentication.defaultProps = {
t: helpers.noopTranslate
};
+/**
+ * Apply actions to props.
+ *
+ * @param {Function} dispatch
+ * @returns {object}
+ */
const mapDispatchToProps = dispatch => ({
authorizeUser: () => dispatch(reduxActions.user.authorizeUser()),
initializeChrome: () => dispatch(reduxActions.platform.initializeChrome()),
@@ -127,6 +157,12 @@ const mapDispatchToProps = dispatch => ({
setNavigation: data => dispatch(reduxActions.platform.setNavigation(data))
});
+/**
+ * Apply state to props.
+ *
+ * @param {object} state
+ * @returns {object}
+ */
const mapStateToProps = state => ({ session: state.user.session });
const ConnectedAuthentication = connectRouterTranslate(mapStateToProps, mapDispatchToProps)(Authentication);
diff --git a/src/components/chartArea/chartArea.js b/src/components/chartArea/chartArea.js
index 2637ffa15..1432bd535 100644
--- a/src/components/chartArea/chartArea.js
+++ b/src/components/chartArea/chartArea.js
@@ -14,6 +14,12 @@ import {
import _cloneDeep from 'lodash/cloneDeep';
import { helpers } from '../../common';
+/**
+ * A wrapper for Patternfly and Victory charts/graphs.
+ *
+ * @augments React.Component
+ * @fires onResizeContainer
+ */
class ChartArea extends React.Component {
state = { chartWidth: 0 };
@@ -28,6 +34,11 @@ class ChartArea extends React.Component {
window.removeEventListener('resize', this.onResizeContainer);
}
+ /**
+ * On window resize adjust graph display.
+ *
+ * @event onResizeContainer
+ */
onResizeContainer = () => {
const containerElement = this.containerRef.current;
@@ -36,6 +47,11 @@ class ChartArea extends React.Component {
}
};
+ /**
+ * Clone and update first dataSets list/array item with tooltip callbacks.
+ *
+ * @returns {object}
+ */
setDataSets() {
const { dataSets, tooltips } = this.props;
@@ -72,6 +88,11 @@ class ChartArea extends React.Component {
return dataSets;
}
+ /**
+ * Apply props, set x and y axis chart increments/ticks formatting.
+ *
+ * @returns {object}
+ */
setChartTicks() {
const { xAxisLabelIncrement, xAxisTickFormat, yAxisTickFormat, dataSets } = this.props;
const xAxisProps = {};
@@ -114,6 +135,11 @@ class ChartArea extends React.Component {
};
}
+ /**
+ * Return x and y axis increments/ticks.
+ *
+ * @returns {object}
+ */
getChartTicks() {
const { xAxisFixLabelOverlap } = this.props;
@@ -145,7 +171,13 @@ class ChartArea extends React.Component {
};
}
- // ToDo: the domain range needs to be update when additional datasets are added
+ // ToDo: the domain range needs to be updated when additional datasets are added
+ /**
+ * Calculate and return the x and y domain range.
+ *
+ * @param {boolean} isXAxisTicks
+ * @returns {object}
+ */
getChartDomain({ isXAxisTicks }) {
const { domain, dataSets } = this.props;
@@ -201,6 +233,11 @@ class ChartArea extends React.Component {
};
}
+ /**
+ * Apply props and return chart/graph legend.
+ *
+ * @returns {object}
+ */
getChartLegend() {
const { dataSets } = this.props;
const legendData = [];
@@ -235,6 +272,11 @@ class ChartArea extends React.Component {
};
}
+ /**
+ * Return a chart/graph container component. Aids in aggregated tooltips.
+ *
+ * @returns {Node}
+ */
static getContainerComponent() {
const VictoryVoronoiCursorContainer = createContainer('voronoi', 'cursor');
const containerComponentProps = {
@@ -259,6 +301,12 @@ class ChartArea extends React.Component {
};
}
+ /**
+ * Return a list/array of both stacked and non-stacked charts/graphs.
+ *
+ * @param {boolean} stacked
+ * @returns {Array}
+ */
renderChart({ stacked = false }) {
const dataSets = this.setDataSets();
const charts = [];
@@ -339,6 +387,11 @@ class ChartArea extends React.Component {
return (stacked && chartsStacked) || charts;
}
+ /**
+ * Render a chart/graph.
+ *
+ * @returns {Node}
+ */
render() {
const { chartWidth } = this.state;
const { padding, themeColor } = this.props;
@@ -371,6 +424,13 @@ class ChartArea extends React.Component {
}
}
+/**
+ * Prop types.
+ *
+ * @type {{padding, xAxisTickFormat: Function, themeColor: string, yAxisTickFormat: Function,
+ * domain: object|Array, dataSets: object, xAxisFixLabelOverlap: boolean, tooltips: Function,
+ * xAxisLabelIncrement: number, height: number}}
+ */
ChartArea.propTypes = {
dataSets: PropTypes.arrayOf(
PropTypes.shape({
@@ -415,6 +475,13 @@ ChartArea.propTypes = {
yAxisTickFormat: PropTypes.func
};
+/**
+ * Default props.
+ *
+ * @type {{padding: {top: number, left: number, bottom: number, right: number}, xAxisTickFormat: null,
+ * themeColor: string, yAxisTickFormat: null, domain: object, dataSets: Array, xAxisFixLabelOverlap: boolean,
+ * tooltips: null, xAxisLabelIncrement: number, height: number}}
+ */
ChartArea.defaultProps = {
domain: {},
dataSets: [],
diff --git a/src/components/graphCard/graphCard.js b/src/components/graphCard/graphCard.js
index 100d29ffa..a59e3d997 100644
--- a/src/components/graphCard/graphCard.js
+++ b/src/components/graphCard/graphCard.js
@@ -12,6 +12,13 @@ import { graphCardHelpers } from './graphCardHelpers';
import { graphCardTypes } from './graphCardTypes';
import ChartArea from '../chartArea/chartArea';
+/**
+ * A chart/graph card.
+ *
+ * @augments React.Component
+ * @fires onUpdateGraphData
+ * @fires onSelect
+ */
class GraphCard extends React.Component {
componentDidMount() {
this.onUpdateGraphData();
@@ -25,6 +32,11 @@ class GraphCard extends React.Component {
}
}
+ /**
+ * Call the RHSM APIs, apply filters.
+ *
+ * @event onUpdateGraphData
+ */
onUpdateGraphData = () => {
const { getGraphReportsCapacity, graphQuery, isDisabled, productId } = this.props;
const graphGranularity = graphQuery && graphQuery[rhsmApiTypes.RHSM_API_QUERY_GRANULARITY];
@@ -41,6 +53,12 @@ class GraphCard extends React.Component {
}
};
+ /**
+ * On granularity select, dispatch granularity type.
+ *
+ * @event onSelect
+ * @param {object} event
+ */
onSelect = (event = {}) => {
const { value } = event;
@@ -52,7 +70,13 @@ class GraphCard extends React.Component {
/**
* FixMe: custom use of dash over threshold vs updating PF Charts legend threshold symbol
- * @patternfly/react-tokens chart_threshold_stroke_dash_array and chart_threshold_stroke_Width
+ *
+ * patternfly/react-tokens chart_threshold_stroke_dash_array and chart_threshold_stroke_Width
+ */
+ /**
+ * Apply props to chart/graph.
+ *
+ * @returns {Node}
*/
renderChart() {
const { filterGraphData, graphData, graphQuery, selectOptionsType, t, productShortLabel } = this.props;
@@ -123,6 +147,11 @@ class GraphCard extends React.Component {
return ;
}
+ /**
+ * Render a chart/graph card with chart/graph.
+ *
+ * @returns {Node}
+ */
render() {
const { cardTitle, children, error, graphQuery, isDisabled, selectOptionsType, pending, t } = this.props;
@@ -166,6 +195,13 @@ class GraphCard extends React.Component {
}
}
+/**
+ * Prop types.
+ *
+ * @type {{productId: string, pending: boolean, error: boolean, graphQuery: object, cardTitle: string,
+ * filterGraphData: Array, getGraphReportsCapacity: Function, productShortLabel: string, selectOptionsType: string,
+ * viewId: string, t: Function, children: Node, graphData: object, isDisabled: boolean}}
+ */
GraphCard.propTypes = {
cardTitle: PropTypes.string,
children: PropTypes.node,
@@ -191,6 +227,13 @@ GraphCard.propTypes = {
viewId: PropTypes.string
};
+/**
+ * Default props.
+ *
+ * @type {{getGraphReportsCapacity: Function, productShortLabel: string, selectOptionsType: string,
+ * viewId: string, t: Function, children: null, pending: boolean, graphData: object,
+ * isDisabled: boolean, error: boolean, cardTitle: null, filterGraphData: Array}}
+ */
GraphCard.defaultProps = {
cardTitle: null,
children: null,
@@ -206,8 +249,19 @@ GraphCard.defaultProps = {
viewId: 'graphCard'
};
+/**
+ * Create a selector from applied state, props.
+ *
+ * @type {Function}
+ */
const makeMapStateToProps = reduxSelectors.graphCard.makeGraphCard();
+/**
+ * Apply actions to props.
+ *
+ * @param {Function} dispatch
+ * @returns {object}
+ */
const mapDispatchToProps = dispatch => ({
getGraphReportsCapacity: (id, query) => dispatch(reduxActions.rhsm.getGraphReportsCapacity(id, query))
});
diff --git a/src/components/graphCard/graphCardHelpers.js b/src/components/graphCard/graphCardHelpers.js
index 9878cb96c..f92767499 100644
--- a/src/components/graphCard/graphCardHelpers.js
+++ b/src/components/graphCard/graphCardHelpers.js
@@ -7,7 +7,7 @@ import { dateHelpers } from '../../common/dateHelpers';
/**
* Returns x axis ticks/intervals array for the xAxisTickInterval
*
- * @param {string} granularity, see enum of RHSM_API_QUERY_GRANULARITY_TYPES
+ * @param {string} granularity See enum of RHSM_API_QUERY_GRANULARITY_TYPES
* @returns {number}
*/
const getChartXAxisLabelIncrement = granularity => {
@@ -26,8 +26,9 @@ const getChartXAxisLabelIncrement = granularity => {
/**
* Return a formatted date string.
*
- * @param {Date} date
- * @param {string} granularity, see enum of RHSM_API_QUERY_GRANULARITY_TYPES
+ * @param {object} params
+ * @property {Date} date
+ * @property {string} granularity See enum of RHSM_API_QUERY_GRANULARITY_TYPES
* @returns {string}
*/
const getTooltipDate = ({ date, granularity }) => {
@@ -51,18 +52,19 @@ const getTooltipDate = ({ date, granularity }) => {
return momentDate.format(formattedDateTooltip);
};
+/**
+ * ToDo: we have access to the datasets and index which gives us access to the previous date.
+ * Consider adding back in the year on tooltip cross year displays
+ */
/**
* Get tooltips for x axis while displaying y axis values.
*
- * @param {Object} itemsByKey
- * @param {string} granularity, see enum of RHSM_API_QUERY_GRANULARITY_TYPES
- * @param {string} product, apply the product to locale strings
+ * @param {object} params
+ * @property {object} itemsByKey
+ * @property {string} granularity See enum of RHSM_API_QUERY_GRANULARITY_TYPES
+ * @property {string} product Apply the product to locale strings
* @returns {string | *}
*/
-/**
- * ToDo: we have access to the datasets and index which gives us access to the previous date.
- * Consider adding back in the year on tooltip cross year displays
- */
const getTooltips = ({ itemsByKey, granularity, product = '' }) => {
let dateString = '';
let thresholdString = '';
@@ -94,10 +96,11 @@ const getTooltips = ({ itemsByKey, granularity, product = '' }) => {
/**
* Format x axis ticks.
*
- * @param {Date} date
- * @param {string} granularity, see enum of RHSM_API_QUERY_GRANULARITY_TYPES
- * @param {number|string} tick
- * @param {Date} previousDate
+ * @param {object} params
+ * @property {Date} date
+ * @property {string} granularity See enum of RHSM_API_QUERY_GRANULARITY_TYPES
+ * @property {number|string} tick
+ * @property {Date} previousDate
* @returns {string|undefined}
*/
const xAxisTickFormat = ({ date, granularity, tick, previousDate }) => {
@@ -142,7 +145,8 @@ const xAxisTickFormat = ({ date, granularity, tick, previousDate }) => {
/**
* Format y axis ticks.
*
- * @param {number|string} tick
+ * @param {object} params
+ * @property {number|string} tick
* @returns {string}
*/
const yAxisTickFormat = ({ tick }) => numbro(tick).format({ average: true, mantissa: 1, optionalMantissa: true });
diff --git a/src/components/graphCard/graphCardTypes.js b/src/components/graphCard/graphCardTypes.js
index 5d0342906..8a0996c00 100644
--- a/src/components/graphCard/graphCardTypes.js
+++ b/src/components/graphCard/graphCardTypes.js
@@ -1,6 +1,12 @@
import { translate } from '../i18n/i18n';
import { RHSM_API_QUERY_GRANULARITY_TYPES as GRANULARITY_TYPES } from '../../types/rhsmApiTypes';
+/**
+ * Return a set of options for display in a select/dropdown list.
+ *
+ * @param {string} optionsType
+ * @returns {object}
+ */
const getGranularityOptionsType = optionsType => {
if (optionsType === 'default') {
return {
diff --git a/src/components/i18n/__tests__/__snapshots__/i18n.test.js.snap b/src/components/i18n/__tests__/__snapshots__/i18n.test.js.snap
index 754003e09..fcd0b88c9 100644
--- a/src/components/i18n/__tests__/__snapshots__/i18n.test.js.snap
+++ b/src/components/i18n/__tests__/__snapshots__/i18n.test.js.snap
@@ -14,114 +14,114 @@ exports[`I18n Component should generate a predictable pot output snapshot: pot o
msgstr \\"\\"
\\"Content-Type: text/plain; charset=UTF-8\\\\n\\"
-#: src/components/openshiftView/openshiftView.js:70
-#: src/components/rhelView/rhelView.js:33
+#: src/components/openshiftView/openshiftView.js:92
+#: src/components/rhelView/rhelView.js:43
msgid \\"curiosity-graph.cardHeading\\"
msgstr \\"\\"
-#: src/components/graphCard/graphCardHelpers.js:73
+#: src/components/graphCard/graphCardHelpers.js:75
msgid \\"curiosity-graph.dateLabel\\"
msgstr \\"\\"
-#: src/components/graphCard/graphCardTypes.js:9
+#: src/components/graphCard/graphCardTypes.js:15
msgid \\"curiosity-graph.dropdownDaily\\"
msgstr \\"\\"
-#: src/components/graphCard/graphCardTypes.js:11
+#: src/components/graphCard/graphCardTypes.js:17
msgid \\"curiosity-graph.dropdownMonthly\\"
msgstr \\"\\"
-#: src/components/graphCard/graphCard.js:143
-#: src/components/graphCard/graphCard.js:147
+#: src/components/graphCard/graphCard.js:172
+#: src/components/graphCard/graphCard.js:176
msgid \\"curiosity-graph.dropdownPlaceholder\\"
msgstr \\"\\"
-#: src/components/graphCard/graphCardTypes.js:12
+#: src/components/graphCard/graphCardTypes.js:18
msgid \\"curiosity-graph.dropdownQuarterly\\"
msgstr \\"\\"
-#: src/components/graphCard/graphCardTypes.js:10
+#: src/components/graphCard/graphCardTypes.js:16
msgid \\"curiosity-graph.dropdownWeekly\\"
msgstr \\"\\"
-#: src/components/graphCard/graphCardHelpers.js:90
+#: src/components/graphCard/graphCardHelpers.js:92
msgid \\"curiosity-graph.noDataLabel\\"
msgstr \\"\\"
-#: src/components/graphCard/graphCard.js:108
-#: src/components/graphCard/graphCardHelpers.js:81
+#: src/components/graphCard/graphCard.js:132
+#: src/components/graphCard/graphCardHelpers.js:83
msgid \\"curiosity-graph.thresholdLabel\\"
msgstr \\"\\"
-#: src/components/toolbar/toolbar.js:73
-#: src/components/toolbar/toolbar.js:76
+#: src/components/toolbar/toolbar.js:102
+#: src/components/toolbar/toolbar.js:105
msgid \\"curiosity-toolbar.slaCategory\\"
msgstr \\"\\"
-#: src/components/toolbar/toolbarTypes.js:22
+#: src/components/toolbar/toolbarTypes.js:28
msgid \\"curiosity-toolbar.slaNone\\"
msgstr \\"\\"
-#: src/components/toolbar/toolbar.js:79
+#: src/components/toolbar/toolbar.js:108
msgid \\"curiosity-toolbar.slaPlaceholder\\"
msgstr \\"\\"
-#: src/components/toolbar/toolbarTypes.js:10
+#: src/components/toolbar/toolbarTypes.js:16
msgid \\"curiosity-toolbar.slaPremium\\"
msgstr \\"\\"
-#: src/components/toolbar/toolbarTypes.js:18
+#: src/components/toolbar/toolbarTypes.js:24
msgid \\"curiosity-toolbar.slaSelfSupport\\"
msgstr \\"\\"
-#: src/components/toolbar/toolbarTypes.js:14
+#: src/components/toolbar/toolbarTypes.js:20
msgid \\"curiosity-toolbar.slaStandard\\"
msgstr \\"\\"
-#: src/components/tourView/tourView.js:64
+#: src/components/tourView/tourView.js:69
msgid \\"curiosity-tour.emptyStateButton\\"
msgstr \\"\\"
-#: src/components/tourView/tourView.js:42
+#: src/components/tourView/tourView.js:47
msgid \\"curiosity-tour.emptyStateDescription\\"
msgstr \\"\\"
-#: src/components/tourView/tourView.js:49
+#: src/components/tourView/tourView.js:54
msgid \\"curiosity-tour.emptyStateDescriptionExtended\\"
msgstr \\"\\"
-#: src/components/tourView/tourView.js:44
+#: src/components/tourView/tourView.js:49
msgid \\"curiosity-tour.emptyStateDescriptionTour\\"
msgstr \\"\\"
-#: src/components/tourView/tourView.js:32
+#: src/components/tourView/tourView.js:37
msgid \\"curiosity-tour.emptyStateIconAlt\\"
msgstr \\"\\"
-#: src/components/tourView/tourView.js:79
+#: src/components/tourView/tourView.js:84
msgid \\"curiosity-tour.emptyStateLinkContactUs\\"
msgstr \\"\\"
-#: src/components/tourView/tourView.js:60
-#: src/components/tourView/tourView.js:75
+#: src/components/tourView/tourView.js:65
+#: src/components/tourView/tourView.js:80
msgid \\"curiosity-tour.emptyStateLinkLearnMore\\"
msgstr \\"\\"
-#: src/components/tourView/tourView.js:37
+#: src/components/tourView/tourView.js:42
msgid \\"curiosity-tour.emptyStateTitle\\"
msgstr \\"\\"
-#: src/components/authentication/authentication.js:72
+#: src/components/authentication/authentication.js:82
msgctxt \\"...\\"
msgid \\"curiosity-auth.authorizedCopy\\"
msgstr \\"\\"
-#: src/components/authentication/authentication.js:71
+#: src/components/authentication/authentication.js:81
msgctxt \\"...\\"
msgid \\"curiosity-auth.authorizedTitle\\"
msgstr \\"\\"
-#: src/components/authentication/authentication.js:59
+#: src/components/authentication/authentication.js:69
msgctxt \\"...\\"
msgid \\"curiosity-auth.pending\\"
msgstr \\"\\"
diff --git a/src/components/i18n/__tests__/i18n.test.js b/src/components/i18n/__tests__/i18n.test.js
index 93d9a0bee..f14039d58 100644
--- a/src/components/i18n/__tests__/i18n.test.js
+++ b/src/components/i18n/__tests__/i18n.test.js
@@ -14,6 +14,8 @@ jest.mock('i18next');
/**
* Help generate a POT output.
+ *
+ * @returns {Function}
*/
const textExtractor = () => {
const extractor = new GettextExtractor();
diff --git a/src/components/i18n/i18n.js b/src/components/i18n/i18n.js
index 80739a0a2..c642c0c34 100644
--- a/src/components/i18n/i18n.js
+++ b/src/components/i18n/i18n.js
@@ -16,6 +16,11 @@ const translateComponent = component => (!helpers.TEST_MODE && withTranslation()
* ajax/xhr setup. Reverting it to just a function call that populates behind
* the scenes appears more predictable.
*/
+/**
+ * Load I18n.
+ *
+ * @augments React.Component
+ */
class I18n extends React.Component {
state = { isLoaded: false };
@@ -31,6 +36,11 @@ class I18n extends React.Component {
}
}
+ /**
+ * Load i18next.
+ *
+ * @returns {Promise}
+ */
i18nInit = async () => {
const { fallbackLng, loadPath, locale } = this.props;
@@ -58,6 +68,11 @@ class I18n extends React.Component {
this.setState({ isLoaded: true });
};
+ /**
+ * Render children after i18next loads.
+ *
+ * @returns {Node}
+ */
render() {
const { isLoaded } = this.state;
const { children } = this.props;
@@ -66,6 +81,11 @@ class I18n extends React.Component {
}
}
+/**
+ * Prop types.
+ *
+ * @type {{loadPath: string, children: Node, locale: string, fallbackLng: string}}
+ */
I18n.propTypes = {
children: PropTypes.node.isRequired,
fallbackLng: PropTypes.string,
@@ -73,6 +93,11 @@ I18n.propTypes = {
locale: PropTypes.string
};
+/**
+ * Default props.
+ *
+ * @type {{loadPath: string, locale: null, fallbackLng: string}}
+ */
I18n.defaultProps = {
fallbackLng: process.env.REACT_APP_CONFIG_SERVICE_LOCALES_DEFAULT_LNG,
loadPath: process.env.REACT_APP_CONFIG_SERVICE_LOCALES_PATH,
diff --git a/src/components/messageView/messageView.js b/src/components/messageView/messageView.js
index d75f1eba7..88dbda217 100644
--- a/src/components/messageView/messageView.js
+++ b/src/components/messageView/messageView.js
@@ -8,6 +8,11 @@ import { helpers } from '../../common';
* FixMe: Patternfly EmptyStateIcon PropType is not intuitive
* Requires the use of a function proptype?!?
*/
+/**
+ * Render a message view.
+ *
+ * @returns {Node}
+ */
const MessageView = ({ icon, message, title }) => (
{title || helpers.UI_DISPLAY_CONFIG_NAME}
@@ -18,12 +23,22 @@ const MessageView = ({ icon, message, title }) => (
);
+/**
+ * Prop types.
+ *
+ * @type {{icon: Node|Function, message: string, title: string}}
+ */
MessageView.propTypes = {
icon: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
message: PropTypes.string,
title: PropTypes.string
};
+/**
+ * Default props.
+ *
+ * @type {{icon: null, message: null, title: null}}
+ */
MessageView.defaultProps = {
icon: null,
message: null,
diff --git a/src/components/openshiftView/openshiftView.js b/src/components/openshiftView/openshiftView.js
index f3bd55443..f8b4c526f 100644
--- a/src/components/openshiftView/openshiftView.js
+++ b/src/components/openshiftView/openshiftView.js
@@ -12,6 +12,12 @@ import { Select } from '../select/select';
import Toolbar from '../toolbar/toolbar';
import { helpers } from '../../common';
+/**
+ * An OpenShift encompassing view.
+ *
+ * @augments React.Component
+ * @fires onSelect
+ */
class OpenshiftView extends React.Component {
state = {
option: null,
@@ -23,6 +29,12 @@ class OpenshiftView extends React.Component {
this.onSelect({ value: initialOption });
}
+ /**
+ * Apply a selected filtered value.
+ *
+ * @event onSelect
+ * @param {object} event
+ */
onSelect = (event = {}) => {
const { option } = this.state;
const { initialFilters } = this.props;
@@ -37,6 +49,11 @@ class OpenshiftView extends React.Component {
}
};
+ /**
+ * Render a select/dropdown list.
+ *
+ * @returns {Node}
+ */
renderSelect() {
const { option } = this.state;
const { initialOption } = this.props;
@@ -48,6 +65,11 @@ class OpenshiftView extends React.Component {
return ;
}
+ /**
+ * Render an OpenShift view.
+ *
+ * @returns {Node}
+ */
render() {
const { filters } = this.state;
const { graphQuery, routeDetail, t } = this.props;
@@ -78,6 +100,11 @@ class OpenshiftView extends React.Component {
}
}
+/**
+ * Prop types.
+ *
+ * @type {{initialFilters: Array, initialOption: string, t: Function, routeDetail: object, graphQuery: object}}
+ */
OpenshiftView.propTypes = {
graphQuery: PropTypes.shape({
[rhsmApiTypes.RHSM_API_QUERY_GRANULARITY]: PropTypes.oneOf([...Object.values(GRANULARITY_TYPES)])
@@ -94,6 +121,11 @@ OpenshiftView.propTypes = {
t: PropTypes.func
};
+/**
+ * Default props.
+ *
+ * @type {{initialFilters: Array, initialOption: string, t: Function, graphQuery: object}}
+ */
OpenshiftView.defaultProps = {
graphQuery: {
[rhsmApiTypes.RHSM_API_QUERY_GRANULARITY]: GRANULARITY_TYPES.DAILY
@@ -108,6 +140,11 @@ OpenshiftView.defaultProps = {
t: helpers.noopTranslate
};
+/**
+ * Create a selector from applied state, props.
+ *
+ * @type {Function}
+ */
const makeMapStateToProps = reduxSelectors.view.makeView(OpenshiftView.defaultProps);
const ConnectedOpenshiftView = connectTranslate(makeMapStateToProps)(OpenshiftView);
diff --git a/src/components/pageLayout/pageHeader.js b/src/components/pageLayout/pageHeader.js
index 1a7b762ba..81fefc256 100644
--- a/src/components/pageLayout/pageHeader.js
+++ b/src/components/pageLayout/pageHeader.js
@@ -5,16 +5,29 @@ import {
PageHeaderTitle
} from '@redhat-cloud-services/frontend-components/components/PageHeader';
+/**
+ * Render a platform page header.
+ *
+ * @returns {Node}
+ */
const PageHeader = ({ children }) => (
);
+/**
+ * Prop types.
+ *
+ * @type {{children: Node}}
+ */
PageHeader.propTypes = {
children: PropTypes.node.isRequired
};
+/**
+ * Default props.
+ */
PageHeader.defaultProps = {};
export { PageHeader as default, PageHeader };
diff --git a/src/components/pageLayout/pageLayout.js b/src/components/pageLayout/pageLayout.js
index 151988e27..168f1bfc6 100644
--- a/src/components/pageLayout/pageLayout.js
+++ b/src/components/pageLayout/pageLayout.js
@@ -9,6 +9,11 @@ import { PageToolbar } from './pageToolbar';
* ToDo: Reevaluate, import for Main component from @redhat-cloud-services/frontend-components
* Fallback towards PF PageSection. Named export for Main is overridden by default connected export.
*/
+/**
+ * Render a platform page layout.
+ *
+ * @returns {Node}
+ */
const PageLayout = ({ children }) => (
{React.Children.toArray(children).filter(child => React.isValidElement(child) && child.type === PageHeader)}
@@ -19,10 +24,18 @@ const PageLayout = ({ children }) => (
);
+/**
+ * Prop types.
+ *
+ * @type {{children: Node}}
+ */
PageLayout.propTypes = {
children: PropTypes.node.isRequired
};
+/**
+ * Default props.
+ */
PageLayout.defaultProps = {};
export { PageLayout as default, PageLayout, PageHeader, PageSection, PageToolbar };
diff --git a/src/components/pageLayout/pageSection.js b/src/components/pageLayout/pageSection.js
index 06dd11048..744fa882d 100644
--- a/src/components/pageLayout/pageSection.js
+++ b/src/components/pageLayout/pageSection.js
@@ -2,12 +2,25 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Section } from '@redhat-cloud-services/frontend-components/components/Section';
+/**
+ * Render a platform page section.
+ *
+ * @returns {Node}
+ */
const PageSection = ({ children, ...props }) => ;
+/**
+ * Prop types.
+ *
+ * @type {{children: Node}}
+ */
PageSection.propTypes = {
children: PropTypes.node.isRequired
};
+/**
+ * Default props.
+ */
PageSection.defaultProps = {};
export { PageSection as default, PageSection };
diff --git a/src/components/pageLayout/pageToolbar.js b/src/components/pageLayout/pageToolbar.js
index e216964b2..864cfffe3 100644
--- a/src/components/pageLayout/pageToolbar.js
+++ b/src/components/pageLayout/pageToolbar.js
@@ -2,12 +2,25 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Section } from '@redhat-cloud-services/frontend-components/components/Section';
+/**
+ * Render a platform toolbar section.
+ *
+ * @returns {Node}
+ */
const PageToolbar = ({ children, ...props }) => ;
+/**
+ * Prop types.
+ *
+ * @type {{children: Node}}
+ */
PageToolbar.propTypes = {
children: PropTypes.node.isRequired
};
+/**
+ * Default props.
+ */
PageToolbar.defaultProps = {};
export { PageToolbar as default, PageToolbar };
diff --git a/src/components/rhelView/rhelView.js b/src/components/rhelView/rhelView.js
index d50e523e0..d181c6aee 100644
--- a/src/components/rhelView/rhelView.js
+++ b/src/components/rhelView/rhelView.js
@@ -12,9 +12,19 @@ import { connectTranslate, reduxSelectors } from '../../redux';
import GraphCard from '../graphCard/graphCard';
import { helpers } from '../../common';
+/**
+ * A Red Hat Enterprise Linux encompassing view, and system architectures.
+ *
+ * @augments React.Component
+ */
class RhelView extends React.Component {
componentDidMount() {}
+ /**
+ * Render a RHEL view.
+ *
+ * @returns {Node}
+ */
render() {
const { graphQuery, initialFilters, routeDetail, t } = this.props;
@@ -39,6 +49,11 @@ class RhelView extends React.Component {
}
}
+/**
+ * Prop types.
+ *
+ * @type {{initialFilters: Array, t: Function, routeDetail: object, graphQuery: object}}
+ */
RhelView.propTypes = {
graphQuery: PropTypes.shape({
[rhsmApiTypes.RHSM_API_QUERY_GRANULARITY]: PropTypes.oneOf([...Object.values(GRANULARITY_TYPES)])
@@ -54,6 +69,11 @@ RhelView.propTypes = {
t: PropTypes.func
};
+/**
+ * Default props.
+ *
+ * @type {{initialFilters: Array, t: Function, graphQuery: object}}
+ */
RhelView.defaultProps = {
graphQuery: {
[rhsmApiTypes.RHSM_API_QUERY_GRANULARITY]: GRANULARITY_TYPES.DAILY
@@ -66,6 +86,11 @@ RhelView.defaultProps = {
t: helpers.noopTranslate
};
+/**
+ * Create a selector from applied state, props.
+ *
+ * @type {Function}
+ */
const makeMapStateToProps = reduxSelectors.view.makeView(RhelView.defaultProps);
const ConnectedRhelView = connectTranslate(makeMapStateToProps)(RhelView);
diff --git a/src/components/router/redirect.js b/src/components/router/redirect.js
index d6f8415cc..d4690725c 100644
--- a/src/components/router/redirect.js
+++ b/src/components/router/redirect.js
@@ -5,6 +5,11 @@ import { withRouter, Route } from 'react-router-dom';
import { routerHelpers } from './routerHelpers';
import { helpers } from '../../common';
+/**
+ * A routing redirect.
+ *
+ * @returns {Node}
+ */
const Redirect = ({ baseName, history, isRedirect, isReplace, url, route }) => {
const forceNavigation = urlRoute => {
if (!helpers.DEV_MODE && !helpers.TEST_MODE) {
@@ -34,6 +39,11 @@ const Redirect = ({ baseName, history, isRedirect, isReplace, url, route }) => {
return null;
};
+/**
+ * Prop types.
+ *
+ * @type {{isRedirect: boolean, route: string, isReplace: boolean, history: object, baseName: string, url: string}}
+ */
Redirect.propTypes = {
history: PropTypes.object,
isRedirect: PropTypes.bool.isRequired,
@@ -43,6 +53,11 @@ Redirect.propTypes = {
route: PropTypes.string
};
+/**
+ * Default props.
+ *
+ * @type {{route: null, isReplace: boolean, history: null, baseName: string, url: null}}
+ */
Redirect.defaultProps = {
history: null,
isReplace: false,
diff --git a/src/components/router/router.js b/src/components/router/router.js
index 6913c3f29..0a5ca9c26 100644
--- a/src/components/router/router.js
+++ b/src/components/router/router.js
@@ -5,7 +5,17 @@ import Redirect from './redirect';
import { routerHelpers } from './routerHelpers';
import { routerTypes } from './routerTypes';
+/**
+ * Load routes.
+ *
+ * @augments React.Component
+ */
class Router extends React.Component {
+ /**
+ * Parse settings array with route options.
+ *
+ * @returns {{redirectRoot: Node, renderRoutes: Array}}
+ */
renderRoutes() {
const { routes } = this.props;
const activateOnErrorRoute = routes.find(route => route.activateOnError === true);
@@ -60,6 +70,11 @@ class Router extends React.Component {
};
}
+ /**
+ * Render router.
+ *
+ * @returns {Node}
+ */
render() {
const { renderRoutes, redirectRoot } = this.renderRoutes();
@@ -72,10 +87,32 @@ class Router extends React.Component {
}
}
+/**
+ * Prop types.
+ *
+ * @type {{routes: Array}}
+ */
Router.propTypes = {
- routes: PropTypes.array
+ routes: PropTypes.arrayOf(
+ PropTypes.shape({
+ activateOnError: PropTypes.boolean,
+ component: PropTypes.any.isRequired,
+ disabled: PropTypes.boolean,
+ exact: PropTypes.boolean,
+ hasParameters: PropTypes.boolean,
+ redirect: PropTypes.boolean,
+ render: PropTypes.boolean,
+ strict: PropTypes.boolean,
+ to: PropTypes.string.isRequired
+ })
+ )
};
+/**
+ * Default props.
+ *
+ * @type {{routes: Array}}
+ */
Router.defaultProps = {
routes: routerTypes.routes
};
diff --git a/src/components/router/routerHelpers.js b/src/components/router/routerHelpers.js
index a7e3b4cc1..16295aa5d 100644
--- a/src/components/router/routerHelpers.js
+++ b/src/components/router/routerHelpers.js
@@ -6,9 +6,10 @@ import { routes, navigation } from './routerTypes';
* based on a predictable platform directory depth of
* /[OPTIONAL]/[environment]/[APP NAME]
*
- * @param pathName {string}
- * @param pathPrefix {string}
- * @return {string}
+ * @param {object} params
+ * @property {string} pathName
+ * @property {string} pathPrefix
+ * @returns {string}
*/
const dynamicBaseName = ({ pathName, pathPrefix }) => {
const path = pathName.split('/');
@@ -20,6 +21,7 @@ const dynamicBaseName = ({ pathName, pathPrefix }) => {
/**
* The app baseName.
+ *
* @type {string}
*/
const baseName =
@@ -29,16 +31,19 @@ const baseName =
/**
* The first error route.
- * @type {Object}
+ *
+ * @type {object}
*/
const getErrorRoute = routes.find(route => route.activateOnError === true) || {};
/**
* Return an object matching a specific navigation object.
- * @param id {string}
- * @param pathname {string}
- * @param returnDefault {boolean}
- * @returns {Object}
+ *
+ * @param {object} params
+ * @property {string} id
+ * @property {string} pathname
+ * @property {boolean} returnDefault
+ * @returns {object}
*/
const getNavigationDetail = ({ id = null, pathname = null, returnDefault = false }) => {
const defaultItem = returnDefault && navigation.find(item => item.default === true);
@@ -63,9 +68,11 @@ const getNavigationDetail = ({ id = null, pathname = null, returnDefault = false
/**
* Return an object matching a specific, or the first generic route.
- * @param id {string}
- * @param pathname {string}
- * @returns {Object}
+ *
+ * @param {object} params
+ * @property {string} id
+ * @property {string} pathname
+ * @returns {object}
*/
const getRouteDetail = ({ id = null, pathname = null }) => {
let routeItem;
@@ -87,10 +94,11 @@ const getRouteDetail = ({ id = null, pathname = null }) => {
* ID is not passed to "getRouteDetail" to avoid conflicts between routing and
* navigation.
*
- * @param id {string}
- * @param pathname {string}
- * @param returnDefault {boolean}
- * @returns {Object}
+ * @param {object} params
+ * @property {string} id
+ * @property {string} pathname
+ * @property {boolean} returnDefault
+ * @returns {object}
*/
const getNavRouteDetail = ({ id = null, pathname = null, returnDefault = false }) => {
const navDetail = getNavigationDetail({ id, pathname, returnDefault });
diff --git a/src/components/router/routerTypes.js b/src/components/router/routerTypes.js
index f8fd8e877..3ad2a1cde 100644
--- a/src/components/router/routerTypes.js
+++ b/src/components/router/routerTypes.js
@@ -7,18 +7,22 @@ import { RHSM_API_PATH_ID_TYPES } from '../../types/rhsmApiTypes';
/**
* Platform name/id.
+ *
+ * @type {string}
*/
const appName = helpers.UI_NAME;
/**
* Return a string that describes a platform redirect.
- * @return {array}
+ *
+ * @returns {Array}
*/
const platformRedirect = path.join(helpers.UI_DEPLOY_PATH_PREFIX, '/?not_entitled=subscriptions');
/**
* Return array of objects that describes routing.
- * @return {array}
+ *
+ * @returns {Array}
*/
const routes = [
{
@@ -71,7 +75,8 @@ const routes = [
/**
* Return an array of objects that describes platform navigation.
- * @return {array}
+ *
+ * @returns {Array}
*/
const navigation = [
{
diff --git a/src/components/select/select.js b/src/components/select/select.js
index ed1718660..23a26d69a 100644
--- a/src/components/select/select.js
+++ b/src/components/select/select.js
@@ -6,6 +6,13 @@ import _isEqual from 'lodash/isEqual';
import _isPlainObject from 'lodash/isPlainObject';
import { helpers } from '../../common/helpers';
+/**
+ * A wrapper for Patternfly Select. Provides additional event data for onSelect callback.
+ *
+ * @augments React.Component
+ * @fires onSelect
+ * @param expanded
+ */
class Select extends React.Component {
state = { isExpanded: false, options: null, selected: null };
@@ -25,6 +32,13 @@ class Select extends React.Component {
}
}
+ /**
+ * Emulate select event object, apply to provided onSelect prop.
+ *
+ * @event onSelect
+ * @param {object} event
+ * @param {string} titleSelection
+ */
onSelect = (event, titleSelection) => {
const { options } = this.state;
const { id, name, onSelect, variant } = this.props;
@@ -88,12 +102,20 @@ class Select extends React.Component {
);
};
+ /**
+ * Patternfly Select's open/closed state.
+ *
+ * @param {boolean} expanded
+ */
onToggle = expanded => {
this.setState({
isExpanded: expanded
});
};
+ /**
+ * Format options into a consumable array of objects format.
+ */
formatOptions() {
const { options, selectedOptions, variant } = this.props;
const updatedOptions = _isPlainObject(options)
@@ -141,6 +163,11 @@ class Select extends React.Component {
});
}
+ /**
+ * Render a select/dropdown list.
+ *
+ * @returns {Node}
+ */
render() {
const { options, selected, isExpanded } = this.state;
const { ariaLabel, className, isDisabled, placeholder, variant } = this.props;
@@ -185,6 +212,13 @@ class Select extends React.Component {
}
}
+/**
+ * Prop types.
+ *
+ * @type {{name: string, options: object, selectedOptions: (number|string|Array), variant: (object|string),
+ * className: string, id: string, isDisabled: boolean, placeholder: string, ariaLabel: string,
+ * onSelect: Function}}
+ */
Select.propTypes = {
ariaLabel: PropTypes.string,
className: PropTypes.string,
@@ -213,6 +247,13 @@ Select.propTypes = {
variant: PropTypes.oneOf([...Object.values(SelectVariant)])
};
+/**
+ * Default props.
+ *
+ * @type {{name: null, options: Array, selectedOptions: null, variant: SelectVariant.single,
+ * className: string, id: string, isDisabled: boolean, placeholder: string,
+ * ariaLabel: string, onSelect: Function}}
+ */
Select.defaultProps = {
ariaLabel: 'Select option',
className: '',
diff --git a/src/components/toolbar/toolbar.js b/src/components/toolbar/toolbar.js
index 5ca19d400..97b235f6a 100644
--- a/src/components/toolbar/toolbar.js
+++ b/src/components/toolbar/toolbar.js
@@ -7,9 +7,21 @@ import { reduxTypes, store } from '../../redux';
import { toolbarTypes } from './toolbarTypes';
import { helpers } from '../../common';
+/**
+ * Application filter toolbar.
+ *
+ * @augments React.Component
+ * @fires onClear
+ * @fires onSlaSelect
+ */
class Toolbar extends React.Component {
state = { filterSla: toolbarTypes.getOptions().selected };
+ /**
+ * Clear filters' state.
+ *
+ * @event onClear
+ */
onClear = () => {
this.setState(
{
@@ -21,6 +33,12 @@ class Toolbar extends React.Component {
);
};
+ /**
+ * Set SLA filter selection.
+ *
+ * @event onSlaSelect
+ * @param {object} event
+ */
onSlaSelect = event => {
const { selected, value } = event;
@@ -34,6 +52,12 @@ class Toolbar extends React.Component {
);
};
+ /**
+ * Dispatch a Redux store type.
+ *
+ * @param {string} type
+ * @param {object} data
+ */
dispatchFilter(type, data = {}) {
const { viewId } = this.props;
@@ -46,6 +70,11 @@ class Toolbar extends React.Component {
}
}
+ /**
+ * Render a filter toolbar.
+ *
+ * @returns {Node}
+ */
render() {
const { filterSla } = this.state;
const { isDisabled, t } = this.props;
@@ -87,12 +116,22 @@ class Toolbar extends React.Component {
}
}
+/**
+ * Prop types
+ *
+ * @type {{viewId: string, t: Function, isDisabled: boolean}}
+ */
Toolbar.propTypes = {
isDisabled: PropTypes.bool,
t: PropTypes.func,
viewId: PropTypes.string
};
+/**
+ * Default props.
+ *
+ * @type {{viewId: string, t: Function, isDisabled: boolean}}
+ */
Toolbar.defaultProps = {
isDisabled: helpers.UI_DISABLED_TOOLBAR,
t: helpers.noopTranslate,
diff --git a/src/components/toolbar/toolbarTypes.js b/src/components/toolbar/toolbarTypes.js
index cf71e6396..f9c84cd44 100644
--- a/src/components/toolbar/toolbarTypes.js
+++ b/src/components/toolbar/toolbarTypes.js
@@ -1,6 +1,12 @@
import { translate } from '../i18n/i18n';
import { RHSM_API_QUERY_SLA_TYPES as SLA_TYPES } from '../../types/rhsmApiTypes';
+/**
+ * Get filter options to display by type.
+ *
+ * @param {string} optionsType
+ * @returns {object}
+ */
const getOptionsType = (optionsType = 'sla') => {
if (optionsType === 'sla') {
return {
diff --git a/src/components/tourView/tourView.js b/src/components/tourView/tourView.js
index 961f0e12c..92715fcd9 100644
--- a/src/components/tourView/tourView.js
+++ b/src/components/tourView/tourView.js
@@ -21,6 +21,11 @@ import subscriptionsSvg from '../../images/subscriptions.svg';
* FixMe: Patternfly EmptyStateBody and Title appear to throw an error on translate string replacement
* Wrap with a fragment to pass.
*/
+/**
+ * Render a user guided tour view.
+ *
+ * @returns {Node} Node containing tour view.
+ */
const TourView = ({ session, t }) => (
{helpers.UI_DISPLAY_CONFIG_NAME}
@@ -84,6 +89,11 @@ const TourView = ({ session, t }) => (
);
+/**
+ * Prop types.
+ *
+ * @type {{t: Function, session: object}}
+ */
TourView.propTypes = {
session: PropTypes.shape({
errorStatus: PropTypes.number
@@ -91,6 +101,11 @@ TourView.propTypes = {
t: PropTypes.func
};
+/**
+ * Default props.
+ *
+ * @type {{t: Function, session: {errorStatus: null}}}
+ */
TourView.defaultProps = {
session: {
errorStatus: null
diff --git a/src/redux/actions/platformActions.js b/src/redux/actions/platformActions.js
index 9d1de2ff9..9c75e3fa8 100644
--- a/src/redux/actions/platformActions.js
+++ b/src/redux/actions/platformActions.js
@@ -6,17 +6,45 @@ import {
import { platformTypes } from '../types';
import { platformServices } from '../../services/platformServices';
+/**
+ * Add a platform plugin toast notification.
+ *
+ * @param {object} data
+ * @returns {*}
+ */
const addNotification = data => RcsAddNotification(data);
+/**
+ * Remove a platform plugin toast notification.
+ *
+ * @param {string} id
+ * @returns {*}
+ */
const removeNotification = id => RcsRemoveNotification(id);
+/**
+ * Clear all platform plugin toast notifications.
+ *
+ * @returns {*}
+ */
const clearNotifications = () => RcsClearNotifications();
+/**
+ * Apply platform method for initializing chrome, i.e. header, left-nav.
+ *
+ * @returns {{payload: Promise, type: string}}
+ */
const initializeChrome = () => ({
type: platformTypes.PLATFORM_INIT,
payload: platformServices.initializeChrome()
});
+/**
+ * Apply platform method for updating routing history on "navigating" with the left-nav.
+ *
+ * @param {Function} callback
+ * @returns {Function}
+ */
const onNavigation = callback => dispatch => {
dispatch({
type: platformTypes.PLATFORM_ON_NAV
@@ -24,6 +52,12 @@ const onNavigation = callback => dispatch => {
return platformServices.onNavigation(callback);
};
+/**
+ * Apply platform method for setting the application name/identifier.
+ *
+ * @param {string} name
+ * @returns {{payload: Promise, meta: {data: {name: *}}, type: string}}
+ */
const setAppName = name => ({
type: platformTypes.PLATFORM_APP_NAME,
payload: platformServices.setAppName(name),
@@ -32,6 +66,12 @@ const setAppName = name => ({
}
});
+/**
+ * Apply platform method for handling the left-nav navigation active item.
+ *
+ * @param {object} data
+ * @returns {Function}
+ */
const setNavigation = data => dispatch => {
dispatch({
type: platformTypes.PLATFORM_SET_NAV
diff --git a/src/redux/actions/rhsmActions.js b/src/redux/actions/rhsmActions.js
index e489cfd35..5738ea9e6 100644
--- a/src/redux/actions/rhsmActions.js
+++ b/src/redux/actions/rhsmActions.js
@@ -1,6 +1,13 @@
import { rhsmTypes } from '../types';
import { rhsmServices } from '../../services/rhsmServices';
+/**
+ * Get a combined RHSM response from reporting and capacity.
+ *
+ * @param {string} id
+ * @param {object} query
+ * @returns {Function}
+ */
const getGraphReportsCapacity = (id = null, query = {}) => dispatch =>
dispatch({
type: rhsmTypes.GET_GRAPH_REPORT_CAPACITY_RHSM,
diff --git a/src/redux/actions/userActions.js b/src/redux/actions/userActions.js
index 972a06216..814191d05 100644
--- a/src/redux/actions/userActions.js
+++ b/src/redux/actions/userActions.js
@@ -1,12 +1,22 @@
import { userTypes } from '../types';
import { userServices } from '../../services/userServices';
+/**
+ * Get an emulated API response from the platforms "getUser" method.
+ *
+ * @returns {Function}
+ */
const authorizeUser = () => dispatch =>
dispatch({
type: userTypes.USER_AUTH,
payload: userServices.authorizeUser()
});
+/**
+ * Get a user's locale.
+ *
+ * @returns {{payload: Promise<{data: void}>, type: string}}
+ */
const getLocale = () => ({
type: userTypes.USER_LOCALE,
payload: userServices.getLocale()
diff --git a/src/redux/common/reduxHelpers.js b/src/redux/common/reduxHelpers.js
index 4d06307f3..3cbcb8bee 100644
--- a/src/redux/common/reduxHelpers.js
+++ b/src/redux/common/reduxHelpers.js
@@ -2,12 +2,36 @@ import _get from 'lodash/get';
import _isPlainObject from 'lodash/isPlainObject';
import { helpers } from '../../common';
+/**
+ * Apply a "fulfilled" suffix for Redux Promise Middleware action responses.
+ *
+ * @param {string} base
+ * @returns {string}
+ */
const FULFILLED_ACTION = (base = '') => `${base}_FULFILLED`;
+/**
+ * Apply a "pending" suffix for Redux Promise Middleware action responses.
+ *
+ * @param {string} base
+ * @returns {string}
+ */
const PENDING_ACTION = (base = '') => `${base}_PENDING`;
+/**
+ * Apply a "rejected" suffix for Redux Promise Middleware action responses.
+ *
+ * @param {string} base
+ * @returns {string}
+ */
const REJECTED_ACTION = (base = '') => `${base}_REJECTED`;
+/**
+ * Apply a "status range" suffix for Status Middleware action responses.
+ *
+ * @param {string} status
+ * @returns {string}
+ */
const HTTP_STATUS_RANGE = status => `${status}_STATUS_RANGE`;
/**
@@ -15,9 +39,9 @@ const HTTP_STATUS_RANGE = status => `${status}_STATUS_RANGE`;
* form of [{ madeUpKey: 'some_api_key' }], or an array of arrays
* in the form of [['some_api_key','another_api_key']]
*
- * @param {array} schemas
+ * @param {Array} schemas
* @param {*} initialValue
- * @returns {unknown[]}
+ * @returns {Array}
*/
const setResponseSchemas = (schemas = [], initialValue) =>
schemas.map(schema => {
@@ -31,6 +55,13 @@ const setResponseSchemas = (schemas = [], initialValue) =>
return generated;
});
+/**
+ * Create a single response from an array of service call responses.
+ * Aids in handling a Promise.all response.
+ *
+ * @param {Array|object} results
+ * @returns {object}
+ */
const getSingleResponseFromResultArray = results => {
const updatedResults = results.payload || results;
@@ -44,6 +75,12 @@ const getSingleResponseFromResultArray = results => {
return updatedResults;
};
+/**
+ * Get a http status message from a service call.
+ *
+ * @param {Array|object} results
+ * @returns {string|null|*}
+ */
const getMessageFromResults = results => {
const updatedResults = getSingleResponseFromResultArray(results);
@@ -72,6 +109,12 @@ const getMessageFromResults = results => {
return (statusResponse && statusResponse.trim()) || null;
};
+/**
+ * Get a date string from a service call.
+ *
+ * @param {Array|object} results
+ * @returns {null|string|Date}
+ */
const getDateFromResults = results => {
const updatedResults = getSingleResponseFromResultArray(results);
@@ -82,6 +125,12 @@ const getDateFromResults = results => {
return _get(updatedResults, 'headers.date', null);
};
+/**
+ * Get a http status from a service call response.
+ *
+ * @param {Array|object} results
+ * @returns {number}
+ */
const getStatusFromResults = results => {
const updatedResults = getSingleResponseFromResultArray(results);
@@ -92,6 +141,17 @@ const getStatusFromResults = results => {
return _get(updatedResults, 'response.status', updatedResults.status) || 0;
};
+/**
+ * Convenience method for setting object properties, specifically Redux reducer based state objects.
+ *
+ * @param {string} prop
+ * @param {object} data
+ * @param {object} options
+ * @property {object} state
+ * @property {object} initialState
+ * @property {boolean} reset
+ * @returns {object}
+ */
const setStateProp = (prop, data, options) => {
const { state = {}, initialState = {}, reset = true } = options;
let obj = { ...state };
@@ -131,6 +191,12 @@ const setStateProp = (prop, data, options) => {
return obj;
};
+/**
+ * Retrieve a data property either from an array of responses, or a single response.
+ *
+ * @param {Array|object} results
+ * @returns {Array|object}
+ */
const singlePromiseDataResponseFromArray = results => {
const updatedResults = results.payload || results;
@@ -140,6 +206,15 @@ const singlePromiseDataResponseFromArray = results => {
return updatedResults.data || {};
};
+/**
+ * Automatically apply reducer logic to state by handling promise responses from redux-promise-middleware.
+ *
+ * @param {Array} types
+ * @param {object} state
+ * @param {object} action
+ * @property { string } type
+ * @returns {object}
+ */
const generatedPromiseActionReducer = (types = [], state = {}, action = {}) => {
const { type } = action;
const expandedTypes = [];
diff --git a/src/redux/middleware/index.js b/src/redux/middleware/index.js
index e313890fc..221e2f8c3 100644
--- a/src/redux/middleware/index.js
+++ b/src/redux/middleware/index.js
@@ -5,6 +5,13 @@ import { notificationsMiddleware } from '@redhat-cloud-services/frontend-compone
import { statusMiddleware } from './statusMiddleware';
import { reduxHelpers } from '../common/reduxHelpers';
+/**
+ * Platform notifications settings.
+ *
+ * @private
+ * @type {{errorDescriptionKey: string, autoDismiss: boolean, errorTitleKey: string, fulfilledSuffix: string,
+ * dispatchDefaultFailure: boolean, pendingSuffix: string, rejectedSuffix: string, dismissDelay: number}}
+ */
const notificationsOptions = {
dispatchDefaultFailure: false, // automatic error notifications
pendingSuffix: reduxHelpers.PENDING_ACTION(), // pending state action suffix
@@ -16,6 +23,11 @@ const notificationsOptions = {
errorDescriptionKey: 'detail' // path to notification description in error response
};
+/**
+ * Redux middleware.
+ *
+ * @type {Array}
+ */
const reduxMiddleware = [
thunkMiddleware,
statusMiddleware(),
diff --git a/src/redux/middleware/statusMiddleware.js b/src/redux/middleware/statusMiddleware.js
index 99ff2f3ed..20e38a3cd 100644
--- a/src/redux/middleware/statusMiddleware.js
+++ b/src/redux/middleware/statusMiddleware.js
@@ -1,5 +1,17 @@
import { reduxHelpers } from '../common/reduxHelpers';
+/**
+ * Apply a status type based on actions, such as those generated from redux-promise-middleware.
+ *
+ * @param {object} config
+ * @property {string} statusSuffix
+ * @property {string} rangeSuffix
+ * @property {string} rangeFiller
+ * @property {string} statusDelimiter
+ * @property {boolean} statusRange
+ * @property {boolean} dispatchStatus
+ * @returns {Function}
+ */
const statusMiddleware = (config = {}) => {
const statusSuffix = config.statusSuffix || 'STATUS';
const rangeSuffix = config.rangeSuffix || 'STATUS_RANGE';
diff --git a/src/redux/reducers/graphReducer.js b/src/redux/reducers/graphReducer.js
index 911b64bbf..464935e92 100644
--- a/src/redux/reducers/graphReducer.js
+++ b/src/redux/reducers/graphReducer.js
@@ -1,10 +1,23 @@
import { rhsmTypes } from '../types';
import { reduxHelpers } from '../common/reduxHelpers';
+/**
+ * Initial state.
+ *
+ * @private
+ * @type {{reportCapacity: object}}
+ */
const initialState = {
reportCapacity: {}
};
+/**
+ * Apply generated graph observer/reducer for reportCapacity to state, against actions.
+ *
+ * @param {object} state
+ * @param {object} action
+ * @returns {object|{}}
+ */
const graphReducer = (state = initialState, action) =>
reduxHelpers.generatedPromiseActionReducer(
[{ ref: 'reportCapacity', type: rhsmTypes.GET_GRAPH_REPORT_CAPACITY_RHSM }],
diff --git a/src/redux/reducers/userReducer.js b/src/redux/reducers/userReducer.js
index 3a998005c..34ac664b8 100644
--- a/src/redux/reducers/userReducer.js
+++ b/src/redux/reducers/userReducer.js
@@ -1,6 +1,13 @@
import { appTypes, userTypes } from '../types';
import { reduxHelpers } from '../common/reduxHelpers';
+/**
+ * Initial state.
+ *
+ * @private
+ * @type {{session: {pending: boolean, authorized: boolean, errorMessage: string, fulfilled: boolean,
+ * errorStatus: (string|number), error: boolean, locale: string}}}
+ */
const initialState = {
session: {
error: false,
@@ -13,6 +20,13 @@ const initialState = {
}
};
+/**
+ * Apply user observer/reducer logic for session to state, against actions.
+ *
+ * @param {object} state
+ * @param {object} action
+ * @returns {object|{}}
+ */
const userReducer = (state = initialState, action) => {
switch (action.type) {
case reduxHelpers.REJECTED_ACTION(userTypes.USER_AUTH):
diff --git a/src/redux/reducers/viewReducer.js b/src/redux/reducers/viewReducer.js
index 6d905839a..7ca4e128e 100644
--- a/src/redux/reducers/viewReducer.js
+++ b/src/redux/reducers/viewReducer.js
@@ -2,10 +2,23 @@ import { reduxTypes } from '../types';
import { reduxHelpers } from '../common/reduxHelpers';
import { RHSM_API_QUERY_GRANULARITY, RHSM_API_QUERY_SLA } from '../../types/rhsmApiTypes';
+/**
+ * Initial state.
+ *
+ * @private
+ * @type {{graphQuery: {}}}
+ */
const initialState = {
graphQuery: {}
};
+/**
+ * Apply user observer/reducer logic for views to state, against actions.
+ *
+ * @param {object} state
+ * @param {object} action
+ * @returns {object|{}}
+ */
const viewReducer = (state = initialState, action) => {
switch (action.type) {
case reduxTypes.rhsm.SET_GRAPH_GRANULARITY_RHSM:
diff --git a/src/redux/selectors/graphCardSelectors.js b/src/redux/selectors/graphCardSelectors.js
index 37229dc0d..f98947bda 100644
--- a/src/redux/selectors/graphCardSelectors.js
+++ b/src/redux/selectors/graphCardSelectors.js
@@ -6,8 +6,22 @@ import _camelCase from 'lodash/camelCase';
import { rhsmApiTypes } from '../../types/rhsmApiTypes';
import { reduxHelpers } from '../common/reduxHelpers';
+/**
+ * Selector cache.
+ *
+ * @private
+ * @type {{dataId: {string}, data: {object}}}
+ */
const graphCardCache = { dataId: null, data: {} };
+/**
+ * Return a combined state, props object.
+ *
+ * @private
+ * @param {object} state
+ * @param {object} props
+ * @returns {object}
+ */
const graphResponse = (state, props = {}) => ({
..._get(state, ['graph', 'reportCapacity', props.productId]),
...{
@@ -17,6 +31,11 @@ const graphResponse = (state, props = {}) => ({
}
});
+/**
+ * Create selector, transform combined state, props into a consumable graph/charting object.
+ *
+ * @type {{pending: boolean, fulfilled: boolean, errorStatus: (*|number), graphData: object, error: boolean}}
+ */
const graphCardSelector = createSelector([graphResponse], response => {
const { viewId = null, productId = null, graphQuery = {}, metaId, metaQuery = {}, ...responseData } = response || {};
@@ -132,6 +151,12 @@ const graphCardSelector = createSelector([graphResponse], response => {
return updatedResponseData;
});
+/**
+ * Expose selector instance. For scenarios where a selector is reused across component instances.
+ *
+ * @param {object} defaultProps
+ * @returns {{pending: boolean, fulfilled: boolean, errorStatus: (*|number), graphData: object, error: boolean}}
+ */
const makeGraphCardSelector = defaultProps => (state, props) => ({
...graphCardSelector(state, props, defaultProps)
});
diff --git a/src/redux/selectors/viewSelectors.js b/src/redux/selectors/viewSelectors.js
index cbbfeccd0..2c3648ebe 100644
--- a/src/redux/selectors/viewSelectors.js
+++ b/src/redux/selectors/viewSelectors.js
@@ -2,8 +2,23 @@ import { createSelectorCreator, defaultMemoize } from 'reselect';
import _get from 'lodash/get';
import _isEqual from 'lodash/isEqual';
+/**
+ * Create a custom "are objects equal" selector.
+ *
+ * @private
+ * @type {Function}}
+ */
const createDeepEqualSelector = createSelectorCreator(defaultMemoize, _isEqual);
+/**
+ * Return a combined state, props object.
+ *
+ * @private
+ * @param {object} state
+ * @param {object} props
+ * @param {object} defaultProps
+ * @returns {object}
+ */
const viewGraphQuery = (state = {}, props, defaultProps = {}) => ({
graphQuery: {
...defaultProps.graphQuery,
@@ -11,10 +26,21 @@ const viewGraphQuery = (state = {}, props, defaultProps = {}) => ({
}
});
+/**
+ * Create selector, transform combined state, props into a consumable API param/query object.
+ *
+ * @type {{graphQuery: object}}
+ */
const viewSelector = createDeepEqualSelector([viewGraphQuery], viewGraph => ({
graphQuery: { ...viewGraph.graphQuery }
}));
+/**
+ * Expose selector instance. For scenarios where a selector is reused across component instances.
+ *
+ * @param {object} defaultProps
+ * @returns {{graphQuery: object}}
+ */
const makeViewSelector = defaultProps => (state, props) => ({
...viewSelector(state, props, defaultProps)
});
diff --git a/src/redux/store.js b/src/redux/store.js
index d7d8c4467..57618599a 100644
--- a/src/redux/store.js
+++ b/src/redux/store.js
@@ -2,6 +2,9 @@ import { createStore, applyMiddleware } from 'redux';
import { reduxMiddleware } from './middleware';
import { reduxReducers } from './reducers';
+/**
+ * Create a Redux store.
+ */
const store = createStore(reduxReducers, applyMiddleware(...reduxMiddleware));
export { store as default, store };
diff --git a/src/redux/types/appTypes.js b/src/redux/types/appTypes.js
index 45782c17a..f2620c10f 100644
--- a/src/redux/types/appTypes.js
+++ b/src/redux/types/appTypes.js
@@ -1,6 +1,11 @@
const STATUS_4XX = '4XX';
const STATUS_5XX = '5XX';
+/**
+ * Application action, reducer types.
+ *
+ * @type {{STATUS_4XX: string, STATUS_5XX: string}}
+ */
const appTypes = {
STATUS_4XX,
STATUS_5XX
diff --git a/src/redux/types/platformTypes.js b/src/redux/types/platformTypes.js
index 92d4c40db..314d50e05 100644
--- a/src/redux/types/platformTypes.js
+++ b/src/redux/types/platformTypes.js
@@ -12,6 +12,13 @@ const PLATFORM_APP_NAME = 'PLATFORM_APP_NAME';
const PLATFORM_ON_NAV = 'PLATFORM_ON_NAV';
const PLATFORM_SET_NAV = 'PLATFORM_SET_NAV';
+/**
+ * Platform action, reducer types.
+ *
+ * @type {{PLATFORM_APP_NAME: string, PLATFORM_INIT: string, PLATFORM_SET_NAV: string,
+ * PLATFORM_CLEAR_NOTIFICATIONS: string, PLATFORM_ADD_NOTIFICATION: string,
+ * PLATFORM_REMOVE_NOTIFICATION: string, PLATFORM_ON_NAV: string}}
+ */
const platformTypes = {
PLATFORM_ADD_NOTIFICATION,
PLATFORM_REMOVE_NOTIFICATION,
diff --git a/src/redux/types/rhsmTypes.js b/src/redux/types/rhsmTypes.js
index a29939ca1..f139eccb2 100644
--- a/src/redux/types/rhsmTypes.js
+++ b/src/redux/types/rhsmTypes.js
@@ -4,6 +4,13 @@ const GET_GRAPH_REPORT_CAPACITY_RHSM = 'GET_GRAPH_REPORT_CAPACITY_RHSM';
const SET_GRAPH_GRANULARITY_RHSM = 'SET_GRAPH_GRANULARITY_RHSM';
const SET_GRAPH_SLA_RHSM = 'SET_GRAPH_SLA_RHSM';
+/**
+ * RHSM API action, reducer types.
+ *
+ * @type {{GET_GRAPH_REPORT_CAPACITY_RHSM: string, SET_GRAPH_GRANULARITY_RHSM: string,
+ * GET_GRAPH_CAPACITY_RHSM: string, SET_GRAPH_SLA_RHSM: string,
+ * GET_GRAPH_REPORT_RHSM: string}}
+ */
const rhsmTypes = {
GET_GRAPH_CAPACITY_RHSM,
GET_GRAPH_REPORT_RHSM,
diff --git a/src/redux/types/userTypes.js b/src/redux/types/userTypes.js
index 81eb38657..f671042bf 100644
--- a/src/redux/types/userTypes.js
+++ b/src/redux/types/userTypes.js
@@ -2,6 +2,11 @@ const USER_AUTH = 'USER_AUTH';
const USER_LOCALE = 'USER_LOCALE';
const USER_LOGOUT = 'USER_LOGOUT';
+/**
+ * User action, reducer types.
+ *
+ * @type {{USER_LOGOUT: string, USER_AUTH: string, USER_LOCALE: string}}
+ */
const userTypes = { USER_AUTH, USER_LOCALE, USER_LOGOUT };
export { userTypes as default, userTypes, USER_AUTH, USER_LOCALE, USER_LOGOUT };
diff --git a/src/services/config.js b/src/services/config.js
index d6a6c99f1..8c12eefb2 100644
--- a/src/services/config.js
+++ b/src/services/config.js
@@ -1,14 +1,32 @@
import axios, { CancelToken } from 'axios';
import { platformServices } from './platformServices';
+/**
+ * Apply consistent service configuration.
+ *
+ * @param {object} passedConfig
+ * @returns {object}
+ */
const serviceConfig = (passedConfig = {}) => ({
headers: {},
timeout: process.env.REACT_APP_AJAX_TIMEOUT,
...passedConfig
});
+/**
+ * Cache Axios service call cancel tokens.
+ *
+ * @private
+ * @type {object}
+ */
const cancelTokens = {};
+/**
+ * Call platform "getUser" auth method, and apply service config.
+ *
+ * @param {object} config
+ * @returns {Promise<*>}
+ */
const serviceCall = async config => {
await platformServices.getUser();
diff --git a/src/services/platformServices.js b/src/services/platformServices.js
index 532a5096c..886ad15c2 100644
--- a/src/services/platformServices.js
+++ b/src/services/platformServices.js
@@ -2,6 +2,7 @@ import { helpers } from '../common';
/**
* Basic user authentication.
+ *
* @returns {Promise}
*/
const getUser = async () => {
@@ -15,6 +16,7 @@ const getUser = async () => {
/**
* Help initialize global platform methods.
+ *
* @returns {Promise}
*/
const initializeChrome = async () => {
@@ -28,7 +30,8 @@ const initializeChrome = async () => {
/**
* Apply on "app_navigation" event. Return an un-listener.
- * @param callback {function}
+ *
+ * @param {Function} callback
* @returns {Function}
*/
const onNavigation = callback => {
@@ -43,7 +46,8 @@ const onNavigation = callback => {
// FixMe: Revert catch to throwing an error. Relaxed for development
/**
* Set application ID.
- * @param name {string}
+ *
+ * @param {string} name
* @returns {Promise}
*/
const setAppName = async (name = null) => {
@@ -58,7 +62,8 @@ const setAppName = async (name = null) => {
/**
* Set platform left hand navigation active item.
- * @param data {Array}
+ *
+ * @param {Array} data
* @returns {*}
*/
const setNavigation = (data = []) => {
diff --git a/src/services/rhsmServices.js b/src/services/rhsmServices.js
index 6027bbc5f..754dda8a6 100644
--- a/src/services/rhsmServices.js
+++ b/src/services/rhsmServices.js
@@ -32,6 +32,11 @@ import { serviceCall } from './config';
* ]
* }
*/
+/**
+ * Get RHSM API version information.
+ *
+ * @returns {Promise<*>}
+ */
const getApiVersion = () =>
serviceCall({
url: process.env.REACT_APP_SERVICES_RHSM_VERSION,
@@ -415,6 +420,13 @@ const getApiVersion = () =>
* ]
* }
*/
+/**
+ * Get RHSM API reporting/tally graph/chart data.
+ *
+ * @param {string} id Product ID
+ * @param {object} params Query/search params
+ * @returns {Promise<*>}
+ */
const getGraphReports = (id, params = {}) =>
serviceCall({
url: `${process.env.REACT_APP_SERVICES_RHSM_REPORT}${id}`,
@@ -577,6 +589,13 @@ const getGraphReports = (id, params = {}) =>
* ]
* }
*/
+/**
+ * Get RHSM API capacity/threshold graph/chart data.
+ *
+ * @param {string} id Product ID
+ * @param {object} params Query/search params
+ * @returns {Promise<*>}
+ */
const getGraphCapacity = (id, params = {}) =>
serviceCall({
url: `${process.env.REACT_APP_SERVICES_RHSM_CAPACITY}${id}`,
diff --git a/src/services/userServices.js b/src/services/userServices.js
index be7895982..6b377943f 100644
--- a/src/services/userServices.js
+++ b/src/services/userServices.js
@@ -3,6 +3,11 @@ import LocaleCode from 'locale-code';
import _isPlainObject from 'lodash/isPlainObject';
import { getUser } from './platformServices';
+/**
+ * Apply an emulated API response to the platforms getUser method.
+ *
+ * @returns {Promise<{data: void, message: string, status: number}>}
+ */
const authorizeUser = async () => {
let message = '{ getUser } = insights.chrome.auth';
let userData;
@@ -26,6 +31,12 @@ const authorizeUser = async () => {
return Promise.reject(emulatedErrorResponse);
};
+/**
+ * Return a platform locale value from a cookie.
+ *
+ * @private
+ * @returns {{value: string, key: string | null}|null}
+ */
const getLocaleFromCookie = () => {
const value = (Cookies.get(process.env.REACT_APP_CONFIG_SERVICE_LOCALES_COOKIE) || '').replace('_', '-');
const key = (value && LocaleCode.getLanguageName(value)) || null;
@@ -33,6 +44,11 @@ const getLocaleFromCookie = () => {
return (key && { value, key }) || null;
};
+/**
+ * Return platform locale.
+ *
+ * @returns {Promise<{data: void}>}
+ */
const getLocale = () => {
const defaultLocale = {
value: process.env.REACT_APP_CONFIG_SERVICE_LOCALES_DEFAULT_LNG,
diff --git a/src/types/rhsmApiTypes.js b/src/types/rhsmApiTypes.js
index cfd75490a..8bfa2f257 100644
--- a/src/types/rhsmApiTypes.js
+++ b/src/types/rhsmApiTypes.js
@@ -62,6 +62,25 @@ const RHSM_API_QUERY_SLA_TYPES = {
const RHSM_API_QUERY_START_DATE = 'beginning';
const RHSM_API_QUERY_END_DATE = 'ending';
+/**
+ * RHSM API types.
+ *
+ * @type {{RHSM_API_QUERY_END_DATE: string, RHSM_API_QUERY_GRANULARITY: string, RHSM_API_QUERY_START_DATE: string,
+ * RHSM_API_RESPONSE_CAPACITY_DATA: string, RHSM_API_PATH_ID_TYPES: {RHEL_ARM: string, RHEL_WORKSTATION: string,
+ * RHEL_DESKTOP: string, RHEL: string, RHEL_SERVER: string, RHEL_IBM_Z: string, RHEL_COMPUTE_NODE: string,
+ * RHEL_IBM_POWER: string, RHEL_X86: string, OPENSHIFT: string}, RHSM_API_QUERY_SLA: string,
+ * RHSM_API_RESPONSE_CAPACITY_META_TYPES: {COUNT: string}, RHSM_API_QUERY_SLA_TYPES: {PREMIUM: string,
+ * SELF: string, NONE: string, STANDARD: string},
+ * RHSM_API_RESPONSE_CAPACITY_DATA_TYPES: {HYPERVISOR_SOCKETS: string, CORES: string, DATE: string,
+ * SOCKETS: string, PHYSICAL_SOCKETS: string, HYPERVISOR_CORES: string, HAS_INFINITE: string,
+ * PHYSICAL_CORES: string}, RHSM_API_QUERY_LIMIT: string, RHSM_API_RESPONSE_CAPACITY_META: string,
+ * RHSM_API_RESPONSE_PRODUCTS_DATA_TYPES: {HYPERVISOR_SOCKETS: string, CORES: string, DATE: string,
+ * SOCKETS: string, HAS_DATA: string, PHYSICAL_SOCKETS: string, HYPERVISOR_CORES: string,
+ * PHYSICAL_CORES: string}, RHSM_API_RESPONSE_PRODUCTS_META: string,
+ * RHSM_API_QUERY_GRANULARITY_TYPES: {WEEKLY: string, QUARTERLY: string, DAILY: string, MONTHLY: string},
+ * RHSM_API_RESPONSE_PRODUCTS_DATA: string, RHSM_API_RESPONSE_PRODUCTS_META_TYPES: {COUNT: string},
+ * RHSM_API_QUERY_OFFSET: string}}
+ */
const rhsmApiTypes = {
RHSM_API_RESPONSE_CAPACITY_DATA,
RHSM_API_RESPONSE_CAPACITY_DATA_TYPES,
diff --git a/yarn.lock b/yarn.lock
index 80cee8044..051d0697e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3182,6 +3182,11 @@ commander@^4.1.1:
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
+comment-parser@^0.7.2:
+ version "0.7.2"
+ resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-0.7.2.tgz#baf6d99b42038678b81096f15b630d18142f4b8a"
+ integrity sha512-4Rjb1FnxtOcv9qsfuaNuVsmmVn4ooVoBHzYfyKteiXwIU84PClyGA5jASoFMwPV93+FPh9spwueXauxFJZkGAg==
+
common-tags@^1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937"
@@ -4788,6 +4793,19 @@ eslint-plugin-jest@^23.8.2:
dependencies:
"@typescript-eslint/experimental-utils" "^2.5.0"
+eslint-plugin-jsdoc@^22.0.1:
+ version "22.0.1"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-22.0.1.tgz#1f36c41a8818c968e46def7423e0b627841d72b9"
+ integrity sha512-lqQgGtd+roOhd5lSdIK4P3mlDmTVmVdcehj/r8nY25CGB2yi4Tk6JVwETCPBGnRKd40JkllkchyZmt0tFN+5pw==
+ dependencies:
+ comment-parser "^0.7.2"
+ debug "^4.1.1"
+ jsdoctypeparser "^6.1.0"
+ lodash "^4.17.15"
+ regextras "^0.7.0"
+ semver "^6.3.0"
+ spdx-expression-parse "^3.0.0"
+
eslint-plugin-json@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-json/-/eslint-plugin-json-2.1.1.tgz#7b9c4da2121f6f48d44efceb9a99ac0d4d12b299"
@@ -7252,6 +7270,11 @@ jsbn@~0.1.0:
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
+jsdoctypeparser@^6.1.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/jsdoctypeparser/-/jsdoctypeparser-6.1.0.tgz#acfb936c26300d98f1405cb03e20b06748e512a8"
+ integrity sha512-UCQBZ3xCUBv/PLfwKAJhp6jmGOSLFNKzrotXGNgbKhWvz27wPsCsVeP7gIcHPElQw2agBmynAitXqhxR58XAmA==
+
jsdom@^11.5.1:
version "11.12.0"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.12.0.tgz#1a80d40ddd378a1de59656e9e6dc5a3ba8657bc8"
@@ -10569,6 +10592,11 @@ regexpu-core@^4.6.0:
unicode-match-property-ecmascript "^1.0.4"
unicode-match-property-value-ecmascript "^1.1.0"
+regextras@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/regextras/-/regextras-0.7.0.tgz#2298bef8cfb92b1b7e3b9b12aa8f69547b7d71e4"
+ integrity sha512-ds+fL+Vhl918gbAUb0k2gVKbTZLsg84Re3DI6p85Et0U0tYME3hyW4nMK8Px4dtDaBA2qNjvG5uWyW7eK5gfmw==
+
regjsgen@^0.5.0:
version "0.5.1"
resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c"