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

fix(web): set album description textarea height correctly #9880

Merged
merged 10 commits into from
Jun 1, 2024
18 changes: 18 additions & 0 deletions web/src/lib/components/album-page/album-description.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import AlbumDescription from '$lib/components/album-page/album-description.svelte';
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/svelte';
import { describe } from 'vitest';

describe('AlbumDescription component', () => {
it('shows an AutogrowTextarea component when isOwned is true', () => {
render(AlbumDescription, { isOwned: true, id: '', description: '' });
const autogrowTextarea = screen.getByTestId('autogrow-textarea');
expect(autogrowTextarea).toBeInTheDocument();
});

it('does not show an AutogrowTextarea component when isOwned is false', () => {
render(AlbumDescription, { isOwned: false, id: '', description: '' });
const autogrowTextarea = screen.queryByTestId('autogrow-textarea');
expect(autogrowTextarea).not.toBeInTheDocument();
});
});
26 changes: 6 additions & 20 deletions web/src/lib/components/album-page/album-description.svelte
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
<script lang="ts">
import { autoGrowHeight } from '$lib/actions/autogrow';
import { updateAlbumInfo } from '@immich/sdk';
import { handleError } from '$lib/utils/handle-error';
import { shortcut } from '$lib/actions/shortcut';
import AutogrowTextarea from '$lib/components/shared-components/autogrow-textarea.svelte';

export let id: string;
export let description: string;
export let isOwned: boolean;

$: newDescription = description;

const handleUpdateDescription = async () => {
if (newDescription === description) {
return;
}

const handleUpdateDescription = async (newDescription: string) => {
try {
await updateAlbumInfo({
id,
Expand All @@ -24,24 +17,17 @@
});
} catch (error) {
handleError(error, 'Error updating album description');
return;
}
description = newDescription;
};
</script>

{#if isOwned}
<textarea
class="w-full mt-2 resize-none text-black dark:text-white border-b-2 border-transparent border-gray-500 bg-transparent text-base outline-none transition-all focus:border-b-2 focus:border-immich-primary disabled:border-none dark:focus:border-immich-dark-primary hover:border-gray-400"
bind:value={newDescription}
on:input={(e) => autoGrowHeight(e.currentTarget)}
on:focusout={handleUpdateDescription}
use:autoGrowHeight
<AutogrowTextarea
content={description}
class="w-full mt-2 text-black dark:text-white border-b-2 border-transparent border-gray-500 bg-transparent text-base outline-none transition-all focus:border-b-2 focus:border-immich-primary disabled:border-none dark:focus:border-immich-dark-primary hover:border-gray-400"
onContentUpdate={handleUpdateDescription}
placeholder="Add a description"
use:shortcut={{
shortcut: { key: 'Enter', ctrl: true },
onShortcut: (e) => e.currentTarget.blur(),
}}
/>
{:else if description}
<p class="break-words whitespace-pre-line w-full text-black dark:text-white text-base">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,18 @@
<script lang="ts">
import { autoGrowHeight } from '$lib/actions/autogrow';
import { clickOutside } from '$lib/actions/click-outside';
import { shortcut } from '$lib/actions/shortcut';
import {
NotificationType,
notificationController,
} from '$lib/components/shared-components/notification/notification';
import { handleError } from '$lib/utils/handle-error';
import { updateAsset, type AssetResponseDto } from '@immich/sdk';
import { tick } from 'svelte';
import AutogrowTextarea from '$lib/components/shared-components/autogrow-textarea.svelte';

export let asset: AssetResponseDto;
export let isOwner: boolean;

let textarea: HTMLTextAreaElement;
$: description = asset.exifInfo?.description || '';
$: newDescription = description;

$: if (textarea) {
newDescription;
void tick().then(() => autoGrowHeight(textarea));
}

const handleFocusOut = async () => {
if (description === newDescription) {
return;
}

const handleFocusOut = async (newDescription: string) => {
try {
await updateAsset({ id: asset.id, updateAssetDto: { description: newDescription } });
notificationController.show({
Expand All @@ -36,23 +22,17 @@
} catch (error) {
handleError(error, 'Cannot update the description');
}
description = newDescription;
};
</script>

{#if isOwner}
<section class="px-4 mt-10">
<textarea
bind:this={textarea}
class="max-h-[500px] w-full resize-none border-b border-gray-500 bg-transparent text-base text-black outline-none transition-all focus:border-b-2 focus:border-immich-primary disabled:border-none dark:text-white dark:focus:border-immich-dark-primary immich-scrollbar"
<AutogrowTextarea
content={description}
class="max-h-[500px] w-full border-b border-gray-500 bg-transparent text-base text-black outline-none transition-all focus:border-b-2 focus:border-immich-primary disabled:border-none dark:text-white dark:focus:border-immich-dark-primary immich-scrollbar"
onContentUpdate={handleFocusOut}
placeholder="Add a description"
on:focusout={handleFocusOut}
on:input={(e) => (newDescription = e.currentTarget.value)}
value={description}
use:clickOutside={{ onOutclick: void handleFocusOut }}
use:shortcut={{
shortcut: { key: 'Enter', ctrl: true },
onShortcut: (e) => e.currentTarget.blur(),
}}
/>
</section>
{:else if description}
Expand Down
60 changes: 60 additions & 0 deletions web/src/lib/components/shared-components/autogrow-textarea.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import AutogrowTextarea from '$lib/components/shared-components/autogrow-textarea.svelte';
import { render, screen, waitFor } from '@testing-library/svelte';
import userEvent from '@testing-library/user-event';

describe('AutogrowTextarea component', () => {
const getTextarea = () => screen.getByTestId('autogrow-textarea') as HTMLTextAreaElement;

it('should render correctly', () => {
render(AutogrowTextarea);
const textarea = getTextarea();
expect(textarea).toBeInTheDocument();
});

it('should show the content passed to the component', () => {
render(AutogrowTextarea, { content: 'stuff' });
const textarea = getTextarea();
expect(textarea.value).toBe('stuff');
});

it('should show the placeholder passed to the component', () => {
render(AutogrowTextarea, { placeholder: 'asdf' });
const textarea = getTextarea();
expect(textarea.placeholder).toBe('asdf');
});

it('should execute the passed callback on blur', async () => {
const user = userEvent.setup();
const update = vi.fn();
render(AutogrowTextarea, { content: 'existing', onContentUpdate: update });
const textarea = getTextarea();
await user.click(textarea);
await user.keyboard('extra');
textarea.blur();
await waitFor(() => expect(update).toHaveBeenCalledWith('existingextra'));
});

it('should execute the passed callback when pressing ctrl+enter in the textarea', async () => {
const user = userEvent.setup();
const update = vi.fn();
render(AutogrowTextarea, { onContentUpdate: update });
const textarea = getTextarea();
await user.click(textarea);
const string = 'content';
await user.keyboard(string);
await user.keyboard('{Control>}{Enter}{/Control}');
await waitFor(() => expect(update).toHaveBeenCalledWith(string));
});

it('should not execute the passed callback if the text has not changed', async () => {
const user = userEvent.setup();
const update = vi.fn();
render(AutogrowTextarea, { content: 'initial', onContentUpdate: update });
const textarea = getTextarea();
await user.click(textarea);
await user.clear(textarea);
await user.keyboard('initial');
await user.keyboard('{Control>}{Enter}{/Control}');
await waitFor(() => expect(update).not.toHaveBeenCalled());
});
});
39 changes: 39 additions & 0 deletions web/src/lib/components/shared-components/autogrow-textarea.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<script lang="ts">
import { autoGrowHeight } from '$lib/actions/autogrow';
import { shortcut } from '$lib/actions/shortcut';
import { tick } from 'svelte';

export let content: string = '';
let className: string = '';
export { className as class };
export let onContentUpdate: (newContent: string) => void = () => null;
export let placeholder: string = '';

let textarea: HTMLTextAreaElement;
$: newContent = content;

$: if (textarea) {
newContent;
void tick().then(() => autoGrowHeight(textarea));
}

const updateContent = () => {
if (content === newContent) {
return;
}
onContentUpdate(newContent);
};
</script>

<textarea
bind:this={textarea}
class="resize-none {className}"
on:focusout={updateContent}
on:input={(e) => (newContent = e.currentTarget.value)}
{placeholder}
use:shortcut={{
shortcut: { key: 'Enter', ctrl: true },
onShortcut: (e) => e.currentTarget.blur(),
}}
data-testid="autogrow-textarea">{content}</textarea
>
Loading