Skip to content
Draft
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
26 changes: 26 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org

root = true

[*]
indent_style = space
indent_size = 4

end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false
insert_final_newline = false

[*.{yaml,yml}]
indent_size = 2
trim_trailing_whitespace = false

# Makefile must be tab indented
[Makefile]
indent_style = tab
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions source/devapi/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Apart from the current documentation, you can also generate the full PHP documen
database/index
search
controllers
symfonycomponents/index
hlapi/index
massiveactions
rules
Expand Down
76 changes: 76 additions & 0 deletions source/devapi/symfonycomponents/alert.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
Alert
=====

.. versionadded:: 12.0.0

Renders an alert box inline at its position in the HTML.

.. image:: /_static/images/symfonycomponents/alert-1.png
:alt: Danger alert

Props
-----

All props are optional.

* :code:`type` **string**.

* Possible values: :code:`info` (default), :code:`success`, :code:`warning`, :code:`danger`.

* :code:`title` **string**.

* :code:`messages` **string|array<string>**. The alert message. An array can be passed to display multiple messages.

* :code:`icon` **string**. A CSS icon class, for example :code:`ti ti-info-circle`.

* If not set, the icon is automatically determined from the alert type.

* :code:`important` **bool**. When ``true``, the alert is visually highlighted. Defaults to ``false``.

Blocks
------

title
^^^^^

Completely overrides the title, including the wrapping ``<h4>`` element.

content
^^^^^^^

Completely overrides the message area.

.. code-block:: twig

<twig:Alert:Danger>
<twig:block name="title">
<h2 class="alert-title">
Custom title block
</h2>
</twig:block>

<div>
My alert content
</div>
</twig:Alert:Danger>

.. image:: /_static/images/symfonycomponents/alert-custom-block.png
:alt: Example with custom twig block

Variants
--------

Pre-typed variant components are available as shortcuts:

.. code-block:: twig

<twig:Alert:Success>Success alert</twig:Alert:Success>
<twig:Alert:Info>Info alert</twig:Alert:Info>
<twig:Alert:Warning>Warning alert</twig:Alert:Warning>
<twig:Alert:Danger>Danger alert</twig:Alert:Danger>

<twig:Alert>Main alert</twig:Alert>
<twig:Alert type="danger">Main alert with type danger</twig:Alert>

.. image:: /_static/images/symfonycomponents/alert-variants.png
:alt: Alert variants
187 changes: 187 additions & 0 deletions source/devapi/symfonycomponents/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
Symfony Twig Components
=======================

.. versionadded:: 12.0

Twig Components is a `Symfony UX bundle <https://symfony.com/bundles/ux-twig-component/current/index.html>`_ that allows configuring components in PHP.

It also enables a cleaner integration with a :code:`Vue.js`-like syntax, making components easier to maintain and review compared to the legacy macro-based integration.

The following components are available:

.. toctree::
:maxdepth: 1

alert

Usage
-----

Twig components support various integration modes. We recommend using the **Component HTML Syntax**.


Component HTML Syntax
^^^^^^^^^^^^^^^^^^^^^

`Symfony Documentation <https://symfony.com/bundles/ux-twig-component/current/index.html#component-html-syntax>`_

.. code-block:: twig

<twig:Alert title="My alert title" messages="My message" />

This syntax resembles modern frontend frameworks.

To pass dynamic values such as variables, booleans, or arrays, prefix the prop name with ``:`` and use a Twig expression:

.. code-block:: twig

<twig:Alert title="Overridden title" :messages="['Message 1', 'Message 2']" type="danger" :important="true">
<twig:block name="title">
<h4 class="alert-title">
Custom title block
</h4>
{{ parent() }} {# Renders the parent content — here: "Overridden title" #}
</twig:block>
</twig:Alert>

Most components also support a default ``content`` block. To inject content into it, place your markup directly inside the ``<twig:xx>`` tag:

.. code-block:: twig

<twig:Alert:Danger>
<twig:block name="title">
<h2 class="alert-title">
Custom title block
</h2>
</twig:block>

<div>
My alert content
</div>
</twig:Alert:Danger>

.. image:: /_static/images/symfonycomponents/alert-custom-block.png
:alt: Example with custom twig block


Component Twig Syntax
^^^^^^^^^^^^^^^^^^^^^

There is also a ``component()`` Twig function, but its use is discouraged except in rare cases. It is less readable and less flexible than the HTML syntax (no block overrides, and it visually blends in with other Twig function calls).

.. code-block:: twig

{{ component('Alert', {
type: 'warning',
title: __('My alert title.')
}) }}

This integration mode will not be shown in the component documentation.

Creating a Component
--------------------

`Symfony Documentation <https://symfony.com/bundles/ux-twig-component/current/index.html>`_

A Twig component consists of two parts: a **PHP class** that declares the props and logic, and a **Twig template** that defines the markup.

The PHP Class
^^^^^^^^^^^^^

Create a class under ``src/Twig/Components/`` and annotate it with ``#[AsTwigComponent]``.

By default, **Public properties** become the component's props and are automatically available as variables in the template.

**Public methods** are also accessible from the template via the special ``this`` variable.

.. code-block:: php

namespace Twig\Components;

use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;

#[AsTwigComponent(name: 'MyComponent', template: 'twig_components/MyComponent.html.twig')]
class MyComponent
{
public string $title = '';
public bool $important = false;

public function getComputedClass(): string
{
return $this->important ? 'text-bold' : '';
}
}

The ``name`` parameter sets the tag name used in templates (``<twig:MyComponent />``). If omitted, it is derived from the class namespace relative to ``Twig\Components``.

The ``template`` parameter is also optional. If omitted, Symfony derives the template path from the class namespace, resolved under ``templates/twig_components/``.

.. note::

For components with multiple variants (e.g., ``Alert:Success``, ``Alert:Danger``), the recommended pattern is to extract shared props and logic into an abstract base class, then create lightweight variant classes that extend it and override the relevant defaults. See ``src/Twig/Components/Alert/`` (`Github <https://github.com/glpi-project/glpi/tree/main/src/Twig/Components/Alert/>`_) for a real-world example.

The Twig Template
^^^^^^^^^^^^^^^^^

Place templates under ``templates/twig_components/``. Props are available directly as template variables. The component object itself is accessible via ``this``, which is useful for calling methods:

.. code-block:: twig

<div class="{{ this.computedClass }}">
{% block title %}
{% if title|length %}
<h4>{{ title }}</h4>
{% endif %}
{% endblock %}

{% block content %}{% endblock %}
</div>

Define ``{% block %}`` sections for any part of the markup that consumers may need to override.

The ``{% block content %}`` block is special: any markup placed directly inside the component tag (without an explicit ``<twig:block>``) is injected into it automatically:

.. code-block:: twig

<twig:Alert>This text is injected into the content block.</twig:Alert>

Variants
^^^^^^^^

Variant components share a base class and, typically, the same template. The class name determines the component tag name: a class ``Twig\Components\Alert\Danger`` automatically resolves to the tag ``<twig:Alert:Danger>``.

.. code-block:: php

namespace Twig\Components\Alert;

use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;

#[AsTwigComponent(template: 'twig_components/Alert/Info.html.twig')]
final class Danger extends AbstractAlert
{
public string $type = 'danger';
}

The ``template`` parameter is specified explicitly here because all Alert variants share a single template file (``twig_components/Alert/Info.html.twig``).

Testing
^^^^^^^

Two levels of tests are recommended:

- **Unit tests**: instantiate the PHP class directly and assert prop defaults and method return values. No GLPI environment needed.
- **Rendering tests**: render a Twig string using ``TemplateRenderer::getInstance()->renderFromStringTemplate()`` and assert the resulting HTML. These extend ``GLPITestCase``.

Tests live in ``tests/functional/Twig/Components/`` (`GitHub <https://github.com/glpi-project/glpi/tree/main/tests/functional/Twig/Components/>`_). See ``AlertTest.php`` and ``AlertRenderingTest.php`` for examples.

Debugging
^^^^^^^^^

To list all registered components and their resolved template paths, run:

.. code-block:: bash

bin/console symfony:debug:twig-component

# Using Makefile
make console c='symfony:debug:twig-component'
4 changes: 4 additions & 0 deletions source/sourcecode.rst
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,11 @@ This is a brief description of GLPI main folders and files:
* |phpfile| `*.php`: Classes

* |folder| `stubs`

* |folder| `templates`: Twig templates files

* |folder| `twig_components`: Symfony Twig Components (`see documentation <devapi/symfonycomponents/index.html>`_)

* |folder| `tests`: unit and integration tests
* |folder| `tools`: a bunch of tools
* |folder| `version`: Current version for internal use
Expand Down