Skip to content

Commit 4e55ef4

Browse files
authored
refactor: EnterE2EPasswordModal (RocketChat#36630)
1 parent dfc4391 commit 4e55ef4

6 files changed

Lines changed: 263 additions & 64 deletions

File tree

apps/meteor/client/views/e2e/EnterE2EPasswordModal.tsx

Lines changed: 0 additions & 64 deletions
This file was deleted.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/* eslint-disable @typescript-eslint/naming-convention */
2+
import { mockAppRoot } from '@rocket.chat/mock-providers';
3+
import { composeStories } from '@storybook/react';
4+
import { render, screen } from '@testing-library/react';
5+
import { axe } from 'jest-axe';
6+
7+
import EnterE2EPasswordModal from '.';
8+
import * as stories from './EnterE2EPasswordModal.stories';
9+
10+
const testCases = Object.values(composeStories(stories)).map((Story) => [Story.storyName || 'Story', Story]);
11+
12+
test.each(testCases)(`renders %s without crashing`, async (_storyname, Story) => {
13+
const { baseElement } = render(<Story />);
14+
expect(baseElement).toMatchSnapshot();
15+
});
16+
17+
test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => {
18+
const { container } = render(<Story />);
19+
20+
const results = await axe(container);
21+
expect(results).toHaveNoViolations();
22+
});
23+
24+
it('should render modal and the password input should be focused', () => {
25+
const inputPlaceholder = 'Please enter your E2E password';
26+
render(<EnterE2EPasswordModal onConfirm={() => undefined} onClose={() => undefined} onCancel={() => undefined} />, {
27+
wrapper: mockAppRoot()
28+
.withTranslations('en', 'core', {
29+
Please_enter_E2EE_password: inputPlaceholder,
30+
})
31+
.build(),
32+
});
33+
expect(screen.getByPlaceholderText(inputPlaceholder)).toHaveFocus();
34+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { mockAppRoot } from '@rocket.chat/mock-providers';
2+
import { action } from '@storybook/addon-actions';
3+
import type { Meta, StoryFn } from '@storybook/react';
4+
5+
import EnterE2EPasswordModal from '.';
6+
7+
export default {
8+
title: 'views/EnterE2EPasswordModal',
9+
component: EnterE2EPasswordModal,
10+
decorators: [mockAppRoot().withTranslations('en', 'core', {}).buildStoryDecorator()],
11+
} satisfies Meta<typeof EnterE2EPasswordModal>;
12+
13+
export const Default: StoryFn<typeof EnterE2EPasswordModal> = () => (
14+
<EnterE2EPasswordModal onConfirm={action('submit')} onClose={action('close')} onCancel={action('cancel')} />
15+
);
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { Box, PasswordInput, Field, FieldGroup, FieldRow, FieldError } from '@rocket.chat/fuselage';
2+
import { GenericModal } from '@rocket.chat/ui-client';
3+
import DOMPurify from 'dompurify';
4+
import { useEffect, useId } from 'react';
5+
import { Controller, useForm } from 'react-hook-form';
6+
import { useTranslation } from 'react-i18next';
7+
8+
type EnterE2EPasswordModalProps = {
9+
onConfirm: (password: string) => void;
10+
onClose: () => void;
11+
onCancel: () => void;
12+
};
13+
14+
const EnterE2EPasswordModal = ({ onConfirm, onClose, onCancel }: EnterE2EPasswordModalProps) => {
15+
const { t } = useTranslation();
16+
const {
17+
handleSubmit,
18+
control,
19+
setFocus,
20+
formState: { errors },
21+
} = useForm({
22+
defaultValues: {
23+
password: '',
24+
},
25+
});
26+
27+
const passwordInputId = useId();
28+
29+
useEffect(() => {
30+
setFocus('password');
31+
}, [setFocus]);
32+
33+
return (
34+
<GenericModal
35+
wrapperFunction={(props) => <Box is='form' onSubmit={handleSubmit(({ password }) => onConfirm(password))} {...props} />}
36+
variant='warning'
37+
title={t('Enter_E2E_password')}
38+
icon='warning'
39+
cancelText={t('Do_It_Later')}
40+
confirmText={t('Enable_encryption')}
41+
onClose={onClose}
42+
onCancel={onCancel}
43+
>
44+
<Box dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(t('E2E_password_request_text')) }} />
45+
<FieldGroup mbs={24} w='full'>
46+
<Field>
47+
<FieldRow>
48+
<Controller
49+
name='password'
50+
control={control}
51+
rules={{ required: t('Invalid_pass') }}
52+
render={({ field, fieldState: { error, invalid } }) => (
53+
<PasswordInput
54+
{...field}
55+
error={error?.message}
56+
aria-invalid={invalid ? 'true' : 'false'}
57+
aria-required='true'
58+
aria-describedby={error ? `${passwordInputId}-error` : undefined}
59+
placeholder={t('Please_enter_E2EE_password')}
60+
/>
61+
)}
62+
/>
63+
</FieldRow>
64+
{errors.password && (
65+
<FieldError id={`${passwordInputId}-error`} role='alert'>
66+
{errors.password.message}
67+
</FieldError>
68+
)}
69+
</Field>
70+
</FieldGroup>
71+
</GenericModal>
72+
);
73+
};
74+
75+
export default EnterE2EPasswordModal;
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
2+
3+
exports[`renders Default without crashing 1`] = `
4+
<body>
5+
<div>
6+
<dialog
7+
aria-labelledby=":r1:-title"
8+
aria-modal="true"
9+
class="rcx-box rcx-box--full rcx-modal"
10+
open=""
11+
>
12+
<form
13+
class="rcx-box rcx-box--full rcx-modal__inner rcx-css-1e2ego0"
14+
>
15+
<header
16+
class="rcx-box rcx-box--full rcx-modal__header"
17+
>
18+
<div
19+
class="rcx-box rcx-box--full rcx-modal__header-inner"
20+
>
21+
<div
22+
class="rcx-box rcx-box--full rcx-css-trljwa rcx-css-1xb12qx"
23+
>
24+
<i
25+
aria-hidden="true"
26+
class="rcx-box rcx-box--full rcx-icon--name-warning rcx-icon rcx-css-zozvgz"
27+
>
28+
29+
</i>
30+
</div>
31+
<div
32+
class="rcx-box rcx-box--full rcx-modal__header-text rcx-css-trljwa rcx-css-lma364"
33+
>
34+
<h2
35+
class="rcx-box rcx-box--full rcx-modal__title"
36+
id=":r1:-title"
37+
>
38+
Enter_E2E_password
39+
</h2>
40+
</div>
41+
<button
42+
aria-label="Close"
43+
class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-css-trljwa rcx-css-lma364"
44+
type="button"
45+
>
46+
<i
47+
aria-hidden="true"
48+
class="rcx-box rcx-box--full rcx-icon--name-cross rcx-icon rcx-css-4pvxx3"
49+
>
50+
51+
</i>
52+
</button>
53+
</div>
54+
</header>
55+
<div
56+
class="rcx-box rcx-box--full rcx-modal__content rcx-css-1vw7itl"
57+
>
58+
<div
59+
class="rcx-box rcx-box--full rcx-modal__content-wrapper rcx-css-r1bpeb"
60+
>
61+
<div
62+
class="rcx-box rcx-box--full"
63+
>
64+
E2E_password_request_text
65+
</div>
66+
<fieldset
67+
class="rcx-box rcx-box--full rcx-field-group rcx-css-1kyd72y"
68+
role="group"
69+
>
70+
<div
71+
class="rcx-box rcx-box--full rcx-field rcx-field-group__item"
72+
>
73+
<span
74+
class="rcx-box rcx-box--full rcx-field__row"
75+
>
76+
<label
77+
class="rcx-box rcx-box--full rcx-label rcx-box rcx-box--full rcx-box--animated rcx-input-box__wrapper"
78+
>
79+
<input
80+
aria-invalid="false"
81+
aria-required="true"
82+
class="rcx-box rcx-box--full rcx-box--animated rcx-input-box--undecorated rcx-input-box--type-password rcx-input-box"
83+
name="password"
84+
placeholder="Please_enter_E2EE_password"
85+
size="1"
86+
type="password"
87+
value=""
88+
/>
89+
<span
90+
class="rcx-box rcx-box--full rcx-input-box__addon"
91+
>
92+
<i
93+
aria-hidden="true"
94+
class="rcx-box rcx-box--full rcx-icon--name-eye-off rcx-icon rcx-css-1bepdyv"
95+
>
96+
97+
</i>
98+
</span>
99+
</label>
100+
</span>
101+
</div>
102+
</fieldset>
103+
</div>
104+
</div>
105+
<div
106+
class="rcx-box rcx-box--full rcx-modal__footer rcx-css-17mu816"
107+
>
108+
<div
109+
class="rcx-button-group rcx-button-group--align-end"
110+
role="group"
111+
>
112+
<button
113+
class="rcx-box rcx-box--full rcx-button--secondary rcx-button rcx-button-group__item"
114+
type="button"
115+
>
116+
<span
117+
class="rcx-button--content"
118+
>
119+
Do_It_Later
120+
</span>
121+
</button>
122+
<button
123+
class="rcx-box rcx-box--full rcx-button--primary rcx-button rcx-button-group__item"
124+
type="submit"
125+
>
126+
<span
127+
class="rcx-button--content"
128+
>
129+
Enable_encryption
130+
</span>
131+
</button>
132+
</div>
133+
</div>
134+
</form>
135+
</dialog>
136+
</div>
137+
</body>
138+
`;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './EnterE2EPasswordModal';

0 commit comments

Comments
 (0)