Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update download jwt button verify page #116

Merged
merged 24 commits into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
bea59ae
feat: update download JWT button in Verify page
ldhyen99 Oct 3, 2024
cac1741
test: add test for json block
ldhyen99 Oct 3, 2024
8d5c455
ci: fix build pipeline
ldhyen99 Oct 4, 2024
3e6b92a
ci: cp app-config.json when build
ldhyen99 Oct 4, 2024
3e43a07
refactor: check vc response from api in verify page
ldhyen99 Oct 7, 2024
5ad8a17
refactor: response of issuing VC api
namhoang1604 Oct 5, 2024
1c1d811
fix: resolve conflict
ldhyen99 Oct 22, 2024
87884ec
refactor: change download button on verify page following the design
ldhyen99 Oct 22, 2024
58d82b5
docs: update document for rendering verified credential page
ldhyen99 Oct 22, 2024
7efab2e
test: update unit test in JsonBlock and CredentialTabs
ldhyen99 Oct 22, 2024
c081d19
ci: update yarn lock
ldhyen99 Oct 22, 2024
df932e1
refactor: pass decode jwt in verify page as props for json block and …
ldhyen99 Oct 23, 2024
f733b90
refactor: change name of the props from decodeCredential to decodedE…
ldhyen99 Oct 23, 2024
477a0a5
test: update test in CredentialTabs and JsonBlock
ldhyen99 Oct 23, 2024
649d230
refactor: remove unused code
ldhyen99 Oct 23, 2024
2354504
fix: resolve conflict
ldhyen99 Oct 23, 2024
d544ad4
fix: resolve conflict
ldhyen99 Oct 24, 2024
d7be79a
refactor: remove unused code
ldhyen99 Oct 24, 2024
c8bd71b
fix: resolve conflict
ldhyen99 Oct 29, 2024
8afabe7
ci: update yarn lock to retry pipeline
ldhyen99 Oct 29, 2024
5be99b7
chore: adjust yarn lock
namhoang1604 Oct 29, 2024
e47473f
refactor: change code in download button of verify page
ldhyen99 Oct 29, 2024
c233cd0
Merge branch 'feat/update_download_jwt_button_verify_page' of https:/…
ldhyen99 Oct 29, 2024
b36c7f4
Merge branch 'next' into feat/update_download_jwt_button_verify_page
ldhyen99 Oct 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion documentation/docs/mock-apps/verify-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,9 @@ sequenceDiagram
VS-->>V: Return Verification Result
V->>V: Render Verified Credential
V->>U: Display Verification Result and Credential
```
```

## Rendering Verified Credential
The UI of this page includes these information fields: Type, Issued by and Issue date. Besides, the page also contains the tab panel for HTML template, JSON data and the download button.

With the download button, it will download the JSON data or JWT data if the credential is JWT.
24 changes: 22 additions & 2 deletions packages/mock-app/src/__tests__/CredentialTabs.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ describe('Credential tabs content', () => {
};
// Render the CredentialTabs component with the modified credential
render(<CredentialTabs credential={credential2} />);
// Expecting the text 'CredentialRender' to be present in the rendered component
expect(screen.getByText('CredentialRender')).not.toBeNull();
// Expecting the text 'Rendered' to be present in the rendered component
expect(screen.getByText('Rendered')).not.toBeNull();
});

it('should display on change value', () => {
Expand All @@ -78,4 +78,24 @@ describe('Credential tabs content', () => {
// Expecting the tab with 'selected' attribute to have accessible name 'Rendered'
expect(screen.getByRole('tab', { selected: true })).toHaveAccessibleName('Rendered');
});

it('should display download button', () => {
// Mocking the URL.createObjectURL function
const mockCreateObjectURL = jest.fn();
global.URL.createObjectURL = mockCreateObjectURL;

// Render component with the mock credential
render(<CredentialTabs credential={credential} />);

// Find the button with text 'Download', simulate a click event on it
const button = screen.getByText(/Download/i);
button.click();

// Expecting the button with text 'Download' to be present in the rendered component
expect(screen.getByText(/Download/i)).not.toBeNull();
// Expecting the URL.createObjectURL function to have been called
expect(mockCreateObjectURL).toHaveBeenCalled();
// Restore the original URL.createObjectURL to avoid side effects on other tests
global.URL.createObjectURL = mockCreateObjectURL;
});
});
20 changes: 0 additions & 20 deletions packages/mock-app/src/__tests__/JsonBlock.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { JsonBlock } from '../components/JsonBlock';

Expand Down Expand Up @@ -40,23 +39,4 @@ describe('Json block content', () => {
// Expecting the text 'VerifiableCredential' to be present in the rendered component
expect(screen.getByText(/VerifiableCredential/i)).not.toBeNull();
});

it('should download credential when click on button Download', () => {
// Mocking the URL.createObjectURL function
const mockCreateObjectURL = jest.fn();
global.URL.createObjectURL = mockCreateObjectURL;

// Render the JsonBlock component with the mock credential
render(<JsonBlock credential={credential} />);
// Find the button with text 'Download', simulate a click event on it
const button = screen.getByText(/Download/i);
button.click();

// Expecting the button with text 'Download' to be present in the rendered component
expect(screen.getByText(/Download/i)).not.toBeNull();
// Expecting the URL.createObjectURL function to have been called
expect(mockCreateObjectURL).toHaveBeenCalled();
// Restore the original URL.createObjectURL to avoid side effects on other tests
global.URL.createObjectURL = mockCreateObjectURL;
});
});
7 changes: 3 additions & 4 deletions packages/mock-app/src/components/Credential/Credential.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React from 'react';
import { Box } from '@mui/material';
import { VerifiableCredential } from '@vckit/core-types';
import { CredentialInfo } from '../CredentialInfo';
import { CredentialTabs } from '../CredentialTabs';
import { CredentialComponentProps } from '../../types/common.types';

const Credential = ({ credential }: { credential: VerifiableCredential }) => {
const Credential = ({ credential, decodedEnvelopedVC }: CredentialComponentProps) => {
return (
<Box
sx={{
ldhyen99 marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -15,9 +15,8 @@ const Credential = ({ credential }: { credential: VerifiableCredential }) => {
width: '100%',
}}
>

<CredentialInfo credential={credential} />
<CredentialTabs credential={credential} />
<CredentialTabs credential={credential} decodedEnvelopedVC={decodedEnvelopedVC} />
</Box>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const CredentialInfo = ({ credential }: { credential: VerifiableCredential }) =>
}

const types = credential?.type as string[];
const type = types.find((item) => item !== 'VerifiableCredential');
const type = types?.find((item) => item !== 'VerifiableCredential');
if (type) {
return type;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import React, { useCallback, useEffect, useState } from 'react';
import { Renderer, WebRenderingTemplate2022 } from '@vckit/renderer';
import { VerifiableCredential } from '@vckit/core-types';
import { UnsignedCredential, VerifiableCredential } from '@vckit/core-types';
import { Box, CircularProgress } from '@mui/material';
import { convertBase64ToString } from '../../utils';

/**
* CredentialRender component is used to render the credential
*/
const CredentialRender = ({ credential }: { credential: VerifiableCredential }) => {
const CredentialRender = ({ credential }: { credential: VerifiableCredential | UnsignedCredential }) => {
const [documents, setDocuments] = useState<string[]>([]);
const [isLoading, setIsLoading] = useState<boolean>(false);

Expand Down
58 changes: 47 additions & 11 deletions packages/mock-app/src/components/CredentialTabs/CredentialTabs.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,33 @@
import React, { useEffect } from 'react';
import { Box, Tab, Tabs } from '@mui/material';
import { VerifiableCredential } from '@vckit/core-types';
import { Box, Tab, Tabs, useMediaQuery, useTheme } from '@mui/material';

import CredentialRender from '../CredentialRender/CredentialRender';
import { JsonBlock } from '../JsonBlock';
import { CredentialComponentProps } from '../../types/common.types';
import { DownloadCredentialButton } from '../DownloadCredentialButton/DownloadCredentialButton';

const CredentialTabs = ({ credential }: { credential: VerifiableCredential }) => {
const CredentialTabs = ({ credential, decodedEnvelopedVC }: CredentialComponentProps) => {
const credentialTabs = [
{
label: 'Rendered',
children: <CredentialRender credential={credential} />,
children: <CredentialRender credential={decodedEnvelopedVC ?? credential} />,
},
{
label: 'JSON',
children: <JsonBlock credential={credential} />,
children: <JsonBlock credential={decodedEnvelopedVC ?? credential} />,
},
];

const [currentTabIndex, setCurrentTabIndex] = React.useState(0);
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));

useEffect(() => {
configDefaultTabs();
}, [credential]);

Check warning on line 27 in packages/mock-app/src/components/CredentialTabs/CredentialTabs.tsx

View workflow job for this annotation

GitHub Actions / test_and_build

React Hook useEffect has a missing dependency: 'configDefaultTabs'. Either include it or remove the dependency array

const configDefaultTabs = () => {
if (credential?.render?.[0]?.template) {
if (decodedEnvelopedVC?.render?.[0]?.template) {
return setCurrentTabIndex(0);
}

Expand All @@ -43,12 +47,44 @@

return (
<Box sx={{ width: '100%', bgcolor: 'background.paper', minHeight: '300px' }}>
<Tabs value={currentTabIndex} onChange={handleChange} centered>
{credentialTabs?.map((item, index) => <Tab key={index} label={item.label} />)}
</Tabs>
{/* Header Row */}
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
flexDirection: 'row',
gap: isMobile ? 1 : 2,
maxWidth: '800px',
margin: 'auto',
}}
>
{/* Tabs aligned to the left */}
<Tabs
value={currentTabIndex}
onChange={handleChange}
sx={{
flexGrow: 1,
minWidth: 0,
justifyContent: 'flex-start',
}}
variant='scrollable'
scrollButtons={isMobile ? 'auto' : false}
>
{credentialTabs.map((item, index) => (
<Tab key={index} label={item.label} />
))}
</Tabs>

{/* Download Button */}
<DownloadCredentialButton credential={credential} />
</Box>

{credentialTabs?.map((item, index) => (
<TabPanel key={index} value={currentTabIndex} index={index} children={item.children} />
{/* Tab Panels */}
{credentialTabs.map((item, index) => (
<TabPanel key={index} value={currentTabIndex} index={index}>
{item.children}
</TabPanel>
))}
</Box>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';
import CloudDownloadOutlinedIcon from '@mui/icons-material/CloudDownloadOutlined';
import { Button, useMediaQuery, useTheme } from '@mui/material';
import { UnsignedCredential, VerifiableCredential } from '@vckit/core-types';

export const DownloadCredentialButton = ({ credential }: { credential: VerifiableCredential | UnsignedCredential }) => {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
/**
* handle click on download button
*/
const handleClickDownloadVC = async () => {
const element = document.createElement('a');
const file = new Blob([JSON.stringify({ verifiableCredential: credential }, null, 2)], {
type: 'text/plain',
});
element.href = URL.createObjectURL(file);
element.download = 'vc.json';
document.body.appendChild(element); // Required for this to work in FireFox
element.click();
};

return (
<Button
variant='text'
startIcon={<CloudDownloadOutlinedIcon sx={{ marginRight: '5px' }} />}
sx={{
color: 'primary.main',
textTransform: 'none',
marginLeft: 2,
paddingRight: 0,
justifyContent: 'end',
fontSize: '16px',
'.MuiButton-startIcon': { marginRight: 0 },
}}
onClick={handleClickDownloadVC}
>
{isMobile ? '' : 'Download'}
</Button>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './DownloadCredentialButton';
23 changes: 3 additions & 20 deletions packages/mock-app/src/components/JsonBlock/JsonBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,11 @@
import React from 'react';
import { Button, Card, CardContent } from '@mui/material';
import { VerifiableCredential } from '@vckit/core-types';

const JsonBlock = ({ credential }: { credential: VerifiableCredential }) => {
/**
* handle click on download button
*/
const handleClickDownloadVC = async () => {
const element = document.createElement('a');
const file = new Blob([JSON.stringify(credential, null, 2)], {
type: 'text/plain',
});
element.href = URL.createObjectURL(file);
element.download = 'vc.json';
document.body.appendChild(element); // Required for this to work in FireFox
element.click();
};
import { Card, CardContent } from '@mui/material';
import { UnsignedCredential, VerifiableCredential } from '@vckit/core-types';

const JsonBlock = ({ credential }: { credential: VerifiableCredential | UnsignedCredential }) => {
return (
<>
<Card sx={{ width: '100%', textAlign: 'left', overflowX: 'scroll' }}>
<Button onClick={handleClickDownloadVC} variant='contained'>
Download
</Button>
<CardContent>
<pre>{JSON.stringify(credential, null, 2)}</pre>
</CardContent>
Expand Down
2 changes: 1 addition & 1 deletion packages/mock-app/src/pages/Verify.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@

useEffect(() => {
fetchEncryptedVC();
}, [search]);

Check warning on line 34 in packages/mock-app/src/pages/Verify.tsx

View workflow job for this annotation

GitHub Actions / test_and_build

React Hook useEffect has a missing dependency: 'fetchEncryptedVC'. Either include it or remove the dependency array

useEffect(() => {
if (credential) {
setCurrentScreen(PassportStatus.LOADING_PASSPORT_VERIFIED);
verifyCredential(credential);
}
}, [credential]);

Check warning on line 41 in packages/mock-app/src/pages/Verify.tsx

View workflow job for this annotation

GitHub Actions / test_and_build

React Hook useEffect has a missing dependency: 'verifyCredential'. Either include it or remove the dependency array

/**
* fetchEncryptedVC function is used to fetch the encrypted VC and decrypt it
Expand Down Expand Up @@ -162,7 +162,7 @@

return (
<BackButton>
<Credential credential={customCredential ?? credential} />
<Credential credential={credential} decodedEnvelopedVC={customCredential} />
</BackButton>
);
default:
Expand Down
6 changes: 6 additions & 0 deletions packages/mock-app/src/types/common.types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { UnsignedCredential, VerifiableCredential } from '@vckit/core-types';
import { IGenericFeatureProps } from '../components/GenericFeature';

export interface IFeature extends IGenericFeatureProps {
Expand Down Expand Up @@ -27,3 +28,8 @@ export interface IStyles {
tertiaryColor: string;
menuIconColor?: string;
}

export interface CredentialComponentProps {
credential: VerifiableCredential;
decodedEnvelopedVC?: UnsignedCredential | null;
}
Loading
Loading