From 96f0fd101c6c60fcc79521c34af2170c48bd8cdf Mon Sep 17 00:00:00 2001 From: Vu Nguyen Date: Wed, 15 Jul 2020 11:15:06 +0700 Subject: [PATCH 1/6] feat: #1917 Update developer preview app page --- .../app-detail-preview/app-detail-preview.tsx | 59 + .../app-button-group.test.tsx.snap | 21 + .../__snapshots__/app-content.test.tsx.snap | 30 + .../__snapshots__/app-detail.test.tsx.snap | 488 ++++++ .../app-install-confirmation.test.tsx.snap | 132 ++ .../app-uninstall-confirmation.test.tsx.snap | 160 ++ .../__snapshots__/aside.test.tsx.snap | 76 + .../contact-developer-modal.test.tsx.snap | 191 ++ .../__tests__/app-button-group.test.tsx | 29 + .../client/__tests__/app-content.test.tsx | 12 + .../client/__tests__/app-detail.test.tsx | 190 ++ .../app-install-confirmation.test.tsx | 107 ++ .../app-uninstall-confirmation.test.tsx | 130 ++ .../client/__tests__/aside.test.tsx | 20 + .../contact-developer-modal.test.tsx | 41 + .../app-detail-preview/client/app-content.tsx | 23 + .../client/app-detail-button-group.tsx | 44 + .../app-detail-preview/client/app-detail.tsx | 159 ++ .../pages/app-detail-preview/client/aside.tsx | 57 + .../client/contact-developer-modal.tsx | 100 ++ .../pages/app-detail-preview/client/index.ts | 1 + .../__stubs__/web-component-config.ts | 14 + .../client-web-component-config.test.tsx.snap | 62 + .../config-modal-inner.test.tsx.snap | 1537 +++++++++++++++++ .../__snapshots__/config-modal.test.tsx.snap | 13 + .../client-web-component-config.test.tsx | 31 + .../__test__/config-modal-inner.test.tsx | 94 + .../__test__/config-modal.test.tsx | 15 + .../client-web-component-config.tsx | 51 + .../config-modal-inner.tsx | 172 ++ .../config-modal.tsx | 17 + .../web-component-config-modal/index.ts | 1 + .../app-authentication-detail.test.tsx.snap | 44 + .../__snapshots__/ui-app-header.test.tsx.snap | 38 + .../__snapshots__/ui-helpers.test.tsx.snap | 33 + .../__snapshots__/ui-sections.test.tsx.snap | 431 +++++ .../app-authentication-detail.test.tsx | 63 + .../common/__tests__/ui-app-header.test.tsx | 17 + .../common/__tests__/ui-helpers.test.tsx | 33 + .../common/__tests__/ui-sections.test.tsx | 200 +++ .../common/app-authentication-detail.tsx | 68 + .../common/ui-app-header.tsx | 48 + .../app-detail-preview/common/ui-helpers.tsx | 37 + .../app-detail-preview/common/ui-sections.tsx | 247 +++ .../pages/app-detail-preview/index.ts | 2 + packages/developer-portal/src/core/router.tsx | 2 + .../src/styles/blocks/installed-app-card.scss | 53 - .../src/styles/blocks/installed-app-list.scss | 17 - 48 files changed, 5340 insertions(+), 70 deletions(-) create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/app-detail-preview.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-button-group.test.tsx.snap create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-content.test.tsx.snap create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-detail.test.tsx.snap create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-install-confirmation.test.tsx.snap create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-uninstall-confirmation.test.tsx.snap create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/aside.test.tsx.snap create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/contact-developer-modal.test.tsx.snap create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-button-group.test.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-content.test.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-detail.test.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-install-confirmation.test.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-uninstall-confirmation.test.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/aside.test.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/contact-developer-modal.test.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/app-content.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/app-detail-button-group.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/app-detail.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/aside.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/contact-developer-modal.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/index.ts create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__stubs__/web-component-config.ts create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/__snapshots__/client-web-component-config.test.tsx.snap create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/__snapshots__/config-modal-inner.test.tsx.snap create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/__snapshots__/config-modal.test.tsx.snap create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/client-web-component-config.test.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/config-modal-inner.test.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/config-modal.test.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/client-web-component-config.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/config-modal-inner.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/config-modal.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/index.ts create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/__snapshots__/app-authentication-detail.test.tsx.snap create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/__snapshots__/ui-app-header.test.tsx.snap create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/__snapshots__/ui-helpers.test.tsx.snap create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/__snapshots__/ui-sections.test.tsx.snap create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/app-authentication-detail.test.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/ui-app-header.test.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/ui-helpers.test.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/ui-sections.test.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/common/app-authentication-detail.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/common/ui-app-header.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/common/ui-helpers.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/common/ui-sections.tsx create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/index.ts delete mode 100644 packages/developer-portal/src/styles/blocks/installed-app-card.scss delete mode 100644 packages/developer-portal/src/styles/blocks/installed-app-list.scss diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/app-detail-preview.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/app-detail-preview.tsx new file mode 100644 index 0000000000..db6abb1d7a --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/app-detail-preview.tsx @@ -0,0 +1,59 @@ +import * as React from 'react' +import AppContent from './client/app-content' +import { Aside as AppAside } from './client/aside' +import AppHeader from './common/ui-app-header' +import { useParams } from 'react-router' +import { AppDetailData } from '@/reducers/developer' +import { Grid, Loader, GridItem, Section } from '@reapit/elements' +import { BackToAppsSection } from '../app-detail/app-sections' +import useReactResponsive from '@/components/hooks/use-react-responsive' + +export type AppDetailPreviewProps = {} + +export const loadAppDetailPreviewDataFromLocalStorage = ( + appId: string, + setAppDetailPreviewData: React.Dispatch>, +) => () => { + try { + const appDataString = localStorage.getItem('developer-preview-app') + if (!appDataString) { + throw 'No app preview' + } + + const appData = JSON.parse(appDataString) as AppDetailData + if (appData?.id !== appId) { + throw 'No app preview' + } + setAppDetailPreviewData(appData) + } catch (err) {} +} + +const AppDetailPreview: React.FC = () => { + const { isMobile } = useReactResponsive() + const [appDetailPreviewData, setAppDetailPreviewData] = React.useState(null) + const { appId } = useParams() + + React.useEffect(loadAppDetailPreviewDataFromLocalStorage(appId, setAppDetailPreviewData), [appId]) + + return ( + + {!appDetailPreviewData ? ( + + ) : ( + <> + + + + +
+ + + {!isMobile && {}} />} +
+
+ + )} +
+ ) +} +export default AppDetailPreview diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-button-group.test.tsx.snap b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-button-group.test.tsx.snap new file mode 100644 index 0000000000..48dc51fdc2 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-button-group.test.tsx.snap @@ -0,0 +1,21 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ClientAppDetailButtonGroup should match snapshot when both buttons are hidden 1`] = ` + + + +`; + +exports[`ClientAppDetailButtonGroup should match snapshot when both buttons are showing 1`] = ` + + + Uninstall App + + +`; diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-content.test.tsx.snap b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-content.test.tsx.snap new file mode 100644 index 0000000000..288783aeb1 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-content.test.tsx.snap @@ -0,0 +1,30 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ClientAppContent ClientAppContent - should match snapshot 1`] = ` + + + + + + + +`; diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-detail.test.tsx.snap b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-detail.test.tsx.snap new file mode 100644 index 0000000000..147f5b6cff --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-detail.test.tsx.snap @@ -0,0 +1,488 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AppDetail renderAppHeaderButtonGroup should match snapshot 1`] = ` +
+ +
+`; + +exports[`AppDetail renderAppHeaderButtonGroup should match snapshot 2`] = ` +
+ +
+`; + +exports[`AppDetail should match a snapshot 1`] = ` + + + + + +
+ +
+
+
+
+
+
+ + + + + Confirm + + + Cancel + + + } + title="Confirm undefined uninstallation" + visible={false} + /> + + + + + + Confirm + + + Cancel + + + } + title="Confirm undefined installation" + visible={false} + /> + +
+ + + + + +`; + +exports[`AppDetail should render loader when client.appDetail.data is an empty object 1`] = ` + + + + + +
+ +
+
+
+
+
+
+ + + + + Confirm + + + Cancel + + + } + title="Confirm undefined uninstallation" + visible={false} + /> + + + + + + Confirm + + + Cancel + + + } + title="Confirm undefined installation" + visible={false} + /> + +
+ + + + + +`; + +exports[`AppDetail should render loader when isLoadingAppDetail = true 1`] = ` + + + + + +
+ +
+
+
+
+
+
+ + + + + Confirm + + + Cancel + + + } + title="Confirm undefined uninstallation" + visible={false} + /> + + + + + + Confirm + + + Cancel + + + } + title="Confirm undefined installation" + visible={false} + /> + +
+ + + + + +`; diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-install-confirmation.test.tsx.snap b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-install-confirmation.test.tsx.snap new file mode 100644 index 0000000000..81e038b37a --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-install-confirmation.test.tsx.snap @@ -0,0 +1,132 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ClientAppInstallConfirmation should match a snapshot 1`] = ` + + + + + + + Confirm + + + Cancel + + + } + title="Confirm Peter's Properties installation" + visible={true} + /> + + + + +`; diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-uninstall-confirmation.test.tsx.snap b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-uninstall-confirmation.test.tsx.snap new file mode 100644 index 0000000000..f70d53f0b6 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-uninstall-confirmation.test.tsx.snap @@ -0,0 +1,160 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ClientAppUninstallConfirmation renderUninstallConfirmationModalFooter should match snapshot 1`] = ` +
+ + Confirm + + + Cancel + +
+`; + +exports[`ClientAppUninstallConfirmation should match a snapshot 1`] = ` + + + + + + + Confirm + + + Cancel + + + } + title="Confirm Peter's Properties uninstallation" + visible={true} + /> + + + + + +`; diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/aside.test.tsx.snap b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/aside.test.tsx.snap new file mode 100644 index 0000000000..d57896ae40 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/aside.test.tsx.snap @@ -0,0 +1,76 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ClientAside ClientAside - should match snapshot 1`] = ` + + + + + + +`; diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/contact-developer-modal.test.tsx.snap b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/contact-developer-modal.test.tsx.snap new file mode 100644 index 0000000000..35d000d8cd --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/contact-developer-modal.test.tsx.snap @@ -0,0 +1,191 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ContactDeveloperSection should match snapshot with gutter 1`] = ` + + + NEED HELP? + + + Close + + } + title="Contact Details" + visible={false} + > + + + + + Company name + + + +

+ Reapit Ltd +

+
+
+ + + + Telephone Number + + + +

+ 0777 777 777 +

+
+
+ + + + Support Email + + + +

+ + reapit@reapit.com + +

+
+
+ + + + Home Page + + + +

+ + https://reapit.com + +

+
+
+
+
+
+`; + +exports[`ContactDeveloperSection should match snapshot without gutter 1`] = ` + + + NEED HELP? + + + Close + + } + title="Contact Details" + visible={false} + > + + + + + Company name + + + +

+ Reapit Ltd +

+
+
+ + + + Telephone Number + + + +

+ 0777 777 777 +

+
+
+ + + + Support Email + + + +

+ + reapit@reapit.com + +

+
+
+ + + + Home Page + + + +

+ + https://reapit.com + +

+
+
+
+
+
+`; diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-button-group.test.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-button-group.test.tsx new file mode 100644 index 0000000000..4d6ab6e782 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-button-group.test.tsx @@ -0,0 +1,29 @@ +import React from 'react' +import { AppDetailButtonGroup } from '../app-detail-button-group' +import { shallow } from 'enzyme' + +describe('ClientAppDetailButtonGroup', () => { + it('should match snapshot when both buttons are showing', () => { + const wrapper = shallow( + , + ) + expect(wrapper).toMatchSnapshot() + }) + + it('should match snapshot when both buttons are hidden', () => { + const wrapper = shallow( + , + ) + expect(wrapper).toMatchSnapshot() + }) +}) diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-content.test.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-content.test.tsx new file mode 100644 index 0000000000..b1afabd887 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-content.test.tsx @@ -0,0 +1,12 @@ +import React from 'react' +import AppContent from '../app-content' +import { appDetailDataStub } from '@/sagas/__stubs__/app-detail' +import { shallow } from 'enzyme' +import { AppDetailDataNotNull } from '@/reducers/client/app-detail' + +describe('ClientAppContent', () => { + it('ClientAppContent - should match snapshot', () => { + const wrapper = shallow() + expect(wrapper).toMatchSnapshot() + }) +}) diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-detail.test.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-detail.test.tsx new file mode 100644 index 0000000000..e21aba589a --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-detail.test.tsx @@ -0,0 +1,190 @@ +import * as React from 'react' +import * as ReactRedux from 'react-redux' +import TestRenderer from 'react-test-renderer' +import { MemoryRouter } from 'react-router' +import { mount, shallow } from 'enzyme' +import configureStore from 'redux-mock-store' +import { getMockRouterProps } from '@/utils/mock-helper' +import AppDetail, { + handleCloseInstallConfirmationModal, + handleInstallAppButtonClick, + renderAppHeaderButtonGroup, + handleCloseUnInstallConfirmationModal, + handleUnInstallAppButtonClick, + onBackToAppsButtonClick, +} from '../app-detail' +import { Button } from '@reapit/elements' +import Routes from '@/constants/routes' +import appState from '@/reducers/__stubs__/app-state' + +describe('AppDetail', () => { + const { history } = getMockRouterProps({}) + let store + beforeEach(() => { + /* mocking store */ + const mockStore = configureStore() + store = mockStore({ + ...appState, + client: { + appDetail: { + data: {}, + loading: false, + }, + }, + }) + }) + + it('should render loader when isLoadingAppDetail = true', () => { + const mockStore = configureStore() + const customStore = mockStore({ + ...appState, + client: { + appDetail: { + isAppDetailLoading: true, + data: {}, + }, + }, + }) + + const wrapper = mount( + + + + + , + ) + + expect(wrapper).toMatchSnapshot() + const loader = wrapper.find('[data-test="client-app-detail-loader"]') + expect(loader.length).toBe(1) + }) + + it('should render loader when client.appDetail.data is an empty object', () => { + const mockStore = configureStore() + const customStore = mockStore({ + ...appState, + client: { + appDetail: { + data: {}, + loading: false, + }, + }, + }) + + const wrapper = mount( + + + + + , + ) + + expect(wrapper).toMatchSnapshot() + const loader = wrapper.find('[data-test="client-app-detail-loader"]') + expect(loader.length).toBe(1) + }) + + it('should match a snapshot', () => { + expect( + mount( + + + + + , + ), + ).toMatchSnapshot() + }) + + describe('renderAppHeaderButtonGroup', () => { + const mockAppId = 'test' + const mockInstalledOn = '2020-2-20' + + it('should match snapshot', () => { + const wrapperWithIsInstallBtnHiddenTrue = shallow( +
{renderAppHeaderButtonGroup(mockAppId, mockInstalledOn, jest.fn(), jest.fn(), true, 'CLIENT')}
, + ) + expect(wrapperWithIsInstallBtnHiddenTrue).toMatchSnapshot() + const wrapperWithIsInstallBtnHiddenFalse = shallow( +
{renderAppHeaderButtonGroup(mockAppId, mockInstalledOn, jest.fn(), jest.fn(), false, 'CLIENT')}
, + ) + expect(wrapperWithIsInstallBtnHiddenFalse).toMatchSnapshot() + }) + + it('should render header button group when appId is existed', () => { + const testRenderer = TestRenderer.create( +
{renderAppHeaderButtonGroup(mockAppId, mockInstalledOn, jest.fn(), jest.fn(), false, 'CLIENT')}
, + ) + const testInstance = testRenderer.root + expect(testInstance.children.length).toBe(1) + }) + + it('should render install app button if installedOn is empty', () => { + const testRenderer = TestRenderer.create( +
{renderAppHeaderButtonGroup(mockAppId, '', jest.fn(), jest.fn(), false, 'CLIENT')}
, + ) + const testInstance = testRenderer.root + expect(testInstance.findByType(Button).props.children).toBe('Install App') + }) + + it('should render uninstall app button if installedOn is existed', () => { + const testRenderer = TestRenderer.create( +
{renderAppHeaderButtonGroup(mockAppId, 'exist', jest.fn(), jest.fn(), false, 'CLIENT')}
, + ) + const testInstance = testRenderer.root + expect(testInstance.findByType(Button).props.children).toBe('Uninstall App') + }) + + it('should not render header button group when appId is empty', () => { + const testRenderer = TestRenderer.create( +
{renderAppHeaderButtonGroup('', mockInstalledOn, jest.fn(), jest.fn(), false, 'CLIENT')}
, + ) + const testInstance = testRenderer.getInstance + expect(testInstance).toHaveLength(0) + }) + }) + + describe('handleCloseInstallConfirmationModal', () => { + it('should run correctly', () => { + const mockFunction = jest.fn() + const fn = handleCloseInstallConfirmationModal(mockFunction) + fn() + expect(mockFunction).toBeCalledWith(false) + }) + }) + + describe('handleInstallAppButtonClick', () => { + it('should run correctly', () => { + const mockFunction = jest.fn() + const fn = handleInstallAppButtonClick(mockFunction) + fn() + expect(mockFunction).toBeCalledWith(true) + }) + }) + + describe('handleCloseUnInstallConfirmationModal', () => { + it('should run correctly', () => { + const mockFunction = jest.fn() + const fn = handleCloseUnInstallConfirmationModal(mockFunction) + fn() + expect(mockFunction).toBeCalledWith(false) + }) + }) + + describe('handleUnInstallAppButtonClick', () => { + it('should run correctly', () => { + const mockFunction = jest.fn() + const fn = handleUnInstallAppButtonClick(mockFunction) + fn() + expect(mockFunction).toBeCalledWith(true) + }) + }) + + describe('onBackToAppsButtonClick', () => { + it('should run correctly', () => { + const fn = onBackToAppsButtonClick(history) + fn() + expect(history.push).toBeCalledWith(Routes.APPS) + }) + }) +}) diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-install-confirmation.test.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-install-confirmation.test.tsx new file mode 100644 index 0000000000..776bf30ef9 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-install-confirmation.test.tsx @@ -0,0 +1,107 @@ +import * as React from 'react' +import * as ReactRedux from 'react-redux' +import { mount } from 'enzyme' +import configureStore from 'redux-mock-store' +import { appDetailDataStub } from '@/sagas/__stubs__/app-detail' +import { MemoryRouter } from 'react-router' +import AppInstallConfirmation, { + AppInstallConfirmationProps, + handleInstallButtonClick, + handleInstallAppSuccessCallback, + handleSuccessAlertButtonClick, + handleSuccessAlertMessageAfterClose, +} from '../app-install-confirmation' +import { appInstallationsRequestInstall } from '@/actions/app-installations' +import { clientFetchAppDetail } from '@/actions/client' +import routes from '@/constants/routes' +import Routes from '@/constants/routes' +import appState from '@/reducers/__stubs__/app-state' + +const mockProps: AppInstallConfirmationProps = { + appDetailData: appDetailDataStub.data, + visible: true, + closeInstallConfirmationModal: jest.fn(), +} + +describe('ClientAppInstallConfirmation', () => { + let store + let spyDispatch + const appId = mockProps.appDetailData?.id || '' + const clientId = appState.auth.loginSession?.loginIdentity.clientId || '' + + beforeEach(() => { + /* mocking store */ + const mockStore = configureStore() + store = mockStore(appState) + /* mocking useDispatch on our mock store */ + spyDispatch = jest.spyOn(ReactRedux, 'useDispatch').mockImplementation(() => store.dispatch) + }) + + it('should match a snapshot', () => { + expect( + mount( + + + + + , + ), + ).toMatchSnapshot() + }) + + describe('handleInstallButtonClick', () => { + it('should run correctly', () => { + const mockFunction = jest.fn() + const fn = handleInstallButtonClick( + appId, + clientId, + spyDispatch, + mockFunction, + mockProps.closeInstallConfirmationModal, + false, + ) + fn() + expect(spyDispatch).toBeCalledWith( + appInstallationsRequestInstall({ + appId, + callback: expect.any(Function), + }), + ) + }) + }) + + describe('handleSuccessAlertMessageAfterClose', () => { + it('should run correctly', () => { + const mockFunction = jest.fn() + const fn = handleSuccessAlertMessageAfterClose(appId, clientId, mockFunction, spyDispatch) + fn() + expect(mockFunction).toBeCalledWith(false) + expect(spyDispatch).toBeCalledWith( + clientFetchAppDetail({ + id: appId, + clientId, + }), + ) + }) + }) + + describe('handleInstallAppSuccessCallback', () => { + it('should run correctly', () => { + const mockFunction = jest.fn() + const fn = handleInstallAppSuccessCallback(mockFunction, mockProps.closeInstallConfirmationModal, false) + fn() + + expect(mockProps.closeInstallConfirmationModal).toBeCalled() + expect(mockFunction).toBeCalledWith(true) + }) + }) + + describe('handleSuccessAlertButtonClick', () => { + const history = { + replace: jest.fn(), + } as any + const fn = handleSuccessAlertButtonClick(history) + fn() + expect(history.replace).toBeCalledWith(routes.APPS) + }) +}) diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-uninstall-confirmation.test.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-uninstall-confirmation.test.tsx new file mode 100644 index 0000000000..ca10ed9e53 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-uninstall-confirmation.test.tsx @@ -0,0 +1,130 @@ +import * as React from 'react' +import * as ReactRedux from 'react-redux' +import { mount, shallow } from 'enzyme' +import configureStore from 'redux-mock-store' +import { MemoryRouter } from 'react-router' +import { appDetailDataStub } from '@/sagas/__stubs__/app-detail' +import { appInstallationsRequestUninstall } from '@/actions/app-installations' +import { clientFetchAppDetail } from '@/actions/client' +import ClientAppUninstallConfirmation, { + AppUninstallConfirmationProps, + onUninstallButtonClick, + handleUninstallAppSuccessCallback, + handleSuccessAlertButtonClick, + handleSuccessAlertMessageAfterClose, + renderUninstallConfirmationModalFooter, +} from '../app-uninstall-confirmation' +import Routes from '@/constants/routes' +import appState from '@/reducers/__stubs__/app-state' + +const mockProps: AppUninstallConfirmationProps = { + appDetailData: appDetailDataStub.data, + visible: true, + closeUninstallConfirmationModal: jest.fn(), +} + +describe('ClientAppUninstallConfirmation', () => { + let store + let spyDispatch + const appId = mockProps.appDetailData?.id || '' + const installationId = mockProps.appDetailData?.installationId || '' + const clientId = appState.auth.loginSession?.loginIdentity.clientId || '' + + beforeEach(() => { + /* mocking store */ + const mockStore = configureStore() + store = mockStore(appState) + /* mocking useDispatch on our mock store */ + spyDispatch = jest.spyOn(ReactRedux, 'useDispatch').mockImplementation(() => store.dispatch) + }) + + it('should match a snapshot', () => { + expect( + mount( + + + + + , + ), + ).toMatchSnapshot() + }) + + describe('onUninstallButtonClick', () => { + it('should run correctly', () => { + const mockFunction = jest.fn() + const fn = onUninstallButtonClick( + appId, + clientId, + installationId, + spyDispatch, + mockFunction, + mockProps.closeUninstallConfirmationModal, + false, + ) + fn() + expect(spyDispatch).toBeCalledWith( + appInstallationsRequestUninstall({ + appId, + installationId, + terminatedReason: 'User uninstall', + callback: expect.any(Function), + }), + ) + }) + }) + + describe('handleUninstallAppSuccessCallback', () => { + it('should run correctly', () => { + const mockFunction = jest.fn() + const fn = handleUninstallAppSuccessCallback(mockFunction, mockProps.closeUninstallConfirmationModal, false) + fn() + expect(mockProps.closeUninstallConfirmationModal).toBeCalled() + expect(mockFunction).toBeCalledWith(true) + }) + }) + + describe('handleSuccessAlertButtonClick', () => { + const history = { + replace: jest.fn(), + } as any + const fn = handleSuccessAlertButtonClick(history) + fn() + expect(history.replace).toBeCalledWith(Routes.APPS) + }) + + describe('handleSuccessAlertMessageAfterClose', () => { + it('should match snapshot', () => { + const mockFunction = jest.fn() + const fn = handleSuccessAlertMessageAfterClose(appId, clientId, mockFunction, spyDispatch) + fn() + expect(spyDispatch).toBeCalledWith( + clientFetchAppDetail({ + id: appId, + clientId, + }), + ) + expect(mockFunction).toBeCalledWith(false) + }) + }) + + describe('renderUninstallConfirmationModalFooter', () => { + it('should match snapshot', () => { + const wrapper = shallow( +
+ {renderUninstallConfirmationModalFooter( + false, + appId, + clientId, + installationId, + spyDispatch, + jest.fn(), + jest.fn(), + false, + )} +
, + ) + expect(wrapper).toMatchSnapshot() + }) + }) +}) diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/aside.test.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/aside.test.tsx new file mode 100644 index 0000000000..758bf2021d --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/aside.test.tsx @@ -0,0 +1,20 @@ +import React from 'react' +import { Aside } from '../aside' +import { shallow } from 'enzyme' +import { integrationTypesStub } from '@/sagas/__stubs__/integration-types' +import { appDetailDataStub } from '@/sagas/__stubs__/app-detail' +import { AppDetailDataNotNull } from '@/reducers/client/app-detail' +import { DesktopIntegrationTypeModel } from '@/actions/app-integration-types' + +describe('ClientAside', () => { + test('ClientAside - should match snapshot', () => { + expect( + shallow( +
+ +
+
+
+ + + + + + } + > +
+ + + + + + + + +
+
+ + + +`; diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/__snapshots__/config-modal.test.tsx.snap b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/__snapshots__/config-modal.test.tsx.snap new file mode 100644 index 0000000000..2cdda1d8f4 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/__snapshots__/config-modal.test.tsx.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Config-modal should match snapshot 1`] = ` + + + +`; diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/client-web-component-config.test.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/client-web-component-config.test.tsx new file mode 100644 index 0000000000..dc9f9fc2fe --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/client-web-component-config.test.tsx @@ -0,0 +1,31 @@ +import * as React from 'react' +import { Provider } from 'react-redux' +import appState from '@/reducers/__stubs__/app-state' +import configureStore from 'redux-mock-store' +import { mount } from 'enzyme' +import { WebComponentConfig } from '../client-web-component-config' +import Routes from '@/constants/routes' +import { MemoryRouter } from 'react-router' + +describe('WebComponentConfig', () => { + const extendStore = { + ...appState, + client: { + webComponent: { isShowModal: true }, + }, + } + const mockStore = configureStore() + const store = mockStore(extendStore) + + it('Should match snapshot', () => { + expect( + mount( + + + + + , + ), + ).toMatchSnapshot() + }) +}) diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/config-modal-inner.test.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/config-modal-inner.test.tsx new file mode 100644 index 0000000000..53c60ec457 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/config-modal-inner.test.tsx @@ -0,0 +1,94 @@ +import React from 'react' +import { + updateWebComponentConfig, + WebComponentConfigModalFooter, + WebComponentConfigModalInner, + genarateNegotiatorOptions, +} from '../config-modal-inner' +import { mount } from 'enzyme' +import configureStore from 'redux-mock-store' +import { Provider } from 'react-redux' +import { UpdateWebComponentConfigParams } from '@/services/web-component' +import { clientUpdateWebComponentConfig } from '@/actions/client' +import { webComponentStub } from '../__stubs__/web-component-config' +import appState from '@/reducers/__stubs__/app-state' +import { FormikProps } from '@reapit/elements' + +const params = { + appId: 'appid', + appointmentLength: 1, + appointmentTimeGap: 1, + customerId: 'string', + daysOfWeek: ['1', '2'], +} as UpdateWebComponentConfigParams + +const extendAppState = webComponent => { + return { + ...appState, + client: { ...appState.client, webComponent }, + } +} + +describe('Config-modal-inner', () => { + const mockStore = configureStore() + const store = mockStore(extendAppState(webComponentStub)) + + it('should WebComponentConfigModalFooter match a snapshot', () => { + const mockProps = { + closeModal: jest.fn(), + formikProps: {} as FormikProps, + } + expect( + mount( + + + , + ), + ).toMatchSnapshot() + }) + + it('should WebComponentConfigModalInner match a snapshot', () => { + const mockStore = configureStore() + const store = mockStore(extendAppState(webComponentStub)) + expect( + mount( + + + , + ), + ).toMatchSnapshot() + }) + + it('should updateWebComponentConfig run correctly', () => { + const dispatch = jest.fn() + const closeModal = jest.fn() + + const fn = updateWebComponentConfig(dispatch, 'appid', closeModal) + fn(params) + expect(dispatch).toBeCalledWith(clientUpdateWebComponentConfig({ ...params, callback: closeModal })) + }) +}) + +describe('should return correctly', () => { + it('should return correctly', () => { + const list = [ + { + active: true, + created: 'string', + email: 'string', + id: 'string', + jobTitle: 'string', + metadata: 'any', + mobilePhone: 'string', + modified: 'string', + name: 'string', + officeId: 'string', + workPhone: 'string', + }, + ] + + const result = genarateNegotiatorOptions(list) + const expected = [{ value: 'string', label: 'string', description: 'string' }] + expect(result).toEqual(expected) + }) +}) diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/config-modal.test.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/config-modal.test.tsx new file mode 100644 index 0000000000..b696c59bd9 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/config-modal.test.tsx @@ -0,0 +1,15 @@ +import { WebComponentModal, WebComponentModalProps } from '../config-modal' +import React from 'react' +import { shallow } from 'enzyme' + +describe('Config-modal', () => { + it('should match snapshot', () => { + const mockProps = { + type: 'BOOK_VIEWING', + afterClose: jest.fn(), + closeModal: jest.fn(), + } as WebComponentModalProps + + expect(shallow()).toMatchSnapshot() + }) +}) diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/client-web-component-config.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/client-web-component-config.tsx new file mode 100644 index 0000000000..e8316fd1b0 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/client-web-component-config.tsx @@ -0,0 +1,51 @@ +import React, { useEffect, useState } from 'react' +import { Button } from '@reapit/elements' +import { useDispatch, useSelector } from 'react-redux' +import { selectWebComponentData } from '@/selector/client' +import { clientFetchWebComponentConfig } from '@/actions/client' +import { Dispatch } from 'redux' +import WebComponentModal from '@/components/pages/app-detail/client/web-component-config-modal/config-modal' +import { AppDetailSection } from '../../common/ui-helpers' +import { selectClientId } from '@/selector/auth' +import { useParams } from 'react-router-dom' + +export const toggleWebComponentModal = (setIsOpenConfigModal, isOpen) => () => { + setIsOpenConfigModal(isOpen) +} + +export const handleFetchWebComponentConfig = ( + dispatch: Dispatch, + customerId?: string, + applicationId?: string, +) => () => { + customerId && applicationId && dispatch(clientFetchWebComponentConfig({ customerId, applicationId })) +} + +export const WebComponentConfig: React.FC = () => { + const dispatch = useDispatch() + const [isOpenConfigModal, setIsOpenConfigModal] = useState(false) + + const clientId = useSelector(selectClientId) || '' + const webComponentData = useSelector(selectWebComponentData) + const { appid: applicationId } = useParams() + + const handleToggleWebComponentModal = toggleWebComponentModal(setIsOpenConfigModal, true) + const handleCloseWebComponentModal = toggleWebComponentModal(setIsOpenConfigModal, false) + + useEffect(handleFetchWebComponentConfig(dispatch, clientId, applicationId), []) + + if (!webComponentData) return null + + return ( + + + {isOpenConfigModal && ( + + )} + + ) +} + +export default WebComponentConfig diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/config-modal-inner.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/config-modal-inner.tsx new file mode 100644 index 0000000000..f3df5e8680 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/config-modal-inner.tsx @@ -0,0 +1,172 @@ +import React from 'react' +import { + ModalHeader, + ModalBody, + ModalFooter, + Button, + Formik, + RadioSelect, + Checkbox, + Loader, + FormikValues, + DropdownSelect, + SelectOption, + FormikProps, +} from '@reapit/elements' +import { useDispatch, useSelector } from 'react-redux' +import { clientUpdateWebComponentConfig } from '@/actions/client' +import { + selectWebComponentData, + selectWebComponentLoading, + selectWebComponentUpdating, + selectWebComponentNegotiators, +} from '@/selector/client' +import { UpdateWebComponentConfigParams } from '@/services/web-component' +import { Dispatch } from 'redux' +import { NegotiatorItem } from '@/services/negotiators' +import { selectAppDetailData } from '@/selector/client-app-detail' + +export const updateWebComponentConfig = (dispatch: Dispatch, appId: string, callback) => (params: FormikValues) => { + dispatch(clientUpdateWebComponentConfig({ ...params, appId, callback } as UpdateWebComponentConfigParams)) +} + +export const genarateNegotiatorOptions = (negotiators: NegotiatorItem[]): SelectOption[] => { + return negotiators.map( + negotiator => + ({ + value: negotiator.id, + label: negotiator.name, + description: negotiator.name, + } as SelectOption), + ) +} +export type WebComponentConfigModalBodyProps = { + subtext: string + formikProps: FormikProps +} +export const WebComponentConfigModalBody = ({ subtext, formikProps }: WebComponentConfigModalBodyProps) => { + const { values, setFieldValue } = formikProps + const negotiators = useSelector(selectWebComponentNegotiators) + const negotiatorOptions = genarateNegotiatorOptions(negotiators) + + return ( + <> +

{subtext}

+ + +
+
+ + +
+ + + + + + + +
+
+
+ + + ) +} + +export type WebComponentConfigModalFooterProps = { + closeModal: () => void + formikProps: FormikProps +} + +export const WebComponentConfigModalFooter = ({ closeModal, formikProps }: WebComponentConfigModalFooterProps) => { + const updating = useSelector(selectWebComponentUpdating) + const { handleSubmit } = formikProps + return ( + <> + + + + ) +} + +export type WebComponentConfigModalInnerProps = { + closeModal: () => void +} + +export const WebComponentConfigModalInner = ({ closeModal }: WebComponentConfigModalInnerProps) => { + const dispatch = useDispatch() + + const webComponentData = useSelector(selectWebComponentData) + const loading = useSelector(selectWebComponentLoading) + const appDetails = useSelector(selectAppDetailData) + const { name, id = '' } = appDetails + + const handleUpdateWebComponentConfig = updateWebComponentConfig(dispatch, id, closeModal) + + const title = `${name} Configuration` + const subtext = `Please use the following form to configure your diary settings for your + ‘${name}’ widget on your website` + + const initialFormValues = (webComponentData || { + appointmentLength: 30, + appointmentTimeGap: 30, + daysOfWeek: ['1', '2', '3', '4', '5', '6'], + }) as FormikValues + + if (loading) return + return ( + + {formikProps => ( + <> + + } /> + } + /> + + )} + + ) +} diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/config-modal.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/config-modal.tsx new file mode 100644 index 0000000000..2c498c47bc --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/config-modal.tsx @@ -0,0 +1,17 @@ +import * as React from 'react' +import { Modal } from '@reapit/elements' +import { WebComponentConfigModalInner } from './config-modal-inner' + +export type WebComponentModalProps = { + afterClose: () => void + closeModal: () => void +} + +export const WebComponentModal = ({ afterClose, closeModal }: WebComponentModalProps) => { + return ( + + + + ) +} +export default WebComponentModal diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/index.ts b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/index.ts new file mode 100644 index 0000000000..fb707ab989 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/index.ts @@ -0,0 +1 @@ +export { default } from './client-web-component-config' diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/__snapshots__/app-authentication-detail.test.tsx.snap b/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/__snapshots__/app-authentication-detail.test.tsx.snap new file mode 100644 index 0000000000..cb2c6409a3 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/__snapshots__/app-authentication-detail.test.tsx.snap @@ -0,0 +1,44 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AppAuthenticationDetail should match a snapshot 1`] = ` + + + +
+ +
+ Authentication: +
+
+ + Show Secret + +
+
+
+
+`; diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/__snapshots__/ui-app-header.test.tsx.snap b/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/__snapshots__/ui-app-header.test.tsx.snap new file mode 100644 index 0000000000..53470f105f --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/__snapshots__/ui-app-header.test.tsx.snap @@ -0,0 +1,38 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AppHeader should match a snapshot 1`] = ` + + +
+
+ Peter's Properties +
+
+ + Peter's Properties + + + Verified by Reapit + + +
+
+
+ + Featured Image + +
+`; diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/__snapshots__/ui-helpers.test.tsx.snap b/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/__snapshots__/ui-helpers.test.tsx.snap new file mode 100644 index 0000000000..63ca3cfe79 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/__snapshots__/ui-helpers.test.tsx.snap @@ -0,0 +1,33 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Ui Sections should match snapshot 1`] = ` + + + test + + some text + +`; + +exports[`Ui Sections should match snapshot for has image 1`] = ` + + some image + +`; + +exports[`Ui Sections should match snapshot for no images 1`] = `""`; + +exports[`Ui Sections should match snapshot 1`] = ` +
+ Test +
+`; diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/__snapshots__/ui-sections.test.tsx.snap b/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/__snapshots__/ui-sections.test.tsx.snap new file mode 100644 index 0000000000..84daec0213 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/__snapshots__/ui-sections.test.tsx.snap @@ -0,0 +1,431 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AdditionalImagesSection should match a snapshot where has images 1`] = ` + + + + + + + + +`; + +exports[`AdditionalImagesSection should match a snapshot where has no images 1`] = `""`; + +exports[`AuthenticationSection should match a snapshot where authFlow is CLIENT_SECRET and not sidebar 1`] = ` + +
+ + Client ID: + + SOME_ID +
+
+`; + +exports[`AuthenticationSection should match a snapshot where authFlow is USER_SESSION sidebar 1`] = ` + +
+ + Client ID: + + SOME_ID +
+
+`; + +exports[`BackToAppsSection should match a snapshot 1`] = ` + + + Back To Apps + + +`; + +exports[`CategorySection should match a snapshot where category is defined and is sidebar 1`] = ` + +

+ None +

+
+`; + +exports[`CategorySection should match a snapshot where category undefined and not sidebar 1`] = ` + +

+ None +

+
+`; + +exports[`DescriptionSection should match a snapshot 1`] = ` + +`; + +exports[`DesktopIntegrationSection should match a snapshot where desktopIntegrationTypes empty and not sidebar 1`] = ` + +

+ None +

+
+`; + +exports[`DesktopIntegrationSection should match a snapshot where desktopIntegrationTypes hasLength and is sidebar 1`] = ` + + + Identity Check + + + Property Marketing Information + + + Vendor Marketing Report + + + Property Details Generation + + + Applicant Export + + + Property + + + Applicant + + +`; + +exports[`DeveloperAboutSection should match a snapshot 1`] = ` + +`; + +exports[`DeveloperSection should match a snapshot 1`] = ` + + Developer Name + +`; + +exports[`DirectApiSection should match a snapshot where isDirectApi false and not sidebar 1`] = ` + + No + +`; + +exports[`DirectApiSection should match a snapshot where isDirectApi true and is sidebar 1`] = ` + + Yes + +`; + +exports[`InstallationsTableSection should not render a table when has no data 1`] = ` + + + +
+ +
+ Installations +
+
+

+ Currently, there are no installations for your app +

+
+
+
+
+`; + +exports[`InstallationsTableSection should render a table and match a snapshot when has data 1`] = ` + + + +
+ +
+ Installations +
+
+ +
+ + + +
+
+
+
+
+
+
+`; + +exports[`ListingPreviewSection should match a snapshot 1`] = ` + + + See listing preview + + + + + + + } + isSidebar={true} +> +

+ The listing preview will display your app as it would appear in the Marketplace +

+
+`; + +exports[`PermissionsSection should match a snapshot where has no permissions 1`] = ` + + + +`; + +exports[`PermissionsSection should match a snapshot where has permissions 1`] = ` + + + + Read data about developers + + + Write data about developers + + + +`; + +exports[`PrivateAppSection should match a snapshot where limitToClientIds empty and not sidebar 1`] = ` + + No + +`; + +exports[`PrivateAppSection should match a snapshot where limitToClientIds hasLength and is sidebar 1`] = ` + + Yes + +`; + +exports[`StatusSection should match a snapshot where isListed false and not sidebar 1`] = ` + +
+ Not listed +
+
+`; + +exports[`StatusSection should match a snapshot where isListed true and is sidebar 1`] = ` + +
+ Listed + +
+
+`; + +exports[`SummarySection should match a snapshot 1`] = ` + + Lorem ipsum + +`; diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/app-authentication-detail.test.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/app-authentication-detail.test.tsx new file mode 100644 index 0000000000..98d2688509 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/app-authentication-detail.test.tsx @@ -0,0 +1,63 @@ +import * as React from 'react' +import { mount } from 'enzyme' +import * as ReactRedux from 'react-redux' +import configureStore from 'redux-mock-store' +import appState from '@/reducers/__stubs__/app-state' +import { appDetailDataStub } from '@/sagas/__stubs__/app-detail' +import { + AppAuthenticationDetail, + AppAuthenticationDetailProps, + handleCopyCode, + handleShowAuthCode, + handleMouseLeave, +} from '../app-authentication-detail' +import { requestAuthenticationCode } from '@/actions/app-detail' + +const props: AppAuthenticationDetailProps = { + appId: appDetailDataStub.data.id || '', +} + +describe('AppAuthenticationDetail', () => { + let store + let spyDispatch + const mockSetTooltipMessage = jest.fn() + + beforeEach(() => { + /* mocking store */ + const mockStore = configureStore() + store = mockStore(appState) + spyDispatch = jest.spyOn(ReactRedux, 'useDispatch').mockImplementation(() => store.dispatch) + }) + it('should match a snapshot', () => { + expect( + mount( + + + , + ), + ).toMatchSnapshot() + }) + describe('handleCopyCode', () => { + it('should copy the code to clipboard', () => { + const fn = handleCopyCode(mockSetTooltipMessage) + fn() + expect(mockSetTooltipMessage).toHaveBeenCalledWith('Copied') + }) + }) + describe('handleShowAuthCode', () => { + it('should run correctly', () => { + const mockedEvent = { preventDefault: jest.fn() } + const fn = handleShowAuthCode(props.appId, spyDispatch) + fn(mockedEvent) + expect(mockedEvent.preventDefault).toBeCalled() + expect(spyDispatch).toBeCalledWith(requestAuthenticationCode(props.appId)) + }) + }) + describe('handleMouseLeave', () => { + it('should run correctly', () => { + const fn = handleMouseLeave(mockSetTooltipMessage) + fn() + expect(mockSetTooltipMessage).toBeCalledWith('Copy') + }) + }) +}) diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/ui-app-header.test.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/ui-app-header.test.tsx new file mode 100644 index 0000000000..2312ad09c9 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/ui-app-header.test.tsx @@ -0,0 +1,17 @@ +import * as React from 'react' +import { shallow } from 'enzyme' +import { appDetailDataStub } from '@/sagas/__stubs__/app-detail' +import AppHeader, { AppHeaderProps } from '../ui-app-header' + +const mockProps: AppHeaderProps = { + appDetailData: { + ...appDetailDataStub.data, + apiKey: '', + }, +} + +describe('AppHeader', () => { + it('should match a snapshot', () => { + expect(shallow()).toMatchSnapshot() + }) +}) diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/ui-helpers.test.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/ui-helpers.test.tsx new file mode 100644 index 0000000000..4cdbb85a32 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/ui-helpers.test.tsx @@ -0,0 +1,33 @@ +import React from 'react' +import { Tag, AppDetailSection, ImageSection } from '../ui-helpers' +import { shallow } from 'enzyme' + +describe('Ui Sections', () => { + test(' should match snapshot', () => { + const wrapper = shallow( + + some text + , + ) + expect(wrapper).toMatchSnapshot() + }) + + test(' should match snapshot', () => { + const wrapper = shallow(Test) + expect(wrapper).toMatchSnapshot() + }) + + test(' should match snapshot for no images', () => { + const wrapper = shallow(Test) + expect(wrapper).toMatchSnapshot() + }) + + test(' should match snapshot for has image', () => { + const wrapper = shallow( + + Test + , + ) + expect(wrapper).toMatchSnapshot() + }) +}) diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/ui-sections.test.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/ui-sections.test.tsx new file mode 100644 index 0000000000..ddf84e6a48 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/ui-sections.test.tsx @@ -0,0 +1,200 @@ +import * as React from 'react' +import { shallow, mount } from 'enzyme' +import { + CategorySection, + DesktopIntegrationSection, + PrivateAppSection, + DirectApiSection, + StatusSection, + BackToAppsSection, + ListingPreviewSection, + AuthenticationSection, + SummarySection, + DeveloperSection, + DeveloperAboutSection, + AdditionalImagesSection, +} from '../ui-sections' +import { appDetailDataStub } from '@/sagas/__stubs__/app-detail' +import { integrationTypesStub } from '@/sagas/__stubs__/integration-types' +import { + DesktopIntegrationTypeModel, + InstallationModel, + MediaModel, + ScopeModel, +} from '@reapit/foundations-ts-definitions' +import { Button } from '@reapit/elements' +import { InstallationsTableSection, PermissionsSection, DescriptionSection } from '../ui-sections' +import { installationsStub } from '@/sagas/__stubs__/installations' + +describe('CategorySection', () => { + it('should match a snapshot where category undefined and not sidebar', () => { + expect(shallow()).toMatchSnapshot() + }) + + it('should match a snapshot where category is defined and is sidebar', () => { + expect(shallow()).toMatchSnapshot() + }) +}) + +describe('DesktopIntegrationSection', () => { + it('should match a snapshot where desktopIntegrationTypes empty and not sidebar', () => { + expect(shallow()).toMatchSnapshot() + }) + + it('should match a snapshot where desktopIntegrationTypes hasLength and is sidebar', () => { + expect( + shallow( + , + ), + ).toMatchSnapshot() + }) +}) + +describe('PrivateAppSection', () => { + it('should match a snapshot where limitToClientIds empty and not sidebar', () => { + expect(shallow()).toMatchSnapshot() + }) + + it('should match a snapshot where limitToClientIds hasLength and is sidebar', () => { + expect(shallow()).toMatchSnapshot() + }) +}) + +describe('DirectApiSection', () => { + it('should match a snapshot where isDirectApi false and not sidebar', () => { + expect(shallow()).toMatchSnapshot() + }) + + it('should match a snapshot where isDirectApi true and is sidebar', () => { + expect(shallow()).toMatchSnapshot() + }) +}) + +describe('StatusSection', () => { + it('should match a snapshot where isListed false and not sidebar', () => { + expect(shallow()).toMatchSnapshot() + }) + + it('should match a snapshot where isListed true and is sidebar', () => { + expect(shallow()).toMatchSnapshot() + }) +}) + +describe('BackToAppsSection', () => { + it('should match a snapshot', () => { + expect(shallow()).toMatchSnapshot() + }) + + it('should respond to a button click', () => { + const onClick = jest.fn() + const button = shallow() + .find(Button) + .first() + + button.simulate('click') + expect(onClick).toHaveBeenCalledTimes(1) + }) +}) + +describe('ListingPreviewSection', () => { + it('should match a snapshot', () => { + expect(shallow()).toMatchSnapshot() + }) + + it('should respond to a link click', () => { + const onClick = jest.fn() + const link = mount() + .find('a') + .first() + + link.simulate('click') + expect(onClick).toHaveBeenCalledTimes(1) + }) +}) + +describe('AuthenticationSection', () => { + it('should match a snapshot where authFlow is CLIENT_SECRET and not sidebar', () => { + expect( + shallow(), + ).toMatchSnapshot() + }) + + it('should match a snapshot where authFlow is USER_SESSION sidebar', () => { + expect( + shallow(), + ).toMatchSnapshot() + }) +}) + +describe('SummarySection', () => { + it('should match a snapshot', () => { + expect(shallow()).toMatchSnapshot() + }) +}) + +describe('InstallationsTableSection', () => { + it('should render a table and match a snapshot when has data', () => { + const wrapper = mount( + , + ) + expect(wrapper.find('[data-test="render-installations-table"]').length).toBe(1) + expect(wrapper).toMatchSnapshot() + }) + + it('should not render a table when has no data', () => { + const wrapper = mount() + expect(wrapper.find('[data-test="render-installations-table-empty-text"]').length).toBe(1) + expect(wrapper).toMatchSnapshot() + }) +}) + +describe('DeveloperSection', () => { + it('should match a snapshot', () => { + expect(shallow()).toMatchSnapshot() + }) +}) + +describe('DeveloperAboutSection', () => { + it('should match a snapshot', () => { + expect(shallow()).toMatchSnapshot() + }) +}) + +describe('AdditionalImagesSection', () => { + it('should match a snapshot where has images', () => { + expect( + shallow( + , + ), + ).toMatchSnapshot() + }) + + it('should match a snapshot where has no images', () => { + expect(shallow()).toMatchSnapshot() + }) +}) + +describe('PermissionsSection', () => { + it('should match a snapshot where has permissions', () => { + expect( + shallow(), + ).toMatchSnapshot() + }) + + it('should match a snapshot where has no permissions', () => { + expect(shallow()).toMatchSnapshot() + }) +}) + +describe('DescriptionSection', () => { + it('should match a snapshot', () => { + expect(shallow()).toMatchSnapshot() + }) +}) diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/common/app-authentication-detail.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/common/app-authentication-detail.tsx new file mode 100644 index 0000000000..e670274857 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/common/app-authentication-detail.tsx @@ -0,0 +1,68 @@ +import * as React from 'react' +import { Dispatch } from 'redux' +import { useSelector, useDispatch } from 'react-redux' +import { requestAuthenticationCode } from '@/actions/app-detail' +import styles from '@/styles/blocks/app-authentication-detail.scss?mod' +import { Loader, Content, H5 } from '@reapit/elements' +import { FaCopy } from 'react-icons/fa' +import { CopyToClipboard } from 'react-copy-to-clipboard' +import { selectAppAuthenticationCode, selectAppAuthenticationLoading } from '@/selector/app-detail' + +export type AppAuthenticationDetailProps = { + appId: string + withCustomHeader?: boolean +} + +export const handleCopyCode = (setTooltipMessage: React.Dispatch>) => { + return () => { + setTooltipMessage('Copied') + } +} + +export const handleShowAuthCode = (appId: string, dispatch: Dispatch) => { + return e => { + e.preventDefault() + dispatch(requestAuthenticationCode(appId)) + } +} + +export const handleMouseLeave = (setTooltipMessage: React.Dispatch>) => { + return () => { + setTooltipMessage('Copy') + } +} + +export const AppAuthenticationDetail: React.FunctionComponent = ({ + appId, + withCustomHeader, +}) => { + const dispatch = useDispatch() + const [tooltipMessage, setTooltipMessage] = React.useState('Copy') + const loading = useSelector(selectAppAuthenticationLoading) + const code = useSelector(selectAppAuthenticationCode) + + return ( + <> + + {!withCustomHeader &&
Authentication:
} + + Show Secret + +
+ {loading && } + {!loading && code && ( +
+

{code}

+ +
+ + {tooltipMessage} +
+
+
+ )} + + ) +} + +export default AppAuthenticationDetail diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/common/ui-app-header.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/common/ui-app-header.tsx new file mode 100644 index 0000000000..9a6b02e8ae --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/common/ui-app-header.tsx @@ -0,0 +1,48 @@ +import * as React from 'react' +import { H3, Grid, GridItem, SubTitleH6 } from '@reapit/elements' +import { FaCheck } from 'react-icons/fa' +import { AppDetailModel } from '@reapit/foundations-ts-definitions' +import styles from '@/styles/blocks/standalone-app-detail.scss?mod' +import { MEDIA_INDEX } from '@/constants/media' +import ImagePlaceHolder from '@/assets/images/default-app-icon.jpg' +import featureImagePlaceHolder from '@/assets/images/default-feature-image.jpg' +import { cx } from 'linaria' + +export type AppHeaderProps = { + appDetailData: AppDetailModel & { + apiKey?: string | undefined + } + buttonGroup?: React.ReactNode +} + +const AppHeader: React.FC = ({ appDetailData, buttonGroup }) => { + const { media } = appDetailData + const appIcon = media?.filter(({ type }) => type === 'icon')[MEDIA_INDEX.ICON] + const featureImageSrc = appDetailData?.media?.[MEDIA_INDEX.FEATURE_IMAGE]?.uri + const { containerOuterHeader, headerContent, containerHeader, check, appIconContainer } = styles + + return ( + + +
+
+ {appDetailData.name} +
+
+

{appDetailData.name || ''}

+ + Verified by Reapit + + + {buttonGroup && buttonGroup} +
+
+
+ + Featured Image + +
+ ) +} + +export default AppHeader diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/common/ui-helpers.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/common/ui-helpers.tsx new file mode 100644 index 0000000000..ba299f4bcd --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/common/ui-helpers.tsx @@ -0,0 +1,37 @@ +import React from 'react' +import styles from '@/styles/blocks/standalone-app-detail.scss?mod' + +import { H5, Content } from '@reapit/elements' + +interface AppDetailSectionProps { + headerText: string | React.ReactNode + dataTest?: string + isSidebar?: boolean +} + +interface ImageSectionProps { + uri?: string + alt?: string +} + +export const AppDetailSection: React.FC = ({ + headerText, + children, + dataTest = '', + isSidebar = false, +}) => ( + +
{headerText}
+ {children} +
+) + +export const Tag: React.FC = ({ children }) =>
{children}
+ +export const ImageSection: React.FC = ({ uri, alt = '' }) => { + return uri ? ( + + {alt} + + ) : null +} diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/common/ui-sections.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/common/ui-sections.tsx new file mode 100644 index 0000000000..f44d07d923 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/common/ui-sections.tsx @@ -0,0 +1,247 @@ +import * as React from 'react' +import { + CategoryModel, + DesktopIntegrationTypeModel, + InstallationModel, + MediaModel, + ScopeModel, +} from '@reapit/foundations-ts-definitions' +import { AppDetailSection, Tag, ImageSection } from './ui-helpers' +import { convertBooleanToYesNoString } from '@/utils/boolean-to-yes-no-string' +import { FaCheck, FaExternalLinkAlt } from 'react-icons/fa' +import styles from '@/styles/blocks/standalone-app-detail.scss?mod' +import { + LevelRight, + Button, + Table, + Grid, + GridItem, + GridFourCol, + GridFourColItem, + HTMLRender, + Content, +} from '@reapit/elements' +import AuthFlow from '@/constants/app-auth-flow' +import AppAuthenticationDetail from '../../app-detail/app-authentication-detail' + +interface IsSidebar { + isSidebar?: boolean +} + +interface CategorySectionProps extends IsSidebar { + category: CategoryModel | undefined +} + +interface DesktopIntegrationSectionProps extends IsSidebar { + desktopIntegrationTypes: DesktopIntegrationTypeModel[] +} + +interface PrivateAppSectionProps extends IsSidebar { + limitToClientIds: string[] +} + +interface DirectApiSectionProps extends IsSidebar { + isDirectApi: boolean | undefined +} + +interface StatusSectionProps extends IsSidebar { + isListed: boolean | undefined +} + +interface BackToAppsSectionProps { + onClick: () => void +} + +interface ListingPreviewSectionProps extends IsSidebar { + onClick: () => void +} + +interface AuthenticationSectionProps extends IsSidebar { + authFlow: string + id: string + externalId: string +} + +interface SummarySectionProps { + summary: string +} + +interface InstallationsTableSectionProps extends IsSidebar { + data: InstallationModel[] + columns: any[] +} + +interface DeveloperSectionProps extends IsSidebar { + developer: string +} + +interface AdditionalImagesSectionProps { + images: MediaModel[] + splitIndex: number + numberImages: number +} + +interface PermissionSectionProps { + permissions: ScopeModel[] +} + +interface DescriptionSectionProps { + description: string +} + +export const CategorySection: React.FC = ({ category, isSidebar = false }) => ( + + {category ? {category.name} :

None

} +
+) + +export const DesktopIntegrationSection: React.FC = ({ + desktopIntegrationTypes, + isSidebar = false, +}) => ( + + {desktopIntegrationTypes.length ? ( + desktopIntegrationTypes.map(({ name }) => {name}) + ) : ( +

None

+ )} +
+) + +export const PrivateAppSection: React.FC = ({ limitToClientIds, isSidebar = false }) => ( + + {convertBooleanToYesNoString(Boolean(limitToClientIds.length > 0))} + +) + +export const DirectApiSection: React.FC = ({ isDirectApi, isSidebar = false }) => ( + + {convertBooleanToYesNoString(Boolean(isDirectApi))} + +) + +export const StatusSection: React.FC = ({ isListed, isSidebar = false }) => ( + +
+ {isListed ? ( + <> + Listed + + ) : ( + 'Not listed' + )} +
+
+) + +export const BackToAppsSection: React.FC = ({ onClick }) => ( + + + +) + +export const ListingPreviewSection: React.FC = ({ onClick, isSidebar = false }) => { + const onListingPreviewClick = (e: React.MouseEvent) => { + e.preventDefault() + onClick() + } + return ( + + See listing preview{' '} + + + + + } + isSidebar={isSidebar} + > +

The listing preview will display your app as it would appear in the Marketplace

+
+ ) +} + +export const AuthenticationSection: React.FC = ({ + authFlow, + id, + externalId, + isSidebar = false, +}) => ( + +
+ Client ID: + {externalId} +
+ {authFlow === AuthFlow.CLIENT_SECRET && } +
+) + +export const SummarySection: React.FC = ({ summary }) => ( + {summary} +) + +export const InstallationsTableSection: React.FC = ({ + data, + columns, + isSidebar = false, +}) => { + const testId = !data.length ? 'render-installations-table-empty-text' : 'render-installations-table' + return ( + + {!data.length ? ( +

Currently, there are no installations for your app

+ ) : ( + + )} + + ) +} + +export const DeveloperSection: React.FC = ({ isSidebar = false, developer }) => ( + + {developer} + +) + +export const DeveloperAboutSection: React.FC = ({ isSidebar = false }) => ( + +) + +export const AdditionalImagesSection: React.FC = ({ + images, + splitIndex, + numberImages, +}) => { + const extraImages = images.filter((_, index) => index > splitIndex) + return ( + (extraImages.length && ( + + {extraImages.map(({ uri }, index) => { + if (index < numberImages) { + return ( + + + + ) + } + })} + + )) || + null + ) +} + +export const PermissionsSection: React.FC = ({ permissions }) => ( + + + {permissions.map(permission => ( + {permission?.description ?? ''} + ))} + + +) + +export const DescriptionSection: React.FC = ({ description }) => ( + +) diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/index.ts b/packages/developer-portal/src/components/pages/app-detail-preview/index.ts new file mode 100644 index 0000000000..fb8e68535f --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/index.ts @@ -0,0 +1,2 @@ +import AppDetailPreview from './app-detail-preview' +export default AppDetailPreview diff --git a/packages/developer-portal/src/core/router.tsx b/packages/developer-portal/src/core/router.tsx index b6cfcc0bc4..f90ea1017f 100644 --- a/packages/developer-portal/src/core/router.tsx +++ b/packages/developer-portal/src/core/router.tsx @@ -24,6 +24,7 @@ const AnalyticsPage = React.lazy(() => catchChunkError(() => import('@/component const RegisterConfirm = React.lazy(() => catchChunkError(() => import('../components/pages/register-confirm'))) const WebhooksPage = React.lazy(() => catchChunkError(() => import('../components/pages/webhooks'))) const SettingsPage = React.lazy(() => catchChunkError(() => import('../components/pages/settings/'))) +const AppDetailPreview = React.lazy(() => catchChunkError(() => import('../components/pages/app-detail-preview'))) const SettingsOrganisationTabPage = React.lazy(() => catchChunkError(() => import('../components/pages/settings/settings-organisation-tab')), @@ -84,6 +85,7 @@ const Router = () => { path={Routes.DEVELOPER_EDITION_DOWNLOAD} component={EditionDownloadPage} /> + } /> diff --git a/packages/developer-portal/src/styles/blocks/installed-app-card.scss b/packages/developer-portal/src/styles/blocks/installed-app-card.scss deleted file mode 100644 index 8a96ca9731..0000000000 --- a/packages/developer-portal/src/styles/blocks/installed-app-card.scss +++ /dev/null @@ -1,53 +0,0 @@ -$icon-width: 80px; -$icon-height: 80px; - -.container { - display: flex; - width: 50%; - flex-direction: column; - align-items: center; - - @media (min-width: 400px) { - width: 33%; - } - - @media (min-width: 600px) { - width: 25%; - } -} -.wrapIcon { - width: $icon-width; - height: $icon-height; - cursor: pointer; - background-color: #ffffff; - display: flex; - justify-content: center; - align-items: center; - overflow: hidden; -} - -.icon { - width: $icon-width - 30px; - max-height: $icon-height - 30px; -} - -.appTitle { - width: $icon-width * 1.5; - white-space: nowrap; - margin-top: 1rem; - text-align: center; - text-overflow: ellipsis; - overflow: hidden; - font-size: 16px; - font-weight: bold; -} - -.content { - display: -webkit-box; - -webkit-line-clamp: 3; - -webkit-box-orient: vertical; - max-width: 700px; - overflow: hidden; - text-overflow: ellipsis; - height: 4.5rem; -} diff --git a/packages/developer-portal/src/styles/blocks/installed-app-list.scss b/packages/developer-portal/src/styles/blocks/installed-app-list.scss deleted file mode 100644 index 08e5a4d6ac..0000000000 --- a/packages/developer-portal/src/styles/blocks/installed-app-list.scss +++ /dev/null @@ -1,17 +0,0 @@ -@import './installed-app-card.scss'; - -.contentIsLoading { - opacity: 0.75; -} -.wrapList { - display: flex; - flex-wrap: wrap; - justify-items: center; - justify-content: flex-start; - align-items: center; - - & > * { - margin-bottom: 1rem; - } -} - From ddeb6e3b16edd2cdb098e04deaaaedbc569bb115 Mon Sep 17 00:00:00 2001 From: Vu Nguyen Date: Wed, 15 Jul 2020 13:00:45 +0700 Subject: [PATCH 2/6] feat: #1917 Update tests + remove unused files --- .../app-detail-preview.test.tsx.snap} | 39 +- .../__tests__/app-detail-preview.test.tsx | 50 + .../app-detail-preview/app-detail-preview.tsx | 20 +- .../__snapshots__/app-detail.test.tsx.snap | 488 ------ .../app-install-confirmation.test.tsx.snap | 132 -- .../app-uninstall-confirmation.test.tsx.snap | 160 -- .../client/__tests__/app-content.test.tsx | 4 +- .../client/__tests__/app-detail.test.tsx | 190 -- .../app-install-confirmation.test.tsx | 107 -- .../app-uninstall-confirmation.test.tsx | 130 -- .../client/__tests__/aside.test.tsx | 4 +- .../app-detail-preview/client/app-detail.tsx | 159 -- .../pages/app-detail-preview/client/aside.tsx | 11 +- .../pages/app-detail-preview/client/index.ts | 1 - .../__stubs__/web-component-config.ts | 14 - .../config-modal-inner.test.tsx.snap | 1537 ----------------- .../__snapshots__/config-modal.test.tsx.snap | 13 - .../client-web-component-config.test.tsx | 31 - .../__test__/config-modal-inner.test.tsx | 94 - .../__test__/config-modal.test.tsx | 15 - .../client-web-component-config.tsx | 51 - .../config-modal-inner.tsx | 172 -- .../config-modal.tsx | 17 - .../web-component-config-modal/index.ts | 1 - .../__tests__/__snapshots__/router.tsx.snap | 13 + 25 files changed, 116 insertions(+), 3337 deletions(-) rename packages/developer-portal/src/components/pages/app-detail-preview/{client/web-component-config-modal/__test__/__snapshots__/client-web-component-config.test.tsx.snap => __tests__/__snapshots__/app-detail-preview.test.tsx.snap} (52%) create mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/__tests__/app-detail-preview.test.tsx delete mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-detail.test.tsx.snap delete mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-install-confirmation.test.tsx.snap delete mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-uninstall-confirmation.test.tsx.snap delete mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-detail.test.tsx delete mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-install-confirmation.test.tsx delete mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-uninstall-confirmation.test.tsx delete mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/app-detail.tsx delete mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/index.ts delete mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__stubs__/web-component-config.ts delete mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/__snapshots__/config-modal-inner.test.tsx.snap delete mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/__snapshots__/config-modal.test.tsx.snap delete mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/client-web-component-config.test.tsx delete mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/config-modal-inner.test.tsx delete mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/config-modal.test.tsx delete mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/client-web-component-config.tsx delete mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/config-modal-inner.tsx delete mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/config-modal.tsx delete mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/index.ts diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/__snapshots__/client-web-component-config.test.tsx.snap b/packages/developer-portal/src/components/pages/app-detail-preview/__tests__/__snapshots__/app-detail-preview.test.tsx.snap similarity index 52% rename from packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/__snapshots__/client-web-component-config.test.tsx.snap rename to packages/developer-portal/src/components/pages/app-detail-preview/__tests__/__snapshots__/app-detail-preview.test.tsx.snap index 5240a1c2cc..42424de27a 100644 --- a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/__snapshots__/client-web-component-config.test.tsx.snap +++ b/packages/developer-portal/src/components/pages/app-detail-preview/__tests__/__snapshots__/app-detail-preview.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`WebComponentConfig Should match snapshot 1`] = ` +exports[`AppDetailPreview should match a snapshot 1`] = ` - + + +
+ +
+
+
+
+
+
+ +
+ + diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/__tests__/app-detail-preview.test.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/__tests__/app-detail-preview.test.tsx new file mode 100644 index 0000000000..dc26a24366 --- /dev/null +++ b/packages/developer-portal/src/components/pages/app-detail-preview/__tests__/app-detail-preview.test.tsx @@ -0,0 +1,50 @@ +import * as React from 'react' +import * as ReactRedux from 'react-redux' +import { MemoryRouter } from 'react-router' +import { mount } from 'enzyme' +import configureStore from 'redux-mock-store' +import AppDetailPreview, { loadAppDetailPreviewDataFromLocalStorage } from '../app-detail-preview' +import Routes from '@/constants/routes' +import appState from '@/reducers/__stubs__/app-state' +import { appDetailDataStub } from '@/sagas/__stubs__/app-detail' + +describe('AppDetailPreview', () => { + let store + let spyDispatch + beforeEach(() => { + /* mocking store */ + const mockStore = configureStore() + store = mockStore({ + ...appState, + client: { + appDetail: { + data: {}, + loading: false, + }, + }, + }) + spyDispatch = jest.spyOn(ReactRedux, 'useDispatch').mockImplementation(() => store.dispatch) + }) + + it('should match a snapshot', () => { + expect( + mount( + + + + + , + ), + ).toMatchSnapshot() + }) + it('should run correctly', () => { + window.localStorage.setItem('developer-preview-app', JSON.stringify(appDetailDataStub.data)) + const mockAppId = '9b6fd5f7-2c15-483d-b925-01b650538e52' + const mockSetAppDetailPreviewData = jest.fn() + const spyLocalStorageGetItem = jest.spyOn(window.localStorage, 'getItem') + const fn = loadAppDetailPreviewDataFromLocalStorage(mockAppId, mockSetAppDetailPreviewData, spyDispatch) + fn() + expect(spyLocalStorageGetItem).toBeCalledWith('developer-preview-app') + expect(mockSetAppDetailPreviewData).toBeCalled() + }) +}) diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/app-detail-preview.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/app-detail-preview.tsx index db6abb1d7a..32417f9ae5 100644 --- a/packages/developer-portal/src/components/pages/app-detail-preview/app-detail-preview.tsx +++ b/packages/developer-portal/src/components/pages/app-detail-preview/app-detail-preview.tsx @@ -7,33 +7,45 @@ import { AppDetailData } from '@/reducers/developer' import { Grid, Loader, GridItem, Section } from '@reapit/elements' import { BackToAppsSection } from '../app-detail/app-sections' import useReactResponsive from '@/components/hooks/use-react-responsive' +import { Dispatch } from 'redux' +import { showNotificationMessage } from '@/actions/notification-message' +import { useDispatch } from 'react-redux' export type AppDetailPreviewProps = {} export const loadAppDetailPreviewDataFromLocalStorage = ( appId: string, setAppDetailPreviewData: React.Dispatch>, + dispatch: Dispatch, ) => () => { try { const appDataString = localStorage.getItem('developer-preview-app') if (!appDataString) { - throw 'No app preview' + throw new Error('No app preview') } const appData = JSON.parse(appDataString) as AppDetailData if (appData?.id !== appId) { - throw 'No app preview' + throw new Error('No app preview') } setAppDetailPreviewData(appData) - } catch (err) {} + } catch (err) { + dispatch( + showNotificationMessage({ + message: err.message, + variant: 'danger', + }), + ) + } } const AppDetailPreview: React.FC = () => { + const dispatch = useDispatch() const { isMobile } = useReactResponsive() const [appDetailPreviewData, setAppDetailPreviewData] = React.useState(null) const { appId } = useParams() - React.useEffect(loadAppDetailPreviewDataFromLocalStorage(appId, setAppDetailPreviewData), [appId]) + React.useEffect(loadAppDetailPreviewDataFromLocalStorage(appId, setAppDetailPreviewData, dispatch), [appId, dispatch]) return ( diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-detail.test.tsx.snap b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-detail.test.tsx.snap deleted file mode 100644 index 147f5b6cff..0000000000 --- a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-detail.test.tsx.snap +++ /dev/null @@ -1,488 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`AppDetail renderAppHeaderButtonGroup should match snapshot 1`] = ` -
- -
-`; - -exports[`AppDetail renderAppHeaderButtonGroup should match snapshot 2`] = ` -
- -
-`; - -exports[`AppDetail should match a snapshot 1`] = ` - - - - - -
- -
-
-
-
-
-
- - - - - Confirm - - - Cancel - - - } - title="Confirm undefined uninstallation" - visible={false} - /> - - - - - - Confirm - - - Cancel - - - } - title="Confirm undefined installation" - visible={false} - /> - -
- - - - - -`; - -exports[`AppDetail should render loader when client.appDetail.data is an empty object 1`] = ` - - - - - -
- -
-
-
-
-
-
- - - - - Confirm - - - Cancel - - - } - title="Confirm undefined uninstallation" - visible={false} - /> - - - - - - Confirm - - - Cancel - - - } - title="Confirm undefined installation" - visible={false} - /> - -
- - - - - -`; - -exports[`AppDetail should render loader when isLoadingAppDetail = true 1`] = ` - - - - - -
- -
-
-
-
-
-
- - - - - Confirm - - - Cancel - - - } - title="Confirm undefined uninstallation" - visible={false} - /> - - - - - - Confirm - - - Cancel - - - } - title="Confirm undefined installation" - visible={false} - /> - -
- - - - - -`; diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-install-confirmation.test.tsx.snap b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-install-confirmation.test.tsx.snap deleted file mode 100644 index 81e038b37a..0000000000 --- a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-install-confirmation.test.tsx.snap +++ /dev/null @@ -1,132 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ClientAppInstallConfirmation should match a snapshot 1`] = ` - - - - - - - Confirm - - - Cancel - - - } - title="Confirm Peter's Properties installation" - visible={true} - /> - - - - -`; diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-uninstall-confirmation.test.tsx.snap b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-uninstall-confirmation.test.tsx.snap deleted file mode 100644 index f70d53f0b6..0000000000 --- a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-uninstall-confirmation.test.tsx.snap +++ /dev/null @@ -1,160 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ClientAppUninstallConfirmation renderUninstallConfirmationModalFooter should match snapshot 1`] = ` -
- - Confirm - - - Cancel - -
-`; - -exports[`ClientAppUninstallConfirmation should match a snapshot 1`] = ` - - - - - - - Confirm - - - Cancel - - - } - title="Confirm Peter's Properties uninstallation" - visible={true} - /> - - - - - -`; diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-content.test.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-content.test.tsx index b1afabd887..4f80c16f99 100644 --- a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-content.test.tsx +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-content.test.tsx @@ -2,11 +2,11 @@ import React from 'react' import AppContent from '../app-content' import { appDetailDataStub } from '@/sagas/__stubs__/app-detail' import { shallow } from 'enzyme' -import { AppDetailDataNotNull } from '@/reducers/client/app-detail' +import { AppDetailModel } from '@reapit/foundations-ts-definitions' describe('ClientAppContent', () => { it('ClientAppContent - should match snapshot', () => { - const wrapper = shallow() + const wrapper = shallow() expect(wrapper).toMatchSnapshot() }) }) diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-detail.test.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-detail.test.tsx deleted file mode 100644 index e21aba589a..0000000000 --- a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-detail.test.tsx +++ /dev/null @@ -1,190 +0,0 @@ -import * as React from 'react' -import * as ReactRedux from 'react-redux' -import TestRenderer from 'react-test-renderer' -import { MemoryRouter } from 'react-router' -import { mount, shallow } from 'enzyme' -import configureStore from 'redux-mock-store' -import { getMockRouterProps } from '@/utils/mock-helper' -import AppDetail, { - handleCloseInstallConfirmationModal, - handleInstallAppButtonClick, - renderAppHeaderButtonGroup, - handleCloseUnInstallConfirmationModal, - handleUnInstallAppButtonClick, - onBackToAppsButtonClick, -} from '../app-detail' -import { Button } from '@reapit/elements' -import Routes from '@/constants/routes' -import appState from '@/reducers/__stubs__/app-state' - -describe('AppDetail', () => { - const { history } = getMockRouterProps({}) - let store - beforeEach(() => { - /* mocking store */ - const mockStore = configureStore() - store = mockStore({ - ...appState, - client: { - appDetail: { - data: {}, - loading: false, - }, - }, - }) - }) - - it('should render loader when isLoadingAppDetail = true', () => { - const mockStore = configureStore() - const customStore = mockStore({ - ...appState, - client: { - appDetail: { - isAppDetailLoading: true, - data: {}, - }, - }, - }) - - const wrapper = mount( - - - - - , - ) - - expect(wrapper).toMatchSnapshot() - const loader = wrapper.find('[data-test="client-app-detail-loader"]') - expect(loader.length).toBe(1) - }) - - it('should render loader when client.appDetail.data is an empty object', () => { - const mockStore = configureStore() - const customStore = mockStore({ - ...appState, - client: { - appDetail: { - data: {}, - loading: false, - }, - }, - }) - - const wrapper = mount( - - - - - , - ) - - expect(wrapper).toMatchSnapshot() - const loader = wrapper.find('[data-test="client-app-detail-loader"]') - expect(loader.length).toBe(1) - }) - - it('should match a snapshot', () => { - expect( - mount( - - - - - , - ), - ).toMatchSnapshot() - }) - - describe('renderAppHeaderButtonGroup', () => { - const mockAppId = 'test' - const mockInstalledOn = '2020-2-20' - - it('should match snapshot', () => { - const wrapperWithIsInstallBtnHiddenTrue = shallow( -
{renderAppHeaderButtonGroup(mockAppId, mockInstalledOn, jest.fn(), jest.fn(), true, 'CLIENT')}
, - ) - expect(wrapperWithIsInstallBtnHiddenTrue).toMatchSnapshot() - const wrapperWithIsInstallBtnHiddenFalse = shallow( -
{renderAppHeaderButtonGroup(mockAppId, mockInstalledOn, jest.fn(), jest.fn(), false, 'CLIENT')}
, - ) - expect(wrapperWithIsInstallBtnHiddenFalse).toMatchSnapshot() - }) - - it('should render header button group when appId is existed', () => { - const testRenderer = TestRenderer.create( -
{renderAppHeaderButtonGroup(mockAppId, mockInstalledOn, jest.fn(), jest.fn(), false, 'CLIENT')}
, - ) - const testInstance = testRenderer.root - expect(testInstance.children.length).toBe(1) - }) - - it('should render install app button if installedOn is empty', () => { - const testRenderer = TestRenderer.create( -
{renderAppHeaderButtonGroup(mockAppId, '', jest.fn(), jest.fn(), false, 'CLIENT')}
, - ) - const testInstance = testRenderer.root - expect(testInstance.findByType(Button).props.children).toBe('Install App') - }) - - it('should render uninstall app button if installedOn is existed', () => { - const testRenderer = TestRenderer.create( -
{renderAppHeaderButtonGroup(mockAppId, 'exist', jest.fn(), jest.fn(), false, 'CLIENT')}
, - ) - const testInstance = testRenderer.root - expect(testInstance.findByType(Button).props.children).toBe('Uninstall App') - }) - - it('should not render header button group when appId is empty', () => { - const testRenderer = TestRenderer.create( -
{renderAppHeaderButtonGroup('', mockInstalledOn, jest.fn(), jest.fn(), false, 'CLIENT')}
, - ) - const testInstance = testRenderer.getInstance - expect(testInstance).toHaveLength(0) - }) - }) - - describe('handleCloseInstallConfirmationModal', () => { - it('should run correctly', () => { - const mockFunction = jest.fn() - const fn = handleCloseInstallConfirmationModal(mockFunction) - fn() - expect(mockFunction).toBeCalledWith(false) - }) - }) - - describe('handleInstallAppButtonClick', () => { - it('should run correctly', () => { - const mockFunction = jest.fn() - const fn = handleInstallAppButtonClick(mockFunction) - fn() - expect(mockFunction).toBeCalledWith(true) - }) - }) - - describe('handleCloseUnInstallConfirmationModal', () => { - it('should run correctly', () => { - const mockFunction = jest.fn() - const fn = handleCloseUnInstallConfirmationModal(mockFunction) - fn() - expect(mockFunction).toBeCalledWith(false) - }) - }) - - describe('handleUnInstallAppButtonClick', () => { - it('should run correctly', () => { - const mockFunction = jest.fn() - const fn = handleUnInstallAppButtonClick(mockFunction) - fn() - expect(mockFunction).toBeCalledWith(true) - }) - }) - - describe('onBackToAppsButtonClick', () => { - it('should run correctly', () => { - const fn = onBackToAppsButtonClick(history) - fn() - expect(history.push).toBeCalledWith(Routes.APPS) - }) - }) -}) diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-install-confirmation.test.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-install-confirmation.test.tsx deleted file mode 100644 index 776bf30ef9..0000000000 --- a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-install-confirmation.test.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import * as React from 'react' -import * as ReactRedux from 'react-redux' -import { mount } from 'enzyme' -import configureStore from 'redux-mock-store' -import { appDetailDataStub } from '@/sagas/__stubs__/app-detail' -import { MemoryRouter } from 'react-router' -import AppInstallConfirmation, { - AppInstallConfirmationProps, - handleInstallButtonClick, - handleInstallAppSuccessCallback, - handleSuccessAlertButtonClick, - handleSuccessAlertMessageAfterClose, -} from '../app-install-confirmation' -import { appInstallationsRequestInstall } from '@/actions/app-installations' -import { clientFetchAppDetail } from '@/actions/client' -import routes from '@/constants/routes' -import Routes from '@/constants/routes' -import appState from '@/reducers/__stubs__/app-state' - -const mockProps: AppInstallConfirmationProps = { - appDetailData: appDetailDataStub.data, - visible: true, - closeInstallConfirmationModal: jest.fn(), -} - -describe('ClientAppInstallConfirmation', () => { - let store - let spyDispatch - const appId = mockProps.appDetailData?.id || '' - const clientId = appState.auth.loginSession?.loginIdentity.clientId || '' - - beforeEach(() => { - /* mocking store */ - const mockStore = configureStore() - store = mockStore(appState) - /* mocking useDispatch on our mock store */ - spyDispatch = jest.spyOn(ReactRedux, 'useDispatch').mockImplementation(() => store.dispatch) - }) - - it('should match a snapshot', () => { - expect( - mount( - - - - - , - ), - ).toMatchSnapshot() - }) - - describe('handleInstallButtonClick', () => { - it('should run correctly', () => { - const mockFunction = jest.fn() - const fn = handleInstallButtonClick( - appId, - clientId, - spyDispatch, - mockFunction, - mockProps.closeInstallConfirmationModal, - false, - ) - fn() - expect(spyDispatch).toBeCalledWith( - appInstallationsRequestInstall({ - appId, - callback: expect.any(Function), - }), - ) - }) - }) - - describe('handleSuccessAlertMessageAfterClose', () => { - it('should run correctly', () => { - const mockFunction = jest.fn() - const fn = handleSuccessAlertMessageAfterClose(appId, clientId, mockFunction, spyDispatch) - fn() - expect(mockFunction).toBeCalledWith(false) - expect(spyDispatch).toBeCalledWith( - clientFetchAppDetail({ - id: appId, - clientId, - }), - ) - }) - }) - - describe('handleInstallAppSuccessCallback', () => { - it('should run correctly', () => { - const mockFunction = jest.fn() - const fn = handleInstallAppSuccessCallback(mockFunction, mockProps.closeInstallConfirmationModal, false) - fn() - - expect(mockProps.closeInstallConfirmationModal).toBeCalled() - expect(mockFunction).toBeCalledWith(true) - }) - }) - - describe('handleSuccessAlertButtonClick', () => { - const history = { - replace: jest.fn(), - } as any - const fn = handleSuccessAlertButtonClick(history) - fn() - expect(history.replace).toBeCalledWith(routes.APPS) - }) -}) diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-uninstall-confirmation.test.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-uninstall-confirmation.test.tsx deleted file mode 100644 index ca10ed9e53..0000000000 --- a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/app-uninstall-confirmation.test.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import * as React from 'react' -import * as ReactRedux from 'react-redux' -import { mount, shallow } from 'enzyme' -import configureStore from 'redux-mock-store' -import { MemoryRouter } from 'react-router' -import { appDetailDataStub } from '@/sagas/__stubs__/app-detail' -import { appInstallationsRequestUninstall } from '@/actions/app-installations' -import { clientFetchAppDetail } from '@/actions/client' -import ClientAppUninstallConfirmation, { - AppUninstallConfirmationProps, - onUninstallButtonClick, - handleUninstallAppSuccessCallback, - handleSuccessAlertButtonClick, - handleSuccessAlertMessageAfterClose, - renderUninstallConfirmationModalFooter, -} from '../app-uninstall-confirmation' -import Routes from '@/constants/routes' -import appState from '@/reducers/__stubs__/app-state' - -const mockProps: AppUninstallConfirmationProps = { - appDetailData: appDetailDataStub.data, - visible: true, - closeUninstallConfirmationModal: jest.fn(), -} - -describe('ClientAppUninstallConfirmation', () => { - let store - let spyDispatch - const appId = mockProps.appDetailData?.id || '' - const installationId = mockProps.appDetailData?.installationId || '' - const clientId = appState.auth.loginSession?.loginIdentity.clientId || '' - - beforeEach(() => { - /* mocking store */ - const mockStore = configureStore() - store = mockStore(appState) - /* mocking useDispatch on our mock store */ - spyDispatch = jest.spyOn(ReactRedux, 'useDispatch').mockImplementation(() => store.dispatch) - }) - - it('should match a snapshot', () => { - expect( - mount( - - - - - , - ), - ).toMatchSnapshot() - }) - - describe('onUninstallButtonClick', () => { - it('should run correctly', () => { - const mockFunction = jest.fn() - const fn = onUninstallButtonClick( - appId, - clientId, - installationId, - spyDispatch, - mockFunction, - mockProps.closeUninstallConfirmationModal, - false, - ) - fn() - expect(spyDispatch).toBeCalledWith( - appInstallationsRequestUninstall({ - appId, - installationId, - terminatedReason: 'User uninstall', - callback: expect.any(Function), - }), - ) - }) - }) - - describe('handleUninstallAppSuccessCallback', () => { - it('should run correctly', () => { - const mockFunction = jest.fn() - const fn = handleUninstallAppSuccessCallback(mockFunction, mockProps.closeUninstallConfirmationModal, false) - fn() - expect(mockProps.closeUninstallConfirmationModal).toBeCalled() - expect(mockFunction).toBeCalledWith(true) - }) - }) - - describe('handleSuccessAlertButtonClick', () => { - const history = { - replace: jest.fn(), - } as any - const fn = handleSuccessAlertButtonClick(history) - fn() - expect(history.replace).toBeCalledWith(Routes.APPS) - }) - - describe('handleSuccessAlertMessageAfterClose', () => { - it('should match snapshot', () => { - const mockFunction = jest.fn() - const fn = handleSuccessAlertMessageAfterClose(appId, clientId, mockFunction, spyDispatch) - fn() - expect(spyDispatch).toBeCalledWith( - clientFetchAppDetail({ - id: appId, - clientId, - }), - ) - expect(mockFunction).toBeCalledWith(false) - }) - }) - - describe('renderUninstallConfirmationModalFooter', () => { - it('should match snapshot', () => { - const wrapper = shallow( -
- {renderUninstallConfirmationModalFooter( - false, - appId, - clientId, - installationId, - spyDispatch, - jest.fn(), - jest.fn(), - false, - )} -
, - ) - expect(wrapper).toMatchSnapshot() - }) - }) -}) diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/aside.test.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/aside.test.tsx index 758bf2021d..21d4cc0e7c 100644 --- a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/aside.test.tsx +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/aside.test.tsx @@ -3,15 +3,15 @@ import { Aside } from '../aside' import { shallow } from 'enzyme' import { integrationTypesStub } from '@/sagas/__stubs__/integration-types' import { appDetailDataStub } from '@/sagas/__stubs__/app-detail' -import { AppDetailDataNotNull } from '@/reducers/client/app-detail' import { DesktopIntegrationTypeModel } from '@/actions/app-integration-types' +import { AppDetailModel } from '@reapit/foundations-ts-definitions' describe('ClientAside', () => { test('ClientAside - should match snapshot', () => { expect( shallow(
- -
-
-
- - - - - - } - > -
- - - - - - - - -
-
- - - -`; diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/__snapshots__/config-modal.test.tsx.snap b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/__snapshots__/config-modal.test.tsx.snap deleted file mode 100644 index 2cdda1d8f4..0000000000 --- a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/__snapshots__/config-modal.test.tsx.snap +++ /dev/null @@ -1,13 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Config-modal should match snapshot 1`] = ` - - - -`; diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/client-web-component-config.test.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/client-web-component-config.test.tsx deleted file mode 100644 index dc9f9fc2fe..0000000000 --- a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/client-web-component-config.test.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import * as React from 'react' -import { Provider } from 'react-redux' -import appState from '@/reducers/__stubs__/app-state' -import configureStore from 'redux-mock-store' -import { mount } from 'enzyme' -import { WebComponentConfig } from '../client-web-component-config' -import Routes from '@/constants/routes' -import { MemoryRouter } from 'react-router' - -describe('WebComponentConfig', () => { - const extendStore = { - ...appState, - client: { - webComponent: { isShowModal: true }, - }, - } - const mockStore = configureStore() - const store = mockStore(extendStore) - - it('Should match snapshot', () => { - expect( - mount( - - - - - , - ), - ).toMatchSnapshot() - }) -}) diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/config-modal-inner.test.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/config-modal-inner.test.tsx deleted file mode 100644 index 53c60ec457..0000000000 --- a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/config-modal-inner.test.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import React from 'react' -import { - updateWebComponentConfig, - WebComponentConfigModalFooter, - WebComponentConfigModalInner, - genarateNegotiatorOptions, -} from '../config-modal-inner' -import { mount } from 'enzyme' -import configureStore from 'redux-mock-store' -import { Provider } from 'react-redux' -import { UpdateWebComponentConfigParams } from '@/services/web-component' -import { clientUpdateWebComponentConfig } from '@/actions/client' -import { webComponentStub } from '../__stubs__/web-component-config' -import appState from '@/reducers/__stubs__/app-state' -import { FormikProps } from '@reapit/elements' - -const params = { - appId: 'appid', - appointmentLength: 1, - appointmentTimeGap: 1, - customerId: 'string', - daysOfWeek: ['1', '2'], -} as UpdateWebComponentConfigParams - -const extendAppState = webComponent => { - return { - ...appState, - client: { ...appState.client, webComponent }, - } -} - -describe('Config-modal-inner', () => { - const mockStore = configureStore() - const store = mockStore(extendAppState(webComponentStub)) - - it('should WebComponentConfigModalFooter match a snapshot', () => { - const mockProps = { - closeModal: jest.fn(), - formikProps: {} as FormikProps, - } - expect( - mount( - - - , - ), - ).toMatchSnapshot() - }) - - it('should WebComponentConfigModalInner match a snapshot', () => { - const mockStore = configureStore() - const store = mockStore(extendAppState(webComponentStub)) - expect( - mount( - - - , - ), - ).toMatchSnapshot() - }) - - it('should updateWebComponentConfig run correctly', () => { - const dispatch = jest.fn() - const closeModal = jest.fn() - - const fn = updateWebComponentConfig(dispatch, 'appid', closeModal) - fn(params) - expect(dispatch).toBeCalledWith(clientUpdateWebComponentConfig({ ...params, callback: closeModal })) - }) -}) - -describe('should return correctly', () => { - it('should return correctly', () => { - const list = [ - { - active: true, - created: 'string', - email: 'string', - id: 'string', - jobTitle: 'string', - metadata: 'any', - mobilePhone: 'string', - modified: 'string', - name: 'string', - officeId: 'string', - workPhone: 'string', - }, - ] - - const result = genarateNegotiatorOptions(list) - const expected = [{ value: 'string', label: 'string', description: 'string' }] - expect(result).toEqual(expected) - }) -}) diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/config-modal.test.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/config-modal.test.tsx deleted file mode 100644 index b696c59bd9..0000000000 --- a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/__test__/config-modal.test.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { WebComponentModal, WebComponentModalProps } from '../config-modal' -import React from 'react' -import { shallow } from 'enzyme' - -describe('Config-modal', () => { - it('should match snapshot', () => { - const mockProps = { - type: 'BOOK_VIEWING', - afterClose: jest.fn(), - closeModal: jest.fn(), - } as WebComponentModalProps - - expect(shallow()).toMatchSnapshot() - }) -}) diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/client-web-component-config.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/client-web-component-config.tsx deleted file mode 100644 index e8316fd1b0..0000000000 --- a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/client-web-component-config.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import React, { useEffect, useState } from 'react' -import { Button } from '@reapit/elements' -import { useDispatch, useSelector } from 'react-redux' -import { selectWebComponentData } from '@/selector/client' -import { clientFetchWebComponentConfig } from '@/actions/client' -import { Dispatch } from 'redux' -import WebComponentModal from '@/components/pages/app-detail/client/web-component-config-modal/config-modal' -import { AppDetailSection } from '../../common/ui-helpers' -import { selectClientId } from '@/selector/auth' -import { useParams } from 'react-router-dom' - -export const toggleWebComponentModal = (setIsOpenConfigModal, isOpen) => () => { - setIsOpenConfigModal(isOpen) -} - -export const handleFetchWebComponentConfig = ( - dispatch: Dispatch, - customerId?: string, - applicationId?: string, -) => () => { - customerId && applicationId && dispatch(clientFetchWebComponentConfig({ customerId, applicationId })) -} - -export const WebComponentConfig: React.FC = () => { - const dispatch = useDispatch() - const [isOpenConfigModal, setIsOpenConfigModal] = useState(false) - - const clientId = useSelector(selectClientId) || '' - const webComponentData = useSelector(selectWebComponentData) - const { appid: applicationId } = useParams() - - const handleToggleWebComponentModal = toggleWebComponentModal(setIsOpenConfigModal, true) - const handleCloseWebComponentModal = toggleWebComponentModal(setIsOpenConfigModal, false) - - useEffect(handleFetchWebComponentConfig(dispatch, clientId, applicationId), []) - - if (!webComponentData) return null - - return ( - - - {isOpenConfigModal && ( - - )} - - ) -} - -export default WebComponentConfig diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/config-modal-inner.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/config-modal-inner.tsx deleted file mode 100644 index f3df5e8680..0000000000 --- a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/config-modal-inner.tsx +++ /dev/null @@ -1,172 +0,0 @@ -import React from 'react' -import { - ModalHeader, - ModalBody, - ModalFooter, - Button, - Formik, - RadioSelect, - Checkbox, - Loader, - FormikValues, - DropdownSelect, - SelectOption, - FormikProps, -} from '@reapit/elements' -import { useDispatch, useSelector } from 'react-redux' -import { clientUpdateWebComponentConfig } from '@/actions/client' -import { - selectWebComponentData, - selectWebComponentLoading, - selectWebComponentUpdating, - selectWebComponentNegotiators, -} from '@/selector/client' -import { UpdateWebComponentConfigParams } from '@/services/web-component' -import { Dispatch } from 'redux' -import { NegotiatorItem } from '@/services/negotiators' -import { selectAppDetailData } from '@/selector/client-app-detail' - -export const updateWebComponentConfig = (dispatch: Dispatch, appId: string, callback) => (params: FormikValues) => { - dispatch(clientUpdateWebComponentConfig({ ...params, appId, callback } as UpdateWebComponentConfigParams)) -} - -export const genarateNegotiatorOptions = (negotiators: NegotiatorItem[]): SelectOption[] => { - return negotiators.map( - negotiator => - ({ - value: negotiator.id, - label: negotiator.name, - description: negotiator.name, - } as SelectOption), - ) -} -export type WebComponentConfigModalBodyProps = { - subtext: string - formikProps: FormikProps -} -export const WebComponentConfigModalBody = ({ subtext, formikProps }: WebComponentConfigModalBodyProps) => { - const { values, setFieldValue } = formikProps - const negotiators = useSelector(selectWebComponentNegotiators) - const negotiatorOptions = genarateNegotiatorOptions(negotiators) - - return ( - <> -

{subtext}

- - -
-
- - -
- - - - - - - -
-
-
- - - ) -} - -export type WebComponentConfigModalFooterProps = { - closeModal: () => void - formikProps: FormikProps -} - -export const WebComponentConfigModalFooter = ({ closeModal, formikProps }: WebComponentConfigModalFooterProps) => { - const updating = useSelector(selectWebComponentUpdating) - const { handleSubmit } = formikProps - return ( - <> - - - - ) -} - -export type WebComponentConfigModalInnerProps = { - closeModal: () => void -} - -export const WebComponentConfigModalInner = ({ closeModal }: WebComponentConfigModalInnerProps) => { - const dispatch = useDispatch() - - const webComponentData = useSelector(selectWebComponentData) - const loading = useSelector(selectWebComponentLoading) - const appDetails = useSelector(selectAppDetailData) - const { name, id = '' } = appDetails - - const handleUpdateWebComponentConfig = updateWebComponentConfig(dispatch, id, closeModal) - - const title = `${name} Configuration` - const subtext = `Please use the following form to configure your diary settings for your - ‘${name}’ widget on your website` - - const initialFormValues = (webComponentData || { - appointmentLength: 30, - appointmentTimeGap: 30, - daysOfWeek: ['1', '2', '3', '4', '5', '6'], - }) as FormikValues - - if (loading) return - return ( - - {formikProps => ( - <> - - } /> - } - /> - - )} - - ) -} diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/config-modal.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/config-modal.tsx deleted file mode 100644 index 2c498c47bc..0000000000 --- a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/config-modal.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import * as React from 'react' -import { Modal } from '@reapit/elements' -import { WebComponentConfigModalInner } from './config-modal-inner' - -export type WebComponentModalProps = { - afterClose: () => void - closeModal: () => void -} - -export const WebComponentModal = ({ afterClose, closeModal }: WebComponentModalProps) => { - return ( - - - - ) -} -export default WebComponentModal diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/index.ts b/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/index.ts deleted file mode 100644 index fb707ab989..0000000000 --- a/packages/developer-portal/src/components/pages/app-detail-preview/client/web-component-config-modal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './client-web-component-config' diff --git a/packages/developer-portal/src/core/__tests__/__snapshots__/router.tsx.snap b/packages/developer-portal/src/core/__tests__/__snapshots__/router.tsx.snap index 4fa08462ff..fd8d8ca8b4 100644 --- a/packages/developer-portal/src/core/__tests__/__snapshots__/router.tsx.snap +++ b/packages/developer-portal/src/core/__tests__/__snapshots__/router.tsx.snap @@ -260,6 +260,19 @@ exports[`Router should match a snapshot 1`] = ` } path="/developer/edition-download" /> + From fb3fa8e1834e1324e55b48e0abcdc1a9f44deaef Mon Sep 17 00:00:00 2001 From: Vu Nguyen Date: Wed, 15 Jul 2020 15:03:20 +0700 Subject: [PATCH 3/6] feat: #1917 Update comments --- .../pages/app-detail-preview/app-detail-preview.tsx | 6 ++---- .../src/components/pages/app-detail/client/app-detail.tsx | 2 ++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/app-detail-preview.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/app-detail-preview.tsx index 32417f9ae5..c2fcabfc4e 100644 --- a/packages/developer-portal/src/components/pages/app-detail-preview/app-detail-preview.tsx +++ b/packages/developer-portal/src/components/pages/app-detail-preview/app-detail-preview.tsx @@ -1,3 +1,5 @@ +// The UI of this page should be the same as the Standalone App Detail page in Marketplace portal +// Make sure to always update the UI in both places if needed import * as React from 'react' import AppContent from './client/app-content' import { Aside as AppAside } from './client/aside' @@ -5,8 +7,6 @@ import AppHeader from './common/ui-app-header' import { useParams } from 'react-router' import { AppDetailData } from '@/reducers/developer' import { Grid, Loader, GridItem, Section } from '@reapit/elements' -import { BackToAppsSection } from '../app-detail/app-sections' -import useReactResponsive from '@/components/hooks/use-react-responsive' import { Dispatch } from 'redux' import { showNotificationMessage } from '@/actions/notification-message' import { useDispatch } from 'react-redux' @@ -41,7 +41,6 @@ export const loadAppDetailPreviewDataFromLocalStorage = ( const AppDetailPreview: React.FC = () => { const dispatch = useDispatch() - const { isMobile } = useReactResponsive() const [appDetailPreviewData, setAppDetailPreviewData] = React.useState(null) const { appId } = useParams() @@ -60,7 +59,6 @@ const AppDetailPreview: React.FC = () => {
- {!isMobile && {}} />}
diff --git a/packages/marketplace/src/components/pages/app-detail/client/app-detail.tsx b/packages/marketplace/src/components/pages/app-detail/client/app-detail.tsx index be52a034fc..71b45a35b5 100644 --- a/packages/marketplace/src/components/pages/app-detail/client/app-detail.tsx +++ b/packages/marketplace/src/components/pages/app-detail/client/app-detail.tsx @@ -1,3 +1,5 @@ +// The UI of this page should be the same as the App Detail Preview page in Developer portal +// Make sure to always update the UI in both places if needed import * as React from 'react' import { History } from 'history' import { useHistory } from 'react-router' From d0d406d97cfc882aa1491c7d72b604cc973770f2 Mon Sep 17 00:00:00 2001 From: Vu Nguyen Date: Thu, 16 Jul 2020 11:55:26 +0700 Subject: [PATCH 4/6] feat: #1917 Fix issues based on reviews --- .../app-detail-preview.test.tsx.snap | 30 ++++---- .../app-detail-preview/app-detail-preview.tsx | 6 +- .../app-button-group.test.tsx.snap | 26 +++---- .../contact-developer-modal.test.tsx.snap | 20 +++--- .../client/app-detail-button-group.tsx | 52 +++++++------- .../client/contact-developer-modal.tsx | 12 ++-- .../app-authentication-detail.test.tsx.snap | 44 ------------ .../app-authentication-detail.test.tsx | 63 ----------------- .../common/app-authentication-detail.tsx | 68 ------------------- .../__tests__/__snapshots__/router.tsx.snap | 2 +- .../app-button-group.test.tsx.snap | 26 +++---- .../contact-developer-modal.test.tsx.snap | 20 +++--- .../client/app-detail-button-group.tsx | 52 +++++++------- .../client/contact-developer-modal.tsx | 12 ++-- 14 files changed, 135 insertions(+), 298 deletions(-) delete mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/__snapshots__/app-authentication-detail.test.tsx.snap delete mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/app-authentication-detail.test.tsx delete mode 100644 packages/developer-portal/src/components/pages/app-detail-preview/common/app-authentication-detail.tsx diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/__tests__/__snapshots__/app-detail-preview.test.tsx.snap b/packages/developer-portal/src/components/pages/app-detail-preview/__tests__/__snapshots__/app-detail-preview.test.tsx.snap index 42424de27a..6148fc9977 100644 --- a/packages/developer-portal/src/components/pages/app-detail-preview/__tests__/__snapshots__/app-detail-preview.test.tsx.snap +++ b/packages/developer-portal/src/components/pages/app-detail-preview/__tests__/__snapshots__/app-detail-preview.test.tsx.snap @@ -18,7 +18,7 @@ exports[`AppDetailPreview should match a snapshot 1`] = ` Array [ Object { "key": "clientAppDetailPreviewRoute", - "pathname": "/developer/apps/:appid", + "pathname": "/apps/:appid", }, ] } @@ -34,7 +34,7 @@ exports[`AppDetailPreview should match a snapshot 1`] = ` Object { "hash": "", "key": "clientAppDetailPreviewRoute", - "pathname": "/developer/apps/:appid", + "pathname": "/apps/:appid", "search": "", }, ], @@ -47,7 +47,7 @@ exports[`AppDetailPreview should match a snapshot 1`] = ` "location": Object { "hash": "", "key": "clientAppDetailPreviewRoute", - "pathname": "/developer/apps/:appid", + "pathname": "/apps/:appid", "search": "", }, "push": [Function], @@ -63,17 +63,23 @@ exports[`AppDetailPreview should match a snapshot 1`] = ` className="" data-test="client-app-detail-container" > - +
-
-
-
-
+ +
+ There is no preview app +
+
diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/app-detail-preview.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/app-detail-preview.tsx index c2fcabfc4e..872d0f07e4 100644 --- a/packages/developer-portal/src/components/pages/app-detail-preview/app-detail-preview.tsx +++ b/packages/developer-portal/src/components/pages/app-detail-preview/app-detail-preview.tsx @@ -6,7 +6,7 @@ import { Aside as AppAside } from './client/aside' import AppHeader from './common/ui-app-header' import { useParams } from 'react-router' import { AppDetailData } from '@/reducers/developer' -import { Grid, Loader, GridItem, Section } from '@reapit/elements' +import { Grid, GridItem, Section, Alert } from '@reapit/elements' import { Dispatch } from 'redux' import { showNotificationMessage } from '@/actions/notification-message' import { useDispatch } from 'react-redux' @@ -49,7 +49,9 @@ const AppDetailPreview: React.FC = () => { return ( {!appDetailPreviewData ? ( - + + + ) : ( <> diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-button-group.test.tsx.snap b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-button-group.test.tsx.snap index 48dc51fdc2..42b648a34c 100644 --- a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-button-group.test.tsx.snap +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/app-button-group.test.tsx.snap @@ -1,21 +1,15 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`ClientAppDetailButtonGroup should match snapshot when both buttons are hidden 1`] = ` - - - -`; +exports[`ClientAppDetailButtonGroup should match snapshot when both buttons are hidden 1`] = `""`; exports[`ClientAppDetailButtonGroup should match snapshot when both buttons are showing 1`] = ` - - - Uninstall App - - + + Uninstall App + `; diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/contact-developer-modal.test.tsx.snap b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/contact-developer-modal.test.tsx.snap index 35d000d8cd..51960255db 100644 --- a/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/contact-developer-modal.test.tsx.snap +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/__tests__/__snapshots__/contact-developer-modal.test.tsx.snap @@ -11,9 +11,9 @@ exports[`ContactDeveloperSection should match snapshot with gutter 1`] = ` > NEED HELP? - } + maskAnimation="fade" + onClose={[Function]} title="Contact Details" visible={false} > @@ -91,7 +93,7 @@ exports[`ContactDeveloperSection should match snapshot with gutter 1`] = ` - + `; @@ -106,9 +108,9 @@ exports[`ContactDeveloperSection should match snapshot without gutter 1`] = ` > NEED HELP? - } + maskAnimation="fade" + onClose={[Function]} title="Contact Details" visible={false} > @@ -186,6 +190,6 @@ exports[`ContactDeveloperSection should match snapshot without gutter 1`] = ` - + `; diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/app-detail-button-group.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/app-detail-button-group.tsx index 40944d0266..2cb9aae24a 100644 --- a/packages/developer-portal/src/components/pages/app-detail-preview/client/app-detail-button-group.tsx +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/app-detail-button-group.tsx @@ -14,31 +14,33 @@ export const AppDetailButtonGroup: React.FC = ({ onUninstallConfirmationModal, isInstallBtnHidden, }) => { + if (installedOn) { + return ( + + ) + } + + if (isInstallBtnHidden) { + return null + } + return ( - <> - {installedOn ? ( - - ) : ( - isInstallBtnHidden || ( - - ) - )} - + ) } diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/client/contact-developer-modal.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/client/contact-developer-modal.tsx index 263effa35b..06b3f8dfe2 100644 --- a/packages/developer-portal/src/components/pages/app-detail-preview/client/contact-developer-modal.tsx +++ b/packages/developer-portal/src/components/pages/app-detail-preview/client/contact-developer-modal.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Button, Grid, GridItem, SubTitleH6, Modal, Content } from '@reapit/elements' +import { Button, Grid, GridItem, SubTitleH6, Content, ModalV2 } from '@reapit/elements' import linkStyles from '@/styles/elements/link.scss?mod' export type ContactDeveloperSectionType = { @@ -31,11 +31,13 @@ export const ContactDeveloperSection = ({ NEED HELP? - - + ) } diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/__snapshots__/app-authentication-detail.test.tsx.snap b/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/__snapshots__/app-authentication-detail.test.tsx.snap deleted file mode 100644 index cb2c6409a3..0000000000 --- a/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/__snapshots__/app-authentication-detail.test.tsx.snap +++ /dev/null @@ -1,44 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`AppAuthenticationDetail should match a snapshot 1`] = ` - - - -
- -
- Authentication: -
-
- - Show Secret - -
-
-
-
-`; diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/app-authentication-detail.test.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/app-authentication-detail.test.tsx deleted file mode 100644 index 98d2688509..0000000000 --- a/packages/developer-portal/src/components/pages/app-detail-preview/common/__tests__/app-authentication-detail.test.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import * as React from 'react' -import { mount } from 'enzyme' -import * as ReactRedux from 'react-redux' -import configureStore from 'redux-mock-store' -import appState from '@/reducers/__stubs__/app-state' -import { appDetailDataStub } from '@/sagas/__stubs__/app-detail' -import { - AppAuthenticationDetail, - AppAuthenticationDetailProps, - handleCopyCode, - handleShowAuthCode, - handleMouseLeave, -} from '../app-authentication-detail' -import { requestAuthenticationCode } from '@/actions/app-detail' - -const props: AppAuthenticationDetailProps = { - appId: appDetailDataStub.data.id || '', -} - -describe('AppAuthenticationDetail', () => { - let store - let spyDispatch - const mockSetTooltipMessage = jest.fn() - - beforeEach(() => { - /* mocking store */ - const mockStore = configureStore() - store = mockStore(appState) - spyDispatch = jest.spyOn(ReactRedux, 'useDispatch').mockImplementation(() => store.dispatch) - }) - it('should match a snapshot', () => { - expect( - mount( - - - , - ), - ).toMatchSnapshot() - }) - describe('handleCopyCode', () => { - it('should copy the code to clipboard', () => { - const fn = handleCopyCode(mockSetTooltipMessage) - fn() - expect(mockSetTooltipMessage).toHaveBeenCalledWith('Copied') - }) - }) - describe('handleShowAuthCode', () => { - it('should run correctly', () => { - const mockedEvent = { preventDefault: jest.fn() } - const fn = handleShowAuthCode(props.appId, spyDispatch) - fn(mockedEvent) - expect(mockedEvent.preventDefault).toBeCalled() - expect(spyDispatch).toBeCalledWith(requestAuthenticationCode(props.appId)) - }) - }) - describe('handleMouseLeave', () => { - it('should run correctly', () => { - const fn = handleMouseLeave(mockSetTooltipMessage) - fn() - expect(mockSetTooltipMessage).toBeCalledWith('Copy') - }) - }) -}) diff --git a/packages/developer-portal/src/components/pages/app-detail-preview/common/app-authentication-detail.tsx b/packages/developer-portal/src/components/pages/app-detail-preview/common/app-authentication-detail.tsx deleted file mode 100644 index e670274857..0000000000 --- a/packages/developer-portal/src/components/pages/app-detail-preview/common/app-authentication-detail.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import * as React from 'react' -import { Dispatch } from 'redux' -import { useSelector, useDispatch } from 'react-redux' -import { requestAuthenticationCode } from '@/actions/app-detail' -import styles from '@/styles/blocks/app-authentication-detail.scss?mod' -import { Loader, Content, H5 } from '@reapit/elements' -import { FaCopy } from 'react-icons/fa' -import { CopyToClipboard } from 'react-copy-to-clipboard' -import { selectAppAuthenticationCode, selectAppAuthenticationLoading } from '@/selector/app-detail' - -export type AppAuthenticationDetailProps = { - appId: string - withCustomHeader?: boolean -} - -export const handleCopyCode = (setTooltipMessage: React.Dispatch>) => { - return () => { - setTooltipMessage('Copied') - } -} - -export const handleShowAuthCode = (appId: string, dispatch: Dispatch) => { - return e => { - e.preventDefault() - dispatch(requestAuthenticationCode(appId)) - } -} - -export const handleMouseLeave = (setTooltipMessage: React.Dispatch>) => { - return () => { - setTooltipMessage('Copy') - } -} - -export const AppAuthenticationDetail: React.FunctionComponent = ({ - appId, - withCustomHeader, -}) => { - const dispatch = useDispatch() - const [tooltipMessage, setTooltipMessage] = React.useState('Copy') - const loading = useSelector(selectAppAuthenticationLoading) - const code = useSelector(selectAppAuthenticationCode) - - return ( - <> - - {!withCustomHeader &&
Authentication:
} - - Show Secret - -
- {loading && } - {!loading && code && ( -
-

{code}

- -
- - {tooltipMessage} -
-
-
- )} - - ) -} - -export default AppAuthenticationDetail diff --git a/packages/developer-portal/src/core/__tests__/__snapshots__/router.tsx.snap b/packages/developer-portal/src/core/__tests__/__snapshots__/router.tsx.snap index d31ea257e5..94ee7db11c 100644 --- a/packages/developer-portal/src/core/__tests__/__snapshots__/router.tsx.snap +++ b/packages/developer-portal/src/core/__tests__/__snapshots__/router.tsx.snap @@ -271,7 +271,7 @@ exports[`Router should match a snapshot 1`] = ` } } exact={true} - path="/developer/apps/:appId/preview" + path="/apps/:appId/preview" />