Skip to content

Commit 91add1d

Browse files
Zoom in/zoom out QR Code
1 parent da37670 commit 91add1d

File tree

4 files changed

+156
-0
lines changed

4 files changed

+156
-0
lines changed

apps/frontend/src/components/App/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import RouteTransition from '../ui/RouteTransition/RouteTransition';
1212
import { useSelector } from 'react-redux';
1313
import { selectAppMode, selectIsAuthenticated, selectIsDarkMode } from '../../store/rootSelectors';
1414
import SQLTerminal from '../modals/SQLTerminal/SQLTerminal';
15+
import QRCodeLarge from '../modals/QRCodeLarge/QRCodeLarge';
1516

1617
export const App = () => {
1718
const currentScreenSize = useBreakpoint();
@@ -37,6 +38,7 @@ export const App = () => {
3738
<ToastMessage />
3839
<NodeInfo />
3940
<ConnectWallet />
41+
<QRCodeLarge />
4042
<LoginComponent />
4143
<LogoutComponent />
4244
<SetPasswordComponent />
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
@import '../../../styles/constants.scss';
2+
3+
.qr-container-large {
4+
position: relative;
5+
width: 440px;
6+
padding-bottom: 1rem;
7+
cursor: zoom-out;
8+
& .qr-cln-logo {
9+
position: absolute;
10+
top: 40%;
11+
left: 44%;
12+
padding: 0;
13+
border-radius: 50%;
14+
width: 3.5rem;
15+
height: 3.5rem;
16+
border: 3px solid $white;
17+
}
18+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import React, { act } from 'react';
2+
import { fireEvent, screen, waitFor } from '@testing-library/react';
3+
import { mockBKPRStoreData, mockCLNStoreData, mockRootStoreData, mockShowModals } from '../../../utilities/test-utilities/mockData';
4+
import { renderWithProviders } from '../../../utilities/test-utilities/mockStore';
5+
import ConnectWallet from '../ConnectWallet/ConnectWallet';
6+
import { APP_ANIMATION_DURATION } from '../../../utilities/constants';
7+
8+
describe('QRCodeLarge Component', () => {
9+
const customMockStore = {
10+
root: {
11+
...mockRootStoreData,
12+
showModals: {
13+
...mockShowModals,
14+
connectWalletModal: true,
15+
}
16+
},
17+
cln: mockCLNStoreData,
18+
bkpr: mockBKPRStoreData
19+
};
20+
21+
it('renders without crashing', async () => {
22+
await renderWithProviders(<ConnectWallet />, { preloadedState: customMockStore });
23+
const QRContainer = screen.getByTestId('qr-container');
24+
expect(QRContainer).toBeInTheDocument();
25+
const QRCanvas = QRContainer.querySelector('canvas');
26+
expect(QRCanvas).toBeInTheDocument();
27+
await act(async () => {
28+
if (QRCanvas) {
29+
fireEvent.click(QRCanvas);
30+
} else {
31+
fail('QR Canvas not found');
32+
}
33+
});
34+
await act(async () => jest.advanceTimersByTime(APP_ANIMATION_DURATION * 2 * 1000));
35+
expect(screen.getByTestId('qr-code-large')).toBeInTheDocument();
36+
const qrContainerLarge = screen.getByTestId('qr-container-large');
37+
const qrCanvasLarge = qrContainerLarge.querySelector('canvas');
38+
expect(qrCanvasLarge).toHaveAttribute('width', '440');
39+
});
40+
41+
it('uses the correct connection URL for the QR code', async () => {
42+
await renderWithProviders(<ConnectWallet />, { preloadedState: customMockStore });
43+
const QRContainer = screen.getByTestId('qr-container');
44+
expect(QRContainer).toBeInTheDocument();
45+
const QRCanvas = QRContainer.querySelector('canvas');
46+
expect(QRCanvas).toBeInTheDocument();
47+
await act(async () => {
48+
if (QRCanvas) {
49+
fireEvent.click(QRCanvas);
50+
} else {
51+
fail('QR Canvas not found');
52+
}
53+
});
54+
await act(async () => jest.advanceTimersByTime(APP_ANIMATION_DURATION * 1000));
55+
const qrContainerLarge = screen.getByTestId('qr-container-large');
56+
const url = qrContainerLarge.getAttribute('data-key');
57+
expect(url).toContain(mockRootStoreData.connectionUrl);
58+
});
59+
60+
it('dispatches correct action when close button is clicked', async () => {
61+
const { getActions } = await renderWithProviders(<ConnectWallet />, { preloadedState: customMockStore });
62+
const QRContainer = screen.getByTestId('qr-container');
63+
expect(QRContainer).toBeInTheDocument();
64+
const QRCanvas = QRContainer.querySelector('canvas');
65+
expect(QRCanvas).toBeInTheDocument();
66+
await act(async () => {
67+
if (QRCanvas) {
68+
fireEvent.click(QRCanvas);
69+
} else {
70+
fail('QR Canvas not found');
71+
}
72+
});
73+
await act(async () => jest.advanceTimersByTime(APP_ANIMATION_DURATION * 1000));
74+
75+
const modalCloseButton = screen.getByTestId('modal-close');
76+
fireEvent.click(modalCloseButton);
77+
78+
await waitFor(() => {
79+
expect(getActions().some(action =>
80+
action.type === 'root/setShowModals' &&
81+
action.payload.connectWalletModal === true &&
82+
action.payload.qrCodeLarge === false
83+
)).toBe(true);
84+
});
85+
});
86+
87+
});
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import './QRCodeLarge.scss';
2+
import { motion, AnimatePresence } from 'framer-motion';
3+
import { QRCodeCanvas } from 'qrcode.react';
4+
import { Modal, Row } from 'react-bootstrap';
5+
6+
import { CloseSVG } from '../../../svgs/Close';
7+
import { setShowModals } from '../../../store/rootSlice';
8+
import { useDispatch, useSelector } from 'react-redux';
9+
import { selectConnectionUrl, selectIsDarkMode, selectShowModals } from '../../../store/rootSelectors';
10+
11+
const QRCodeLarge = () => {
12+
const dispatch = useDispatch();
13+
const isDarkMode = useSelector(selectIsDarkMode);
14+
const showModals = useSelector(selectShowModals);
15+
const connectionUrl = useSelector(selectConnectionUrl);
16+
17+
const closeHandler = () => {
18+
dispatch(setShowModals({ ...showModals, connectWalletModal: true, qrCodeLarge: false }));
19+
}
20+
21+
return (
22+
<>
23+
<Modal show={showModals.qrCodeLarge} onHide={closeHandler} centered className='modal-lg' data-testid='qr-code-large'>
24+
<Modal.Header className='d-flex align-items-start justify-content-end pb-0 border-0'>
25+
<span data-testid='modal-close' className='span-close-svg' onClick={closeHandler}><CloseSVG /></span>
26+
</Modal.Header>
27+
<Modal.Body className='p-0'>
28+
<Row className='qr-container-large m-auto d-flex' data-testid='qr-container-large' data-key={connectionUrl}>
29+
<AnimatePresence>
30+
<motion.img
31+
key='cln-logo'
32+
alt='Core Lightning Logo'
33+
src={isDarkMode ? '/images/cln-logo-dark.png' : '/images/cln-logo-light.png'}
34+
onClick={closeHandler}
35+
className='qr-cln-logo'
36+
initial={{ opacity: 0 }}
37+
animate={{ opacity: 1 }}
38+
transition={{ delay: 0.05, duration: 0.01 }}
39+
/>
40+
</AnimatePresence>
41+
<QRCodeCanvas onClick={closeHandler} value={connectionUrl} size={440} includeMargin={true} bgColor={isDarkMode ? '#0C0C0F' : '#FFFFFF'} fgColor={isDarkMode ? '#FFFFFF' : '#000000'} />
42+
</Row>
43+
</Modal.Body>
44+
</Modal>
45+
</>
46+
);
47+
};
48+
49+
export default QRCodeLarge;

0 commit comments

Comments
 (0)