Add Remove Password UI into V2 (#4214)

# Description of Changes
- Add UI for Remove Password tool
- Fix more translation warnings that were being thrown in the console
- Add an encrypted PDF thumbnail and refactor thumbnail generation code
This commit is contained in:
James Brunton
2025-08-18 15:26:29 +01:00
committed by GitHub
parent 4c17c520d7
commit acbebd67a3
20 changed files with 806 additions and 163 deletions

View File

@@ -49,7 +49,7 @@ const LandingPage = () => {
activateOnClick={false}
styles={{
root: {
'&[data-accept]': {
'&[dataAccept]': {
backgroundColor: 'var(--landing-drop-paper-bg)',
},
},

View File

@@ -0,0 +1,127 @@
import { describe, expect, test, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import { MantineProvider } from '@mantine/core';
import RemovePasswordSettings from './RemovePasswordSettings';
import { defaultParameters } from '../../../hooks/tools/removePassword/useRemovePasswordParameters';
// Mock useTranslation with predictable return values
const mockT = vi.fn((key: string) => `mock-${key}`);
vi.mock('react-i18next', () => ({
useTranslation: () => ({ t: mockT })
}));
// Wrapper component to provide Mantine context
const TestWrapper = ({ children }: { children: React.ReactNode }) => (
<MantineProvider>{children}</MantineProvider>
);
describe('RemovePasswordSettings', () => {
const mockOnParameterChange = vi.fn();
beforeEach(() => {
vi.clearAllMocks();
});
test('should render password input field', () => {
render(
<TestWrapper>
<RemovePasswordSettings
parameters={defaultParameters}
onParameterChange={mockOnParameterChange}
/>
</TestWrapper>
);
expect(screen.getByText('mock-removePassword.password.label')).toBeInTheDocument();
});
test('should call onParameterChange when password is entered', () => {
render(
<TestWrapper>
<RemovePasswordSettings
parameters={defaultParameters}
onParameterChange={mockOnParameterChange}
/>
</TestWrapper>
);
const passwordInput = screen.getByPlaceholderText('mock-removePassword.password.placeholder');
fireEvent.change(passwordInput, { target: { value: 'test-password' } });
expect(mockOnParameterChange).toHaveBeenCalledWith('password', 'test-password');
});
test('should display current password value', () => {
const parametersWithPassword = { ...defaultParameters, password: 'current-password' };
render(
<TestWrapper>
<RemovePasswordSettings
parameters={parametersWithPassword}
onParameterChange={mockOnParameterChange}
/>
</TestWrapper>
);
const passwordInput = screen.getByPlaceholderText('mock-removePassword.password.placeholder') as HTMLInputElement;
expect(passwordInput.value).toBe('current-password');
});
test('should disable password input when disabled prop is true', () => {
render(
<TestWrapper>
<RemovePasswordSettings
parameters={defaultParameters}
onParameterChange={mockOnParameterChange}
disabled={true}
/>
</TestWrapper>
);
const passwordInput = screen.getByPlaceholderText('mock-removePassword.password.placeholder');
expect(passwordInput).toBeDisabled();
});
test('should enable password input when disabled prop is false', () => {
render(
<TestWrapper>
<RemovePasswordSettings
parameters={defaultParameters}
onParameterChange={mockOnParameterChange}
disabled={false}
/>
</TestWrapper>
);
const passwordInput = screen.getByPlaceholderText('mock-removePassword.password.placeholder');
expect(passwordInput).not.toBeDisabled();
});
test('should show password input as required', () => {
render(
<TestWrapper>
<RemovePasswordSettings
parameters={defaultParameters}
onParameterChange={mockOnParameterChange}
/>
</TestWrapper>
);
const passwordInput = screen.getByPlaceholderText('mock-removePassword.password.placeholder');
expect(passwordInput).toHaveAttribute('required');
});
test('should call translation function with correct keys', () => {
render(
<TestWrapper>
<RemovePasswordSettings
parameters={defaultParameters}
onParameterChange={mockOnParameterChange}
/>
</TestWrapper>
);
expect(mockT).toHaveBeenCalledWith('removePassword.password.label', 'Current Password');
expect(mockT).toHaveBeenCalledWith('removePassword.password.placeholder', 'Enter current password');
});
});

View File

@@ -0,0 +1,30 @@
import { Stack, Text, PasswordInput } from "@mantine/core";
import { useTranslation } from "react-i18next";
import { RemovePasswordParameters } from "../../../hooks/tools/removePassword/useRemovePasswordParameters";
interface RemovePasswordSettingsProps {
parameters: RemovePasswordParameters;
onParameterChange: (key: keyof RemovePasswordParameters, value: string) => void;
disabled?: boolean;
}
const RemovePasswordSettings = ({ parameters, onParameterChange, disabled = false }: RemovePasswordSettingsProps) => {
const { t } = useTranslation();
return (
<Stack gap="md">
<Stack gap="sm">
<PasswordInput
label={t('removePassword.password.label', 'Current Password')}
placeholder={t('removePassword.password.placeholder', 'Enter current password')}
value={parameters.password}
onChange={(e) => onParameterChange('password', e.target.value)}
disabled={disabled}
required
/>
</Stack>
</Stack>
);
};
export default RemovePasswordSettings;

View File

@@ -11,9 +11,9 @@ export function SuggestedToolsSection(): React.ReactElement {
return (
<Stack gap="md">
<Divider />
<Text size="lg" fw={600}>
{t('editYourNewFiles', 'Edit your new File(s)')}
{t('editYourNewFiles', 'Edit your new file(s)')}
</Text>
<Stack gap="xs">
@@ -39,4 +39,4 @@ export function SuggestedToolsSection(): React.ReactElement {
</Stack>
</Stack>
);
}
}

View File

@@ -6,7 +6,7 @@ export const useAddPasswordPermissionsTips = (): TooltipContent => {
return {
header: {
title: t("addPassword.tooltip.permissions.title", "Document Permissions")
title: t("addPassword.tooltip.permissions.title", "Change Permissions")
},
tips: [
{

View File

@@ -0,0 +1,20 @@
import { useTranslation } from 'react-i18next';
import { TooltipContent } from '../../types/tips';
export const useRemovePasswordTips = (): TooltipContent => {
const { t } = useTranslation();
return {
header: {
title: t("removePassword.title", "Remove Password")
},
tips: [
{
description: t(
"removePassword.tooltip.description",
"Removing password protection requires the current password that was used to encrypt the PDF. This will decrypt the document, making it accessible without a password."
)
}
]
};
};