Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Binary file added apps/e2e/public/images/example.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
68 changes: 64 additions & 4 deletions apps/e2e/src/Controller/CropperjsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,77 @@
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\UX\Cropperjs\Factory\CropperInterface;
use Symfony\UX\Cropperjs\Form\CropperType;

#[Route('/ux-cropperjs', name: 'app_ux_cropperjs_')]
final class CropperjsController extends AbstractController
{
#[Route('/', name: 'index')]
public function index(): Response
public function __construct(
#[Autowire('%kernel.project_dir%/public')]
private string $publicDir
) {}

#[Route('/crop', name: 'crop')]
public function crop(CropperInterface $cropper, Request $request): Response
{
return $this->render('ux_cropperjs/index.html.twig', [
'controller_name' => 'CropperjsController',
$crop = $cropper->createCrop($this->publicDir . '/images/example.jpg');
$crop->setCroppedMaxSize(800, 600);

$form = $this->createFormBuilder(['crop' => $crop])
->add('crop', CropperType::class, [
'public_url' => '/images/example.jpg',
'cropper_options' => [
'viewMode' => 1,
],
])
->getForm();

$form->handleRequest($request);

$croppedImageData = null;
if ($form->isSubmitted() && $form->isValid()) {
// Get the cropped image as base64
$croppedImageData = base64_encode($crop->getCroppedImage());
}

return $this->render('ux_cropperjs/crop.html.twig', [
'form' => $form,
'croppedImageData' => $croppedImageData,
]);
}

#[Route('/crop-with-aspect-ratio', name: 'crop_with_aspect_ratio')]
public function cropWithAspectRatio(CropperInterface $cropper, Request $request): Response
{
$crop = $cropper->createCrop($this->publicDir . '/images/example.jpg');
$crop->setCroppedMaxSize(1920, 1080);

$form = $this->createFormBuilder(['crop' => $crop])
->add('crop', CropperType::class, [
'public_url' => '/images/example.jpg',
'cropper_options' => [
'aspectRatio' => 16 / 9,
'viewMode' => 1,
],
])
->getForm();

$form->handleRequest($request);

$croppedImageData = null;
if ($form->isSubmitted() && $form->isValid()) {
// Get the cropped image as base64
$croppedImageData = base64_encode($crop->getCroppedImage());
}

return $this->render('ux_cropperjs/crop.html.twig', [
'form' => $form,
'croppedImageData' => $croppedImageData,
]);
}
}
2 changes: 2 additions & 0 deletions apps/e2e/src/Repository/ExampleRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public function __construct()
new Example(UxPackage::ChartJs, 'Line chart with options', 'A line chart with custom options (showLines: false) that displays data points without connecting lines.', 'app_ux_chartjs_with_options'),
new Example(UxPackage::ChartJs, 'Pie chart', 'A pie chart displaying data distribution across different categories.', 'app_ux_chartjs_pie'),
new Example(UxPackage::ChartJs, 'Pie chart with options', 'A pie chart with custom options to control the appearance and behavior.', 'app_ux_chartjs_pie_with_options'),
new Example(UxPackage::Cropperjs, 'Image cropper', 'Crop an image with Cropper.js using default options.', 'app_ux_cropperjs_crop'),
new Example(UxPackage::Cropperjs, 'Image cropper with aspect ratio', 'Crop an image with a fixed 16:9 aspect ratio constraint.', 'app_ux_cropperjs_crop_with_aspect_ratio'),
new Example(UxPackage::LiveComponent, 'Examples filtering', "On this page, you can filter all examples by query terms, and observe how the UI and URLs update during and after processing.", 'app_home'),
new Example(UxPackage::LiveComponent, 'Counter', 'A basic counter that you can increment or decrement.', 'app_ux_live_component_counter'),
new Example(UxPackage::Turbo, 'Turbo Drive navigation', 'Navigate between pages without full page reload using Turbo Drive.', 'app_ux_turbo_drive'),
Expand Down
26 changes: 26 additions & 0 deletions apps/e2e/templates/ux_cropperjs/crop.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{% extends 'example.html.twig' %}

{% block example %}
<div class="row">
<div class="col-md-8 mx-auto">
{% if croppedImageData %}
<div class="alert alert-success mb-4" id="crop-success">
<h5>Image cropped successfully!</h5>
<p>Here is your cropped image:</p>
<img src="data:image/jpeg;base64,{{ croppedImageData }}"
alt="Cropped image"
id="cropped-result"
class="img-fluid border"
style="max-width: 100%;">
</div>
{% endif %}

{{ form_start(form, { attr: { 'data-turbo': 'false' } }) }}
{{ form_row(form.crop) }}
<div class="mt-3">
<button type="submit" class="btn btn-primary" id="crop-submit">Crop Image</button>
</div>
{{ form_end(form) }}
</div>
</div>
{% endblock %}
3 changes: 0 additions & 3 deletions apps/e2e/templates/ux_cropperjs/index.html.twig

This file was deleted.

70 changes: 70 additions & 0 deletions src/Cropperjs/assets/test/browser/cropperjs.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { expect, test } from '@playwright/test';

test('Can display and interact with Cropper.js', async ({ page }) => {
await page.goto('/ux-cropperjs/crop');

// Wait for the cropper to be initialized
const cropperContainer = page.locator('.cropperjs');
await expect(cropperContainer).toBeVisible();

// Check that the canvas is created by Cropper.js
const cropperCanvas = page.locator('.cropper-canvas');
await expect(cropperCanvas).toBeVisible();

// Check that the crop box is visible
const cropBox = page.locator('.cropper-crop-box');
await expect(cropBox).toBeVisible();

const actionE = page.locator('[data-cropper-action="e"].cropper-line');
const actionEBox = await cropBox.boundingBox();
if (actionEBox) {
// Drag the east handle to resize the crop box
await actionE.hover({ force: true });
await page.mouse.down();
await page.mouse.move(actionEBox.x + actionEBox.width - 200, actionEBox.y + actionEBox.height / 2);
await page.mouse.up();
}

// Submit the form to crop the image
await page.click('#crop-submit');

// Wait for the cropped image to be displayed
await expect(page.locator('#crop-success')).toBeVisible();
await expect(page.locator('#cropped-result')).toBeVisible();

// Verify the cropped image is a valid base64 image
const croppedImage = page.locator('#cropped-result');
expect(await croppedImage.getAttribute('src')).toContain('data:image/jpeg;base64,');
const croppedImageWidth = await croppedImage.evaluate((img: HTMLImageElement) => img.naturalWidth);
const croppedImageHeight = await croppedImage.evaluate((img: HTMLImageElement) => img.naturalHeight);
expect(croppedImageWidth).toBeGreaterThanOrEqual(540); // Chrome
expect(croppedImageWidth).toBeLessThanOrEqual(541); // Firefox
expect(croppedImageHeight).toEqual(461);
});

test('Can display Cropper.js with aspect ratio constraint', async ({ page }) => {
await page.goto('/ux-cropperjs/crop-with-aspect-ratio');

// Wait for the cropper to be initialized
const cropperContainer = page.locator('.cropperjs');
await expect(cropperContainer).toBeVisible();

// Check that the canvas and crop box are visible
await expect(page.locator('.cropper-canvas')).toBeVisible();
await expect(page.locator('.cropper-crop-box')).toBeVisible();

// Submit the form to crop the image
await page.click('#crop-submit');

// Wait for the cropped image to be displayed
await expect(page.locator('#crop-success')).toBeVisible();
await expect(page.locator('#cropped-result')).toBeVisible();

// Verify the cropped image is a valid base64 image, and has the correct aspect ratio (16:9)
const croppedImage = page.locator('#cropped-result');
expect(await croppedImage.getAttribute('src')).toContain('data:image/jpeg;base64,');
expect(await croppedImage.evaluate((img: HTMLImageElement) => img.naturalWidth / img.naturalHeight)).toBeCloseTo(
16 / 9,
1
);
});
7 changes: 0 additions & 7 deletions src/Cropperjs/assets/test/browser/placeholder.test.ts

This file was deleted.

Loading