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