Skip to content

Commit

Permalink
Add forms (#47)
Browse files Browse the repository at this point in the history
  • Loading branch information
loic425 authored Sep 19, 2024
2 parents db928c6 + e5b35ae commit d00a6a6
Show file tree
Hide file tree
Showing 13 changed files with 268 additions and 3 deletions.
7 changes: 5 additions & 2 deletions app/Entity/Book.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use Sylius\Resource\Metadata\Delete;
use Sylius\Resource\Metadata\Index;
use Sylius\Resource\Metadata\Update;
use Symfony\Component\Validator\Constraints\NotBlank;

#[ORM\Entity(repositoryClass: BookRepository::class)]
#[AsResource(
Expand Down Expand Up @@ -59,9 +60,11 @@ class Book implements ResourceInterface
private ?int $id = null;

#[ORM\Column(type: 'string', length: 255)]
#[NotBlank]
private ?string $title = null;

#[ORM\Column(type: 'string', length: 255)]
#[NotBlank]
private ?string $authorName = null;

#[ORM\Column(type: 'datetime_immutable')]
Expand All @@ -82,7 +85,7 @@ public function getTitle(): ?string
return $this->title;
}

public function setTitle(string $title): void
public function setTitle(?string $title): void
{
$this->title = $title;
}
Expand All @@ -92,7 +95,7 @@ public function getAuthorName(): ?string
return $this->authorName;
}

public function setAuthorName(string $authorName): void
public function setAuthorName(?string $authorName): void
{
$this->authorName = $authorName;
}
Expand Down
44 changes: 44 additions & 0 deletions src/BootstrapAdminUi/assets/styles/_form.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*!
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

textarea.form-control {
min-height: 8rem;
height: 12rem;
}

.tab-error {
@extend .float-end;
@extend .badge;
@extend .bg-danger;
@extend .rounded-pill;
@extend .text-white;
}

.accordion-error {
@extend .position-absolute;
@extend .top-50;
@extend .start-0;
@extend .translate-middle;
@extend .badge;
@extend .rounded-pill;
@extend .bg-danger;
@extend .text-white;
}

.accordion-item:has(.accordion-error),
.list-group-item:has(.tab-error),
.list-group-item.active:has(.tab-error) {
border-left-style: solid;
border-left-width: 2px;
border-left-color: #ff0017;
}

.form-select:disabled {
color: var(--tblr-gray-500);
}
1 change: 1 addition & 0 deletions src/BootstrapAdminUi/assets/styles/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ body {

@import "alert";
@import "datatable";
@import "form";
@import "sidebar";

@import "body";
21 changes: 21 additions & 0 deletions src/BootstrapAdminUi/config/app/twig_hooks/common/create.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@
'header' => [
'template' => '@SyliusBootstrapAdminUi/shared/crud/common/content/header.html.twig',
],
'form_error_alert' => [
'template' => '@SyliusBootstrapAdminUi/shared/crud/common/content/form_error_alert.html.twig',
],
'form' => [
'template' => '@SyliusBootstrapAdminUi/shared/crud/common/content/form.html.twig',
],
],
'sylius_admin.common.create.content.header' => [
'breadcrumbs' => [
Expand All @@ -56,6 +62,21 @@
'template' => '@SyliusBootstrapAdminUi/shared/crud/common/content/header/title_block/actions/create.html.twig',
],
],
'sylius_admin.common.create.content.form' => [
'sections' => [
'template' => '@SyliusBootstrapAdminUi/shared/crud/common/content/form/sections.html.twig',
],
],
'sylius_admin.common.create.content.form.sections' => [
'general' => [
'template' => '@SyliusBootstrapAdminUi/shared/crud/common/content/form/sections/general.html.twig',
],
],
'sylius_admin.common.create.content.form.sections.general' => [
'default' => [
'template' => '@SyliusBootstrapAdminUi/shared/crud/common/content/form/sections/general/default.html.twig',
],
],
],
]);
};
21 changes: 21 additions & 0 deletions src/BootstrapAdminUi/config/app/twig_hooks/common/update.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@
'header' => [
'template' => '@SyliusBootstrapAdminUi/shared/crud/common/content/header.html.twig',
],
'form_error_alert' => [
'template' => '@SyliusBootstrapAdminUi/shared/crud/common/content/form_error_alert.html.twig',
],
'form' => [
'template' => '@SyliusBootstrapAdminUi/shared/crud/common/content/form.html.twig',
],
],
'sylius_admin.common.update.content.header' => [
'breadcrumbs' => [
Expand Down Expand Up @@ -58,6 +64,21 @@
'priority' => -300,
],
],
'sylius_admin.common.update.content.form' => [
'sections' => [
'template' => '@SyliusBootstrapAdminUi/shared/crud/common/content/form/sections.html.twig',
],
],
'sylius_admin.common.update.content.form.sections' => [
'general' => [
'template' => '@SyliusBootstrapAdminUi/shared/crud/common/content/form/sections/general.html.twig',
],
],
'sylius_admin.common.update.content.form.sections.general' => [
'default' => [
'template' => '@SyliusBootstrapAdminUi/shared/crud/common/content/form/sections/general/default.html.twig',
],
],
],
]);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{% if form is not defined %}
{% set form = hookable_metadata.context.form %}
{% endif %}

{% form_theme form '@SyliusBootstrapAdminUi/shared/form_theme.html.twig' %}

<div class="container-xl" {% if attributes is defined %} {{ attributes }} {% endif %}>
{{ form_start(form, {'attr': {'novalidate': 'novalidate', 'id': form.vars.id}}) }}
<div class="card-body">
{% if hookable_metadata.configuration.method is defined %}
<input type="hidden" name="_method" value="{{ hookable_metadata.configuration.method }}"/>
{% elseif form.vars.data.id is not null %}
<input type="hidden" name="_method" value="PUT"/>
{% endif %}
{{ form_errors(form) }}
{{ form_widget(form._token) }}

{% hook 'form' with { form } %}
</div>
{{ form_end(form, {render_rest: hookable_metadata.configuration.render_rest|default(false)}) }}
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div class="row">
{% hook 'sections' %}
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<div class="col-12">
<div class="card">
<div class="card-body">
<div class="row align-items-center">
{% hook 'general' %}
</div>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{ form_widget(hookable_metadata.context.form) }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{% from '@SyliusBootstrapAdminUi/shared/helper/icon.html.twig' import icon %}

{% set form = hookable_metadata.context.form|default(null) %}

{% if form is not null and form.vars.valid == false %}
<div class="container-xl">
<div class="sylius alert alert-danger" role="alert" {{ sylius_test_html_attribute('form-error-alert') }}>
<div class="d-flex">
<div>{{ icon({ icon: 'danger' }) }}</div>
<div class="mx-2">
<h4 class="alert-title">{{ 'sylius.ui.error'|trans }}</h4>
<div class="text-secondary">{{ 'sylius.ui.this_form_contains_errors'|trans }}</div>
</div>
</div>
</div>
</div>
{% endif %}
45 changes: 45 additions & 0 deletions src/BootstrapAdminUi/templates/shared/form_theme.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{% extends 'bootstrap_5_layout.html.twig' %}

{%- block form_start -%}
{%- do form.setMethodRendered() -%}
{% set method = method|upper %}
{%- if method in ["GET", "POST"] -%}
{% set form_method = method %}
{%- else -%}
{% set form_method = "POST" %}
{%- endif -%}
<form{% if name != '' %} name="{{ name }}"{% endif %} method="{{ form_method|lower }}"{% if action != '' %} action="{{ action }}"{% endif %}{{ block('attributes') }}{% if multipart %} enctype="multipart/form-data"{% endif %} {{ stimulus_controller('compound-form-errors') }}>
{%- if form_method != method -%}
<input type="hidden" name="_method" value="{{ method }}" />
{%- endif -%}
{%- endblock form_start -%}

{%- block form_row -%}
{% set row_attr = row_attr|default({})|merge({ class: (row_attr.class|default('mb-3') ~ ' field')|trim }) %}
{{ parent() }}
{%- endblock form_row %}

{% block sylius_resource_autocomplete_choice_row %}
<div class="{% if required %}required {% endif %}{% if disabled %}disabled {% endif %}field{% if (not compound or force_error|default(false)) and not valid %} error{% endif %}">
{{- form_label(form) -}}
<select
class="form-control"
data-choices="true"
data-choice-label-field="{{ choice_name }}"
data-choice-value-field="{{ choice_value }}"
data-input-id="{{ form.vars.id }}"
data-init-url="{{ init_load_url|default(null) }}"
{% if search_url is defined %}data-search-url="{{ search_url }}"{% endif %}
data-no-choices-text="{{ no_choices_translation|default('sylius.ui.start_typing_to_search')|trans }}"
{% if form.vars.disabled %}disabled{% endif %}
{% if multiple %}multiple{% endif %}
></select>
{{ form_widget(form) }}
{{- form_errors(form) -}}
</div>
{% endblock %}

{%- block button_widget -%}
{% set attr = attr|merge({'class': (attr.class|default('') ~ ' btn btn-outline-primary')|trim}) %}
{{ parent() }}
{%- endblock -%}
1 change: 1 addition & 0 deletions src/UiTranslations/translations/messages.en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ sylius:
search: Search
search_menu: 'Search menu'
success: Success
this_form_contains_errors: 'This form contains errors.'
update: Update
value: Value
warning: Warning
80 changes: 79 additions & 1 deletion tests/Functional/BookTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

namespace MainTests\Sylius\Functional;

use App\Entity\Book;
use App\Factory\BookFactory;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\DomCrawler\Link;
use Symfony\Component\HttpFoundation\Response;
use Zenstruck\Foundry\Persistence\Proxy;
use Zenstruck\Foundry\Test\ResetDatabase;

final class BookTest extends WebTestCase
Expand Down Expand Up @@ -93,6 +95,39 @@ public function testAddingBookContent(): void
self::assertResponseIsSuccessful();
}

public function testAddingBook(): void
{
$this->client->request('GET', '/admin/books/new');

$this->client->submitForm('Create', [
'sylius_resource[title]' => 'Shinning',
'sylius_resource[authorName]' => 'Stephen King',
]);

self::assertResponseRedirects(expectedCode: Response::HTTP_FOUND);

/** @var Proxy<Book> $book */
$book = BookFactory::find(['title' => 'Shinning']);

self::assertSame('Shinning', $book->getTitle());
self::assertSame('Stephen King', $book->getAuthorName());
}

public function testValidationErrorsWhenAddingBook(): void
{
$this->client->request('GET', '/admin/books/new');
$this->client->submitForm('Create', [
'sylius_resource[title]' => null,
'sylius_resource[authorName]' => null,
]);

self::assertResponseStatusCodeSame(Response::HTTP_UNPROCESSABLE_ENTITY);
self::assertSelectorTextContains('[data-test-form-error-alert] .alert-title', 'Error');
self::assertSelectorTextContains('[data-test-form-error-alert] .text-secondary', 'This form contains errors.');
self::assertSelectorTextContains('#sylius_resource_title + .invalid-feedback', 'This value should not be blank.');
self::assertSelectorTextContains('#sylius_resource_authorName + .invalid-feedback', 'This value should not be blank.');
}

public function testEditingBookContent(): void
{
$book = BookFactory::new()
Expand All @@ -105,6 +140,49 @@ public function testEditingBookContent(): void
self::assertResponseIsSuccessful();
}

public function testEditingBook(): void
{
$book = BookFactory::new()
->withTitle('Shinning')
->withAuthorName('Stephen King')
->create();

$this->client->request('GET', sprintf('/admin/books/%s/edit', $book->getId()));

$this->client->submitForm('Update', [
'sylius_resource[title]' => 'Carrie',
'sylius_resource[authorName]' => 'Stephen King',
]);

self::assertResponseRedirects(expectedCode: Response::HTTP_FOUND);

/** @var Proxy<Book> $book */
$book = BookFactory::find(['title' => 'Carrie']);

self::assertSame('Carrie', $book->getTitle());
self::assertSame('Stephen King', $book->getAuthorName());
}

public function testValidationErrorsWhenEditingBook(): void
{
$book = BookFactory::new()
->withTitle('Shinning')
->withAuthorName('Stephen King')
->create();

$this->client->request('GET', sprintf('/admin/books/%s/edit', $book->getId()));
$this->client->submitForm('Update', [
'sylius_resource[title]' => null,
'sylius_resource[authorName]' => null,
]);

self::assertResponseStatusCodeSame(Response::HTTP_UNPROCESSABLE_ENTITY);
self::assertSelectorTextContains('[data-test-form-error-alert] .alert-title', 'Error');
self::assertSelectorTextContains('[data-test-form-error-alert] .text-secondary', 'This form contains errors.');
self::assertSelectorTextContains('#sylius_resource_title + .invalid-feedback', 'This value should not be blank.');
self::assertSelectorTextContains('#sylius_resource_authorName + .invalid-feedback', 'This value should not be blank.');
}

public function testRemovingBook(): void
{
BookFactory::new()
Expand Down

0 comments on commit d00a6a6

Please sign in to comment.