Skip to content

Commit

Permalink
fix(productViewMissing): ent-3913 apply platform appNavClick
Browse files Browse the repository at this point in the history
* productViewMissing, apply action setAppNav
* platformActions, expose setAppNav
* platformServices, expose appNavClick, replace navigation
  • Loading branch information
cdcabrera committed Jun 2, 2021
1 parent 66818dd commit c1fa0d5
Show file tree
Hide file tree
Showing 12 changed files with 115 additions and 60 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`ProductViewMissing Component should redirect when there are limited product cards: redirect 1`] = `
<Redirect
baseName="/"
isForced={true}
isRedirect={true}
isReplace={false}
route="/rhel"
url={null}
/>
exports[`ProductViewMissing Component should redirect when there are limited product cards: redirect 1`] = `""`;

exports[`ProductViewMissing Component should redirect when there are limited product cards: redirect action 1`] = `
Array [
Array [
Object {
"meta": Object {
"appName": undefined,
"id": "rhel",
"secondaryNav": undefined,
},
"payload": Promise {},
"type": "PLATFORM_SET_NAV",
},
],
]
`;

exports[`ProductViewMissing Component should render a non-connected component: non-connected 1`] = `
Expand Down
13 changes: 13 additions & 0 deletions src/components/productView/__tests__/productViewMissing.test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import React from 'react';
import { shallow } from 'enzyme';
import * as reactRedux from 'react-redux';
import { ProductViewMissing } from '../productViewMissing';

describe('ProductViewMissing Component', () => {
const useDispatchMock = jest.spyOn(reactRedux, 'useDispatch');

afterEach(() => {
useDispatchMock.mockClear();
});

it('should render a non-connected component', () => {
useDispatchMock.mockReturnValue(jest.fn());

const props = {
availableProductsRedirect: 1
};
Expand All @@ -28,11 +37,15 @@ describe('ProductViewMissing Component', () => {
});

it('should redirect when there are limited product cards', () => {
const mockDispatch = jest.fn();
useDispatchMock.mockReturnValue(action => action(mockDispatch));

const props = {};

mockWindowLocation(
() => {
const component = shallow(<ProductViewMissing {...props} />);
expect(mockDispatch.mock.calls).toMatchSnapshot('redirect action');
expect(component).toMatchSnapshot('redirect');
},
{
Expand Down
19 changes: 12 additions & 7 deletions src/components/productView/productViewMissing.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Button, Card, CardBody, CardFooter, CardTitle, Gallery, Title, PageSection } from '@patternfly/react-core';
import { ArrowRightIcon } from '@patternfly/react-icons';
import { useHistory } from 'react-router-dom';
import { PageLayout, PageHeader } from '../pageLayout/pageLayout';
import { Redirect, routerHelpers } from '../router/router';
import { routerHelpers } from '../router/router';
import { reduxActions, useDispatch } from '../../redux';
import { helpers } from '../../common';
import { translate } from '../i18n/i18n';

Expand All @@ -18,7 +18,7 @@ import { translate } from '../i18n/i18n';
* @returns {Node}
*/
const ProductViewMissing = ({ availableProductsRedirect, t }) => {
const history = useHistory();
const dispatch = useDispatch();

/**
* Return a list of available products.
Expand All @@ -38,14 +38,19 @@ const ProductViewMissing = ({ availableProductsRedirect, t }) => {
* @returns {void}
*/
const onNavigate = id => {
const { routeHref } = routerHelpers.getRouteConfig({ id });
history.push(routeHref);
if (helpers.DEV_MODE) {
const { routeHref } = routerHelpers.getRouteConfig({ id });
window.location.href = routeHref;
} else {
dispatch(reduxActions.platform.setAppNav(id));
}
};

const availableProducts = filterAvailableProducts();

if (availableProducts.length <= availableProductsRedirect) {
return <Redirect isForced route={availableProducts[0].path} />;
if (!helpers.DEV_MODE && availableProducts.length <= availableProductsRedirect) {
dispatch(reduxActions.platform.setAppNav(availableProducts[0].id));
return null;
}

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,16 @@ exports[`PlatformActions Should return a function for the onNavigation method: e

exports[`PlatformActions Should return a function for the onNavigation method: function 1`] = `[Function]`;

exports[`PlatformActions Should return a function for the setNavigation method: expected process 1`] = `
Array [
Object {
"active": false,
exports[`PlatformActions Should return a function for the setAppNav method: expected process 1`] = `
Object {
"meta": Object {
"appName": undefined,
"id": "lorem",
"secondaryNav": undefined,
},
Object {
"active": false,
"id": "ipsum",
},
]
"payload": Promise {},
"type": "PLATFORM_SET_NAV",
}
`;

exports[`PlatformActions Should return a function for the setNavigation method: function 1`] = `[Function]`;
exports[`PlatformActions Should return a function for the setAppNav method: function 1`] = `[Function]`;
9 changes: 4 additions & 5 deletions src/redux/actions/__tests__/platformActions.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { platformActions } from '../platformActions';
import setAppNav from "../../../services/platformServices";

describe('PlatformActions', () => {
it('Should return a dispatch object for the hideGlobalFilter method', () => {
Expand All @@ -21,13 +22,11 @@ describe('PlatformActions', () => {
expect(platformActions.setAppName()).toMatchSnapshot('dispatch object');
});

it('Should return a function for the setNavigation method', () => {
expect(platformActions.setNavigation()).toMatchSnapshot('function');
it('Should return a function for the setAppNav method', () => {
expect(platformActions.setAppNav()).toMatchSnapshot('function');

window.insights.chrome.navigation = jest.fn().mockImplementation(value => value);
const dispatch = obj => obj;
expect(platformActions.setNavigation([{ id: 'lorem' }, { id: 'ipsum' }])(dispatch)).toMatchSnapshot(
'expected process'
);
expect(platformActions.setAppNav('lorem')(dispatch)).toMatchSnapshot('expected process');
});
});
23 changes: 15 additions & 8 deletions src/redux/actions/platformActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,24 @@ const setAppName = name => ({
});

/**
* Apply platform method for handling the left-nav navigation active item.
* Apply platform method for changing routes via the left-nav navigation.
*
* @param {object} data
* @param {string} id
* @param {object} options
* @param {string} options.appName
* @param {boolean} options.secondaryNav
* @returns {Function}
*/
const setNavigation = data => dispatch => {
const setAppNav = (id, { appName, secondaryNav } = {}) => dispatch =>
dispatch({
type: platformTypes.PLATFORM_SET_NAV
type: platformTypes.PLATFORM_SET_NAV,
payload: platformServices.setAppNav(id, { appName, secondaryNav }),
meta: {
id,
appName,
secondaryNav
}
});
return platformServices.setNavigation(data);
};

const platformActions = {
addNotification,
Expand All @@ -98,7 +105,7 @@ const platformActions = {
initializeChrome,
onNavigation,
setAppName,
setNavigation
setAppNav
};

export {
Expand All @@ -111,5 +118,5 @@ export {
initializeChrome,
onNavigation,
setAppName,
setNavigation
setAppNav
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ exports[`PlatformServices should return a failed initializeChrome: failed initia

exports[`PlatformServices should return a failed setAppName: failed setAppName 1`] = `"{ identifyApp } = insights.chrome, insights.chrome.identifyApp is not a function"`;

exports[`PlatformServices should return a failed setAppNav: failed setAppNav 1`] = `[Error: { appNavClick } = insights.chrome, insights.chrome.appNavClick is not a function]`;

exports[`PlatformServices should return a successful getUser with a specific response: specific success for authorized user 1`] = `"lorem ipsum"`;

exports[`PlatformServices should return a successful getUser: success authorized user 1`] = `
Expand Down
12 changes: 6 additions & 6 deletions src/services/__tests__/platformServices.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('PlatformServices', () => {
expect(platformServices.initializeChrome).toBeDefined();
expect(platformServices.onNavigation).toBeDefined();
expect(platformServices.setAppName).toBeDefined();
expect(platformServices.setNavigation).toBeDefined();
expect(platformServices.setAppNav).toBeDefined();
});

/**
Expand Down Expand Up @@ -95,10 +95,10 @@ describe('PlatformServices', () => {
expect(response).toMatchSnapshot('failed setAppName');
});

it('should return a failed setNavigation', () => {
window.insights.chrome.navigation = undefined;
expect(platformServices.setNavigation).toThrowError(
'{ navigation } = insights.chrome, insights.chrome.navigation is not a function'
);
it('should return a failed setAppNav', async () => {
window.insights.chrome.appNavClick = undefined;
const response = await returnPromiseAsync(platformServices.setAppNav);

expect(response).toMatchSnapshot('failed setAppNav');
});
});
28 changes: 14 additions & 14 deletions src/services/platformServices.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,24 +122,24 @@ const setAppName = async (name = null) => {
}
};

// ToDo: Clean up, consider removing setNavigation, currently no longer used.
/**
* Set platform left hand navigation active item.
* Set app routes via the platform left-nav navigation.
*
* @param {Array} data
* @returns {*}
* @param {string} id The navigation ID associated with internal route config, and external platform nav config
* @param {object} options
* @param {string} options.appName
* @param {boolean} options.secondaryNav
* @returns {Promise<object>}
*/
const setNavigation = (data = []) => {
const { insights, location } = window;
const setAppNav = async (id, { appName = helpers.UI_NAME, secondaryNav = true } = {}) => {
const { insights } = window;
try {
return insights.chrome.navigation(
data.map(item => ({
...item,
active: item.id === location.pathname.split('/').slice(-1)[0]
}))
return (
(helpers.DEV_MODE && { [platformApiTypes.PLATFORM_API_RESPONSE_NAV_TYPES.ACTIVE_APP]: id }) ||
(await insights.chrome.appNavClick({ id, secondaryNav, parentId: appName }))
);
} catch (e) {
throw new Error(`{ navigation } = insights.chrome, ${e.message}`);
throw new Error(`{ appNavClick } = insights.chrome, ${e.message}`);
}
};

Expand All @@ -150,7 +150,7 @@ const platformServices = {
initializeChrome,
onNavigation,
setAppName,
setNavigation
setAppNav
};

export {
Expand All @@ -162,5 +162,5 @@ export {
initializeChrome,
onNavigation,
setAppName,
setNavigation
setAppNav
};
2 changes: 1 addition & 1 deletion src/setupTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ addDisplayName(pfReactChartComponents);
*/
global.window.insights = {
chrome: {
appNavClick: Function.prototype,
auth: {
getUser: () =>
new Promise(resolve =>
Expand All @@ -70,7 +71,6 @@ global.window.insights = {
identifyApp: Function.prototype,
init: Function.prototype,
isBeta: Function.prototype,
navigation: Function.prototype,
on: Function.prototype
}
};
Expand Down
12 changes: 12 additions & 0 deletions src/types/__tests__/__snapshots__/index.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ exports[`ApiTypes should have specific API properties: all exported api types 1`
Object {
"apiTypes": Object {
"platformApi": Object {
"PLATFORM_API_RESPONSE_NAV_TYPES": Object {
"ACTIVE_APP": "activeApp",
},
"PLATFORM_API_RESPONSE_USER_ENTITLEMENTS": "entitlements",
"PLATFORM_API_RESPONSE_USER_ENTITLEMENTS_APP_TYPES": Object {
"ENTITLED": "is_entitled",
Expand Down Expand Up @@ -241,6 +244,9 @@ Object {
},
"default": Object {
"platformApi": Object {
"PLATFORM_API_RESPONSE_NAV_TYPES": Object {
"ACTIVE_APP": "activeApp",
},
"PLATFORM_API_RESPONSE_USER_ENTITLEMENTS": "entitlements",
"PLATFORM_API_RESPONSE_USER_ENTITLEMENTS_APP_TYPES": Object {
"ENTITLED": "is_entitled",
Expand Down Expand Up @@ -477,6 +483,9 @@ Object {
},
},
"platformApiTypes": Object {
"PLATFORM_API_RESPONSE_NAV_TYPES": Object {
"ACTIVE_APP": "activeApp",
},
"PLATFORM_API_RESPONSE_USER_ENTITLEMENTS": "entitlements",
"PLATFORM_API_RESPONSE_USER_ENTITLEMENTS_APP_TYPES": Object {
"ENTITLED": "is_entitled",
Expand Down Expand Up @@ -717,6 +726,9 @@ Object {
exports[`ApiTypes should have specific API properties: specific types 1`] = `
Object {
"platformApi": Object {
"PLATFORM_API_RESPONSE_NAV_TYPES": Object {
"ACTIVE_APP": "activeApp",
},
"PLATFORM_API_RESPONSE_USER_ENTITLEMENTS": "entitlements",
"PLATFORM_API_RESPONSE_USER_ENTITLEMENTS_APP_TYPES": Object {
"ENTITLED": "is_entitled",
Expand Down
11 changes: 11 additions & 0 deletions src/types/platformApiTypes.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
/**
* Platform response for appNavClick.
*
* @type {{ACTIVE_APP: string}}
*/
const PLATFORM_API_RESPONSE_NAV_TYPES = {
ACTIVE_APP: 'activeApp'
};

/**
* Platform response entitlements type.
*
Expand Down Expand Up @@ -76,6 +85,7 @@ const PLATFORM_API_RESPONSE_USER_PERMISSION_OPERATION_TYPES = {
* PLATFORM_API_RESPONSE_USER_IDENTITY_USER_TYPES: {ORG_ADMIN: string}}}
*/
const platformApiTypes = {
PLATFORM_API_RESPONSE_NAV_TYPES,
PLATFORM_API_RESPONSE_USER_ENTITLEMENTS,
PLATFORM_API_RESPONSE_USER_ENTITLEMENTS_APP_TYPES,
PLATFORM_API_RESPONSE_USER_IDENTITY,
Expand All @@ -90,6 +100,7 @@ const platformApiTypes = {
export {
platformApiTypes as default,
platformApiTypes,
PLATFORM_API_RESPONSE_NAV_TYPES,
PLATFORM_API_RESPONSE_USER_ENTITLEMENTS,
PLATFORM_API_RESPONSE_USER_ENTITLEMENTS_APP_TYPES,
PLATFORM_API_RESPONSE_USER_IDENTITY,
Expand Down

0 comments on commit c1fa0d5

Please sign in to comment.