Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
3841fd7
initial background replacement
OscarFava Jul 21, 2025
55af7e2
Modify folder structure + add background video container
OscarFava Jul 21, 2025
506acb5
Implemented background replacement and blur effects for video calls, …
OscarFava Jul 23, 2025
8f359b0
Refactored background effects for better layout and responsiveness, a…
OscarFava Jul 25, 2025
45020d0
Add custom background images initial button
OscarFava Jul 29, 2025
f60b121
Minor fix
OscarFava Jul 29, 2025
0b5e785
Remove blurButton
OscarFava Jul 29, 2025
d40aa17
unit testing
OscarFava Jul 29, 2025
3a51e0e
removed
OscarFava Jul 29, 2025
21077e5
Add unit testing
OscarFava Jul 30, 2025
7570983
Fix unit testing and first review (clean code and add comments)
OscarFava Jul 31, 2025
ceffe18
Merge remote-tracking branch 'origin/develop' into vidsol-105/backgro…
OscarFava Jul 31, 2025
a9ba4d2
Remove unfound file
OscarFava Jul 31, 2025
310abf1
Reduce duplicated lines
OscarFava Aug 1, 2025
cfff835
Reduce duplicated lines
OscarFava Aug 1, 2025
d36d700
Fix unit testing
OscarFava Aug 1, 2025
2639977
Clean code and give appropiate names
OscarFava Aug 1, 2025
965c1fe
Improve reliability
OscarFava Aug 1, 2025
b7c582b
Fix lint
OscarFava Aug 1, 2025
835c2b5
Fix kint
OscarFava Aug 1, 2025
714c562
Fix testing
OscarFava Aug 1, 2025
3d1af88
Fix integration
OscarFava Aug 1, 2025
574ac90
Fix unit testing + clean code + Fix docs typo + minor fixes
OscarFava Aug 4, 2025
9f5e3f4
Fix unit testing + add doc + minor bug fixs
OscarFava Aug 5, 2025
8092cdc
Tabs layout
OscarFava Aug 6, 2025
75f145e
Improve CSS responsive
OscarFava Aug 6, 2025
3afadd5
Start 'Add custom background effects layout'
OscarFava Aug 6, 2025
651cff2
Fix unit testing + add doc + minor bug fixs
OscarFava Aug 7, 2025
6b7031f
Clean code
OscarFava Aug 18, 2025
8f9211b
Merge branch 'vidsol-105/background-replacement' into vidsol-105/back…
OscarFava Aug 18, 2025
54081f6
Improve visibility of add background + store images on LocalStorage a…
OscarFava Aug 18, 2025
05b808e
Tooltip in add background + automatic apply when uploading image + cl…
OscarFava Aug 20, 2025
cf7502d
Tooltip in backgrounds boxes + align cancel and apply buttons margins…
OscarFava Aug 21, 2025
e4b58ec
Add cross to close dialog + improve responsive layout
OscarFava Aug 21, 2025
72bf9ac
Merge
OscarFava Aug 21, 2025
f5e4392
Add comment
OscarFava Aug 21, 2025
baa4ce2
Fix unit testing and clean code
OscarFava Aug 21, 2025
9287c09
Fix crypto and duplicated code
OscarFava Aug 21, 2025
dfa5506
Fix test
OscarFava Aug 21, 2025
8650c99
Reduce duplicated code
OscarFava Aug 21, 2025
973d82d
Reduce duplicated code
OscarFava Aug 21, 2025
6fc8c7e
Merge remote-tracking branch 'origin/develop' into vidsol-105/backgro…
OscarFava Sep 12, 2025
03cf685
Add margin to not enabled video
OscarFava Sep 12, 2025
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

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { vi, describe, it, expect } from 'vitest';
import AddBackgroundEffectLayout from './AddBackgroundEffectLayout';

vi.mock('../../../../utils/useImageStorage/useImageStorage', () => ({
useImageStorage: () => ({
storageError: '',
handleImageFromFile: vi.fn(async () => ({
dataUrl: 'data:image/png;base64,MOCKED',
})),
handleImageFromLink: vi.fn(async () => ({
dataUrl: 'data:image/png;base64,MOCKED_LINK',
})),
}),
}));

describe('AddBackgroundEffectLayout', () => {
it('should render without crashing', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think we can just say should render

render(<AddBackgroundEffectLayout customBackgroundImageChange={vi.fn()} />);
expect(screen.getByText(/Drag and Drop, or click here to upload Image/i)).toBeInTheDocument();
expect(screen.getByPlaceholderText(/Link from the web/i)).toBeInTheDocument();
expect(screen.getByTestId('background-effect-link-submit-button')).toBeInTheDocument();
});

it('shows error for invalid file type', async () => {
render(<AddBackgroundEffectLayout customBackgroundImageChange={vi.fn()} />);
const input = screen.getByLabelText(/upload/i);
const file = new File(['dummy'], 'test.txt', { type: 'text/plain' });
fireEvent.change(input, { target: { files: [file] } });
expect(
await screen.findByText(/Only JPG, PNG, or WebP images are allowed/i)
).toBeInTheDocument();
});

it('shows error for file size too large', async () => {
render(<AddBackgroundEffectLayout customBackgroundImageChange={vi.fn()} />);
const input = screen.getByLabelText(/upload/i);
const file = new File(['x'.repeat(3 * 1024 * 1024)], 'big.png', { type: 'image/png' });
Object.defineProperty(file, 'size', { value: 3 * 1024 * 1024 });
fireEvent.change(input, { target: { files: [file] } });
expect(await screen.findByText(/Image must be less than 2MB/i)).toBeInTheDocument();
});

it('calls customBackgroundImageChange on valid file upload', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think the name of the test is too literal. could we change it to something more generic?

const cb = vi.fn();
render(<AddBackgroundEffectLayout customBackgroundImageChange={cb} />);
const input = screen.getByLabelText(/upload/i);
const file = new File(['dummy'], 'test.png', { type: 'image/png' });
fireEvent.change(input, { target: { files: [file] } });
await waitFor(() => expect(cb).toHaveBeenCalledWith('data:image/png;base64,MOCKED'));
});

it('calls customBackgroundImageChange on valid link submit', async () => {
const cb = vi.fn();
render(<AddBackgroundEffectLayout customBackgroundImageChange={cb} />);
const input = screen.getByPlaceholderText(/Link from the web/i);
fireEvent.change(input, { target: { value: 'https://example.com/image.png' } });
const button = screen.getByTestId('background-effect-link-submit-button');
fireEvent.click(button);
await waitFor(() => expect(cb).toHaveBeenCalledWith('data:image/png;base64,MOCKED_LINK'));
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import {
Box,
Button,
CircularProgress,
InputAdornment,
TextField,
Typography,
} from '@mui/material';
import { ChangeEvent, ReactElement, useState } from 'react';
import ArrowForwardIcon from '@mui/icons-material/ArrowForward';
import LinkIcon from '@mui/icons-material/Link';
import { useImageStorage } from '../../../../utils/useImageStorage/useImageStorage';
import FileUploader from '../../FileUploader/FileUploader';

export type AddBackgroundEffectLayoutProps = {
customBackgroundImageChange: (param1: string) => void;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we change the name param1 to something else? something like dataUrl?

};

/**
* AddBackgroundEffectLayout Component
*
* This component manages the UI for adding background effects.
* @param {AddBackgroundEffectLayoutProps} props - The props for the component.
* @property {Function} customBackgroundImageChange - Callback function to handle background image change.
* @returns {ReactElement} The add background effect layout component.
*/
const AddBackgroundEffectLayout = ({
customBackgroundImageChange,
}: AddBackgroundEffectLayoutProps): ReactElement => {
const MAX_SIZE_MB = 2;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could this be moved to the constants file and be imported here instead?

const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp'];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here. can this be moved to the contants file?


const [fileError, setFileError] = useState('');
const [imageLink, setImageLink] = useState('');
const [linkLoading, setLinkLoading] = useState(false);
Comment on lines +33 to +35
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you specify the types i.e useState<string> and useState<boolean>?

const { storageError, handleImageFromFile, handleImageFromLink } = useImageStorage();

type HandleFileChangeType = ChangeEvent<HTMLInputElement> | { target: { files: FileList } };

const handleFileChange = async (e: HandleFileChangeType) => {
const { files } = e.target;
if (!files || files.length === 0) {
return;
}

const file = files[0];
if (!file) {
return;
}

if (!ALLOWED_TYPES.includes(file.type)) {
setFileError('Only JPG, PNG, or WebP images are allowed.');
return;
}

if (file.size > MAX_SIZE_MB * 1024 * 1024) {
setFileError('Image must be less than 2MB.');
return;
}

try {
const newImage = await handleImageFromFile(file);
if (newImage) {
setFileError('');
customBackgroundImageChange(newImage.dataUrl);
}
} catch {
setFileError('Failed to process uploaded image.');
}
};

const handleLinkSubmit = async () => {
setFileError('');
setLinkLoading(true);
try {
const newImage = await handleImageFromLink(imageLink);
if (newImage) {
setFileError('');
customBackgroundImageChange(newImage.dataUrl);
} else {
setFileError('Failed to store image.');
}
} catch {
// error handled in hook
} finally {
setLinkLoading(false);
}
};

return (
<Box>
<FileUploader handleFileChange={handleFileChange} />

{(fileError || storageError) && (
<Typography color="error" mt={1} fontSize={14}>
{fileError || storageError}
</Typography>
)}

<Box mt={2} display="flex" alignItems="center" gap={1}>
<TextField
fullWidth
size="small"
placeholder="Link from the web"
className="add-background-effect-input"
value={imageLink}
onChange={(e) => setImageLink(e.target.value)}
InputProps={{
startAdornment: (
<InputAdornment position="start">
{linkLoading ? <CircularProgress size={24} /> : <LinkIcon sx={{ fontSize: 24 }} />}
</InputAdornment>
),
}}
/>

<Button
data-testid="background-effect-link-submit-button"
variant="contained"
color="primary"
onClick={handleLinkSubmit}
disabled={linkLoading}
style={{ minWidth: 0, padding: '8px 12px' }}
>
{linkLoading ? (
<CircularProgress size={24} color="inherit" />
) : (
<ArrowForwardIcon sx={{ fontSize: 24 }} />
)}
</Button>
</Box>
</Box>
);
};

export default AddBackgroundEffectLayout;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import AddBackgroundEffectLayout from './AddBackgroundEffectLayout';

export default AddBackgroundEffectLayout;

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { vi, describe, it, expect } from 'vitest';
import BackgroundEffectTabs from './BackgroundEffectTabs';

describe('BackgroundEffectTabs', () => {
const setTabSelected = vi.fn();
const setBackgroundSelected = vi.fn();
const cleanBackgroundReplacementIfSelectedAndDeleted = vi.fn();
const customBackgroundImageChange = vi.fn();

it('renders tabs and defaults to Backgrounds tab', () => {
render(
<BackgroundEffectTabs
tabSelected={0}
setTabSelected={setTabSelected}
backgroundSelected=""
setBackgroundSelected={setBackgroundSelected}
cleanBackgroundReplacementIfSelectedAndDeletedFunction={
cleanBackgroundReplacementIfSelectedAndDeleted
}
customBackgroundImageChange={customBackgroundImageChange}
/>
);
expect(screen.getByRole('tab', { name: /Backgrounds/i })).toBeInTheDocument();
expect(screen.getByRole('tab', { name: /Add Background/i })).toBeInTheDocument();
});

it('switches to Add Background tab when clicked', async () => {
render(
<BackgroundEffectTabs
tabSelected={0}
setTabSelected={setTabSelected}
backgroundSelected=""
setBackgroundSelected={setBackgroundSelected}
cleanBackgroundReplacementIfSelectedAndDeletedFunction={
cleanBackgroundReplacementIfSelectedAndDeleted
}
customBackgroundImageChange={customBackgroundImageChange}
/>
);
const addTab = screen.getByRole('tab', { name: /Add Background/i });
await userEvent.click(addTab);
expect(setTabSelected).toHaveBeenCalledWith(1);
});

it('renders AddBackgroundEffectLayout when Add Background tab is selected', () => {
render(
<BackgroundEffectTabs
tabSelected={1}
setTabSelected={setTabSelected}
backgroundSelected=""
setBackgroundSelected={setBackgroundSelected}
cleanBackgroundReplacementIfSelectedAndDeletedFunction={
cleanBackgroundReplacementIfSelectedAndDeleted
}
customBackgroundImageChange={customBackgroundImageChange}
/>
);
expect(screen.getByText(/upload/i)).toBeInTheDocument();
});
});
Loading