From fec97a7727028eb96ff596282fe38d13a7d83c74 Mon Sep 17 00:00:00 2001 From: undefined Date: Mon, 18 May 2020 17:31:57 +0700 Subject: [PATCH] fix: #1225 ui for mobile client side app --- .../__snapshots__/app-content.test.tsx.snap | 211 ++++++++++++++++ .../client-app-detail.test.tsx.snap | 233 ++++++++++++------ .../__test__/app-content.test.tsx | 62 +++++ .../__test__/app-header.text.tsx | 17 ++ .../__test__/client-app-detail.test.tsx | 20 +- .../pages/client-app-detail/app-content.tsx | 118 +++++++++ .../pages/client-app-detail/app-header.tsx | 57 +++++ .../client-app-detail/client-app-detail.tsx | 80 ++++-- ...t-app-uninstall-confirmation.test.tsx.snap | 2 +- .../client-app-uninstall-confirmation.tsx | 6 +- .../src/reducers/client/app-detail.ts | 6 +- .../src/sagas/apps/__test__/apps.test.ts | 27 +- packages/marketplace/src/sagas/apps/apps.ts | 8 +- .../src/styles/pages/client-app-detail.scss | 69 ++++++ 14 files changed, 803 insertions(+), 113 deletions(-) create mode 100644 packages/marketplace/src/components/pages/client-app-detail/__test__/__snapshots__/app-content.test.tsx.snap create mode 100644 packages/marketplace/src/components/pages/client-app-detail/__test__/app-content.test.tsx create mode 100644 packages/marketplace/src/components/pages/client-app-detail/__test__/app-header.text.tsx create mode 100644 packages/marketplace/src/components/pages/client-app-detail/app-content.tsx create mode 100644 packages/marketplace/src/components/pages/client-app-detail/app-header.tsx create mode 100644 packages/marketplace/src/styles/pages/client-app-detail.scss diff --git a/packages/marketplace/src/components/pages/client-app-detail/__test__/__snapshots__/app-content.test.tsx.snap b/packages/marketplace/src/components/pages/client-app-detail/__test__/__snapshots__/app-content.test.tsx.snap new file mode 100644 index 0000000000..8ccf4af89d --- /dev/null +++ b/packages/marketplace/src/components/pages/client-app-detail/__test__/__snapshots__/app-content.test.tsx.snap @@ -0,0 +1,211 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AppContent AppContent - should match snapshot 1`] = ` +
+
+
+ + Description + + +
+
+ + Permission Required + +
    +
+
+ + Destop Integration + + + Identity Check + + + Property Marketing Information + + + Vendor Marketing Report + + + Property Details Generation + + + Applicant Export + + + Property + + + Applicant + +
+ + TBC + + +

+ Lorem ipsum, dolor sit amet consectetur adipisicing elit. Magnam aliquid culpa saepe asperiores debitis illo dolorum dolore incidunt officiis praesentium, nemo similique veritatis exercitationem perferendis non mollitia animi laboriosam perspiciatis. +

+
+ + + NEED HELP? + + +
+`; + +exports[`AppContent RenderWithHeader - should match snapshot 1`] = ` +
+ + test + + some text +
+`; + +exports[`AppContent Tag - should match snapshot 1`] = ` +
+ Test +
+`; + +exports[`AppContent renderCategory - should match snapshot 1`] = `
`; + +exports[`AppContent renderDescripion - should match snapshot 1`] = ` +
+
+ + Description + + +
+
+`; + +exports[`AppContent renderDesktopIntegrationTypes - should match snapshot 1`] = ` +
+
+ + Destop Integration + + + Identity Check + + + Property Marketing Information + + + Vendor Marketing Report + + + Property Details Generation + + + Applicant Export + + + Property + + + Applicant + +
+
+`; + +exports[`AppContent renderExtraMedia - should match snapshot 1`] = ` +
+
+ +
+
+ +
+
+ +
+
+`; + +exports[`AppContent renderPermissions - should match snapshot 1`] = ` +
+
+ + Permission Required + +
    +
  • + Read data about developers +
  • +
  • + Write data about developers +
  • +
+
+
+`; diff --git a/packages/marketplace/src/components/pages/client-app-detail/__test__/__snapshots__/client-app-detail.test.tsx.snap b/packages/marketplace/src/components/pages/client-app-detail/__test__/__snapshots__/client-app-detail.test.tsx.snap index 5cb1726154..ce3f472e52 100644 --- a/packages/marketplace/src/components/pages/client-app-detail/__test__/__snapshots__/client-app-detail.test.tsx.snap +++ b/packages/marketplace/src/components/pages/client-app-detail/__test__/__snapshots__/client-app-detail.test.tsx.snap @@ -2,15 +2,17 @@ exports[`ClientAppDetail renderAppHeaderButtonGroup should match snapshot 1`] = `
-
-
+ - - - Installed - -
+ Uninstall App +
`; @@ -71,7 +73,9 @@ exports[`ClientAppDetail should match a snapshot 1`] = ` } > -
+
} > - +
- -
+
+
+
- + +

+ +
+ + + + + + + + Verified by Reapit +

-
-
- -
- -

- -

-
+ +
-
+ +
- -
- -
+
+
+
+ +
+ Description +
-
- - -
- - -
-
- -
- -
+
+
-
+
+ +
+ Permission Required +
+
+
    +
+ +
+ +
+ Developer +
+
+ TBC +
+
+ +
+ +
+ About Developer +
+
+

+ Lorem ipsum, dolor sit amet consectetur adipisicing elit. Magnam aliquid culpa saepe asperiores debitis illo dolorum dolore incidunt officiis praesentium, nemo similique veritatis exercitationem perferendis non mollitia animi laboriosam perspiciatis. +

+
+
+ +
+ +
+ Contact Developer +
+
+ + + +
+
+
diff --git a/packages/marketplace/src/components/pages/client-app-detail/__test__/app-content.test.tsx b/packages/marketplace/src/components/pages/client-app-detail/__test__/app-content.test.tsx new file mode 100644 index 0000000000..714d76e7c6 --- /dev/null +++ b/packages/marketplace/src/components/pages/client-app-detail/__test__/app-content.test.tsx @@ -0,0 +1,62 @@ +import React from 'react' +import AppContent, { + renderCategory, + renderDescripion, + renderDesktopIntegrationTypes, + renderExtraMedia, + renderPermissions, + RenderWithHeader, + Tag, +} from '../app-content' + +import { appDetailDataStub } from '@/sagas/__stubs__/app-detail' +import { integrationTypesStub } from '@/sagas/__stubs__/integration-types' + +import { shallow } from 'enzyme' +import { DesktopIntegrationTypeModel } from '@reapit/foundations-ts-definitions' +import { AppDetailDataNotNull } from '@/reducers/client/app-detail' + +describe('AppContent', () => { + test('renderCategory - should match snapshot', () => { + const result = renderCategory(appDetailDataStub.data.category) + const wrapper = shallow(
{result}
) + expect(wrapper).toMatchSnapshot() + }) + test('renderDescripion - should match snapshot', () => { + const result = renderDescripion(appDetailDataStub.data.description as string) + const wrapper = shallow(
{result}
) + expect(wrapper).toMatchSnapshot() + }) + test('renderDesktopIntegrationTypes - should match snapshot', () => { + const result = renderDesktopIntegrationTypes(integrationTypesStub.data as DesktopIntegrationTypeModel[]) + const wrapper = shallow(
{result}
) + expect(wrapper).toMatchSnapshot() + }) + test('renderExtraMedia - should match snapshot', () => { + const result = renderExtraMedia(appDetailDataStub.data.media) + const wrapper = shallow(
{result}
) + expect(wrapper).toMatchSnapshot() + }) + test('renderPermissions - should match snapshot', () => { + const result = renderPermissions(appDetailDataStub.data.scopes) + const wrapper = shallow(
{result}
) + expect(wrapper).toMatchSnapshot() + }) + test('RenderWithHeader - should match snapshot', () => { + const wrapper = shallow(some text) + expect(wrapper).toMatchSnapshot() + }) + test('Tag - should match snapshot', () => { + const wrapper = shallow(Test) + expect(wrapper).toMatchSnapshot() + }) + test('AppContent - should match snapshot', () => { + const wrapper = shallow( + , + ) + expect(wrapper).toMatchSnapshot() + }) +}) diff --git a/packages/marketplace/src/components/pages/client-app-detail/__test__/app-header.text.tsx b/packages/marketplace/src/components/pages/client-app-detail/__test__/app-header.text.tsx new file mode 100644 index 0000000000..5f5e5b64e2 --- /dev/null +++ b/packages/marketplace/src/components/pages/client-app-detail/__test__/app-header.text.tsx @@ -0,0 +1,17 @@ +import React from 'react' +import AppHeader, { VerifiedByReapit } from '../app-header' + +import { appDetailDataStub } from '@/sagas/__stubs__/app-detail' +import { shallow } from 'enzyme' +import { AppDetailModel } from '@reapit/foundations-ts-definitions' + +describe('AppHeader', () => { + test('VerifiedByReapit - should match snapshot', () => { + const wrapper = shallow() + expect(wrapper).toMatchSnapshot() + }) + test('AppHeader - should match snapshot', () => { + const wrapper = shallow() + expect(wrapper).toMatchSnapshot() + }) +}) diff --git a/packages/marketplace/src/components/pages/client-app-detail/__test__/client-app-detail.test.tsx b/packages/marketplace/src/components/pages/client-app-detail/__test__/client-app-detail.test.tsx index 77027721bc..92d9aca2cf 100644 --- a/packages/marketplace/src/components/pages/client-app-detail/__test__/client-app-detail.test.tsx +++ b/packages/marketplace/src/components/pages/client-app-detail/__test__/client-app-detail.test.tsx @@ -36,30 +36,28 @@ describe('ClientAppDetail', () => { const mockAppId = 'test' const mockInstalledOn = '2020-2-20' it('should match snapshot', () => { - const wrapper = shallow(
{renderAppHeaderButtonGroup(mockAppId, mockInstalledOn, jest.fn())}
) + const wrapper = shallow(
{renderAppHeaderButtonGroup(mockAppId, mockInstalledOn, jest.fn(), jest.fn())}
) expect(wrapper).toMatchSnapshot() }) it('should render header button group when appId is existed', () => { - const testRenderer = TestRenderer.create(renderAppHeaderButtonGroup(mockAppId, mockInstalledOn, jest.fn())) + const testRenderer = TestRenderer.create( + renderAppHeaderButtonGroup(mockAppId, mockInstalledOn, jest.fn(), jest.fn()), + ) 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())) + const testRenderer = TestRenderer.create(renderAppHeaderButtonGroup(mockAppId, '', jest.fn(), jest.fn())) const testInstance = testRenderer.root expect(testInstance.findByType(Button).props.children).toBe('Install App') }) - it('should render installed label if installedOn is existed', () => { - const testRenderer = TestRenderer.create(renderAppHeaderButtonGroup(mockAppId, mockInstalledOn, jest.fn())) + it('should render uninstall app button if installedOn is existed', () => { + const testRenderer = TestRenderer.create(renderAppHeaderButtonGroup(mockAppId, 'exist', jest.fn(), jest.fn())) const testInstance = testRenderer.root - expect( - testInstance.findByProps({ - id: 'installed-label-container', - }).children.length, - ).toBeGreaterThan(0) + 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())) + const testRenderer = TestRenderer.create(renderAppHeaderButtonGroup('', mockInstalledOn, jest.fn(), jest.fn())) const testInstance = testRenderer.getInstance expect(testInstance).toHaveLength(0) }) diff --git a/packages/marketplace/src/components/pages/client-app-detail/app-content.tsx b/packages/marketplace/src/components/pages/client-app-detail/app-content.tsx new file mode 100644 index 0000000000..057a701bc5 --- /dev/null +++ b/packages/marketplace/src/components/pages/client-app-detail/app-content.tsx @@ -0,0 +1,118 @@ +import * as React from 'react' +import { H5, HTMLRender, Button } from '@reapit/elements' +import { CategoryModel, DesktopIntegrationTypeModel } from '@reapit/foundations-ts-definitions' +import styles from '@/styles/blocks/app-detail.scss?mod' +import { ScopeModel, MediaModel } from '@reapit/foundations-ts-definitions' +import '@/styles/vendor/slick.scss' +import clientAppDetailStyles from '@/styles/pages/client-app-detail.scss?mod' + +import { AppDetailDataNotNull } from '@/reducers/client/app-detail' + +export type AppContentProps = { + appDetailData: AppDetailDataNotNull + desktopIntegrationTypes: DesktopIntegrationTypeModel[] +} + +export const Tag = ({ children }) =>
{children}
+ +export const renderDescripion = (description: string) => ( +
+
Description
+ +
+) + +export const renderPermissions = (scopes: ScopeModel[] = []) => ( +
+
Permission Required
+
    + {scopes.map(scope => ( +
  • {scope?.description}
  • + ))} +
+
+) + +export const renderCategory = (category: CategoryModel | undefined) => { + if (!category) { + return undefined + } + + return ( +
+
Category
+ {category.name} +
+ ) +} + +export const renderDesktopIntegrationTypes = (desktopIntegrationTypes: DesktopIntegrationTypeModel[]) => { + if (desktopIntegrationTypes.length === 0) { + return undefined + } + + return ( +
+
Destop Integration
+ {desktopIntegrationTypes.map(({ name }) => ( + {name} + ))} +
+ ) +} + +export const RenderWithHeader: React.FC<{ header: string }> = ({ header, children }) => ( +
+
{header}
+ {children} +
+) + +export const renderExtraMedia = (media: MediaModel[] = []) => + media.map(({ uri }) => ( +
+ +
+ )) + +const AppContent: React.FC = ({ appDetailData, desktopIntegrationTypes = [] }) => { + const { summary = '', description = '', category, scopes = [], media = [] } = appDetailData + /** + * 0 = icon + * 1 = featured media + * 2 -> nth: extra media + */ + const extraMedia = media.filter((_, index) => index > 1) + console.log({ + extraMedia, + media, + }) + + return ( +
+
{summary}
+ {renderDescripion(description)} + {summary &&

{summary}

} + {media[0] && } + {renderPermissions(scopes)} + {renderExtraMedia(extraMedia)} + {renderCategory(category)} + {renderDesktopIntegrationTypes(desktopIntegrationTypes)} + TBC + +

+ Lorem ipsum, dolor sit amet consectetur adipisicing elit. Magnam aliquid culpa saepe asperiores debitis illo + dolorum dolore incidunt officiis praesentium, nemo similique veritatis exercitationem perferendis non mollitia + animi laboriosam perspiciatis. +

+
+ + + +
+ ) +} + +export default AppContent diff --git a/packages/marketplace/src/components/pages/client-app-detail/app-header.tsx b/packages/marketplace/src/components/pages/client-app-detail/app-header.tsx new file mode 100644 index 0000000000..db76acf5aa --- /dev/null +++ b/packages/marketplace/src/components/pages/client-app-detail/app-header.tsx @@ -0,0 +1,57 @@ +import * as React from 'react' +import { H2 } from '@reapit/elements' +import { FaCheck } from 'react-icons/fa' +import developerAppDetailstyles from '@/styles/pages/developer-app-detail.scss?mod' +import { AppDetailModel } from '@reapit/foundations-ts-definitions' +import clientAppDetailStyles from '@/styles/pages/client-app-detail.scss?mod' + +export type AppHeaderProps = { + appDetailData: AppDetailModel & { + apiKey?: string | undefined + } + buttonGroup?: React.ReactNode +} + +const appIconContainerClassName = [ + developerAppDetailstyles.appIconContainer, + clientAppDetailStyles.appIconContainer, +].join(' ') + +const appIconClassName = ['images', clientAppDetailStyles.appIcon].join(' ') + +const placeHolderUrl = 'https://bulma.io/images/placeholders/48x48.png' + +export const VerifiedByReapit: React.FC<{ appName: string }> = ({ appName }) => ( +
+

{appName}

+
+ Verified by Reapit +
+
+) + +const AppHeader: React.FC = ({ appDetailData, buttonGroup }) => { + const { media, name = '' } = appDetailData + const appIcon = media?.filter(({ type }) => type === 'icon')[0] + + return ( +
+
+
+ {name} +
+
+ + {buttonGroup} +
+
+ + +
+ ) +} + +export default AppHeader diff --git a/packages/marketplace/src/components/pages/client-app-detail/client-app-detail.tsx b/packages/marketplace/src/components/pages/client-app-detail/client-app-detail.tsx index 68f8d44a56..d9a9cf4d6f 100644 --- a/packages/marketplace/src/components/pages/client-app-detail/client-app-detail.tsx +++ b/packages/marketplace/src/components/pages/client-app-detail/client-app-detail.tsx @@ -1,17 +1,29 @@ import * as React from 'react' -import { FaCheck } from 'react-icons/fa' +import { + handleUninstallAppButtonClick, + handleCloseUninstallConfirmationModal, +} from '../client-app-detail-manage/client-app-detail-manage' +import ClientAppUninstallConfirmation from '@/components/ui/client-app-detail/client-app-uninstall-confirmation' +import { DesktopIntegrationTypeModel } from '@/actions/app-integration-types' +import { AppDetailDataNotNull } from '@/reducers/client/app-detail' +import { selectIntegrationTypes } from '@/selector/integration-types' import { useSelector } from 'react-redux' import { selectAppDetailData, selectAppDetailLoading } from '@/selector/client-app-detail' -import { selectLoginType } from '@/selector/auth' -import AppHeader from '@/components/ui/app-detail/app-header' -import AppContent from '@/components/ui/app-detail/app-content' +import AppHeader from './app-header' +import AppContent from './app-content' import { Loader, Button } from '@reapit/elements' -import styles from '@/styles/pages/developer-app-detail.scss?mod' +import developerAppDetailStyles from '@/styles/pages/developer-app-detail.scss?mod' +import clientAppDetailStyles from '@/styles/pages/client-app-detail.scss?mod' import ClientAppInstallConfirmation from '@/components/ui/client-app-detail/client-app-install-confirmation' export type ClientAppDetailProps = {} +const appDetailContainerClassNames = [ + clientAppDetailStyles.appDetailContainer, + developerAppDetailStyles.appDetailContainer, +].join(' ') + export const handleCloseInstallConfirmationModal = (setIsVisibleInstallConfirmation: (isVisible: boolean) => void) => { return () => { setIsVisibleInstallConfirmation(false) @@ -24,16 +36,25 @@ export const handleInstallAppButtonClick = (setIsVisibleInstallConfirmation: (is } } -export const renderAppHeaderButtonGroup = (id: string, installedOn: string, onInstallConfirmationModal: () => void) => { +export const renderAppHeaderButtonGroup = ( + id: string, + installedOn: string, + onUninstallConfirmationModal: () => void, + onInstallConfirmationModal: () => void, +) => { return ( <> {id && ( -
+
{installedOn ? ( -
- - Installed -
+ ) : (