diff --git a/src/DataTables/.gitattributes b/src/DataTables/.gitattributes new file mode 100644 index 00000000000..1ba1a889ab8 --- /dev/null +++ b/src/DataTables/.gitattributes @@ -0,0 +1,8 @@ +/.gitattributes export-ignore +/.gitignore export-ignore +/.symfony.bundle.yaml export-ignore +/assets/src export-ignore +/assets/test export-ignore +/assets/vitest.config.js export-ignore +/phpunit.xml.dist export-ignore +/tests export-ignore diff --git a/src/DataTables/.gitignore b/src/DataTables/.gitignore new file mode 100644 index 00000000000..bb17c3e124c --- /dev/null +++ b/src/DataTables/.gitignore @@ -0,0 +1,4 @@ +/vendor/ +.phpunit.result.cache +.php_cs.cache +composer.lock diff --git a/src/DataTables/LICENSE b/src/DataTables/LICENSE new file mode 100644 index 00000000000..0ed3a246553 --- /dev/null +++ b/src/DataTables/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020-present Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/DataTables/README.md b/src/DataTables/README.md new file mode 100644 index 00000000000..5170680fb93 --- /dev/null +++ b/src/DataTables/README.md @@ -0,0 +1,30 @@ +# Symfony UX DataTables + +Symfony UX DataTables is a Symfony bundle integrating the [DataTables](https://datatables.net/) +library in Symfony applications. It is part of [the Symfony UX initiative](https://ux.symfony.com/). + +**This repository is a READ-ONLY sub-tree split**. See +https://github.com/symfony/ux to create issues or submit pull requests. + +## Sponsor + +The Symfony UX packages are [backed][1] by [Mercure.rocks][2]. + +Create real-time experiences in minutes! Mercure.rocks provides a realtime API service +that is tightly integrated with Symfony: create UIs that update in live with UX Turbo, +send notifications with the Notifier component, expose async APIs with API Platform and +create low level stuffs with the Mercure component. We maintain and scale the complex +infrastructure for you! + +Help Symfony by [sponsoring][3] its development! + +## Resources + +- [Documentation](https://symfony.com/bundles/ux-datatables/current/index.html) +- [Report issues](https://github.com/symfony/ux/issues) and + [send Pull Requests](https://github.com/symfony/ux/pulls) + in the [main Symfony UX repository](https://github.com/symfony/ux) + +[1]: https://symfony.com/backers +[2]: https://mercure.rocks +[3]: https://symfony.com/sponsor diff --git a/src/DataTables/assets/dist/controller.d.ts b/src/DataTables/assets/dist/controller.d.ts new file mode 100644 index 00000000000..9b31f62867d --- /dev/null +++ b/src/DataTables/assets/dist/controller.d.ts @@ -0,0 +1,9 @@ +import { Controller } from '@hotwired/stimulus'; +export default class extends Controller { + readonly viewValue: any; + static values: { + view: ObjectConstructor; + }; + connect(): void; + private dispatchEvent; +} diff --git a/src/DataTables/assets/dist/controller.js b/src/DataTables/assets/dist/controller.js new file mode 100644 index 00000000000..409436c2078 --- /dev/null +++ b/src/DataTables/assets/dist/controller.js @@ -0,0 +1,33 @@ +import { Controller } from '@hotwired/stimulus'; +import DataTable from 'datatables.net-dt'; + +class default_1 extends Controller { + constructor() { + super(...arguments); + this.table = null; + this.isDataTableInitialized = false; + } + connect() { + if (this.isDataTableInitialized) { + return; + } + if (!(this.element instanceof HTMLTableElement)) { + throw new Error('Invalid element'); + } + const payload = this.viewValue; + this.dispatchEvent('pre-connect', { + config: payload, + }); + this.table = new DataTable(this.element, payload); + this.dispatchEvent('connect', { table: this.table }); + this.isDataTableInitialized = true; + } + dispatchEvent(name, payload) { + this.dispatch(name, { detail: payload, prefix: 'datatables' }); + } +} +default_1.values = { + view: Object, +}; + +export { default_1 as default }; diff --git a/src/DataTables/assets/package.json b/src/DataTables/assets/package.json new file mode 100644 index 00000000000..d760d2ee118 --- /dev/null +++ b/src/DataTables/assets/package.json @@ -0,0 +1,33 @@ +{ + "name": "@symfony/ux-datatables", + "description": "DataTables integration for Symfony", + "license": "MIT", + "version": "1.0.0", + "type": "module", + "main": "dist/controller.js", + "symfony": { + "controllers": { + "datatable": { + "main": "dist/controller.js", + "webpackMode": "eager", + "fetch": "eager", + "enabled": true, + "autoimport": { + "datatables.net-dt/css/dataTables.dataTables.min.css": true + } + } + }, + "importmap": { + "@hotwired/stimulus": "^3.0.0", + "datatables.net-dt": "^2.1.5" + } + }, + "peerDependencies": { + "@hotwired/stimulus": "^3.0.0", + "datatables.net-dt": "^2.1.5" + }, + "devDependencies": { + "@hotwired/stimulus": "^3.0.0", + "datatables.net-dt": "^2.1.5" + } +} diff --git a/src/DataTables/assets/src/controller.ts b/src/DataTables/assets/src/controller.ts new file mode 100644 index 00000000000..0a8e7c9ecca --- /dev/null +++ b/src/DataTables/assets/src/controller.ts @@ -0,0 +1,48 @@ +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { Controller } from '@hotwired/stimulus'; +import DataTable from 'datatables.net-dt'; + +export default class extends Controller { + declare readonly viewValue: any; + + static values = { + view: Object, + }; + + private table: DataTable | null = null; + private isDataTableInitialized = false; + + connect() { + if (this.isDataTableInitialized) { + return; + } + + if (!(this.element instanceof HTMLTableElement)) { + throw new Error('Invalid element'); + } + + const payload = this.viewValue; + + this.dispatchEvent('pre-connect', { + config: payload, + }); + + this.table = new DataTable(this.element as HTMLElement, payload); + + this.dispatchEvent('connect', { table: this.table }); + + this.isDataTableInitialized = true; + } + + private dispatchEvent(name: string, payload: any) { + this.dispatch(name, { detail: payload, prefix: 'datatables' }); + } +} diff --git a/src/DataTables/assets/test/controller.test.ts b/src/DataTables/assets/test/controller.test.ts new file mode 100644 index 00000000000..7f506af1f26 --- /dev/null +++ b/src/DataTables/assets/test/controller.test.ts @@ -0,0 +1,83 @@ +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { Application } from '@hotwired/stimulus'; +import { waitFor } from '@testing-library/dom'; +import DataTableController from '../src/controller'; + +const startDataTableTest = async ( + tableHtml: string +): Promise<{ table: HTMLTableElement; dataTable: DataTable }> => { + let dataTable: DataTable | null = null; + + document.body.addEventListener('datatables:pre-connect', () => { + document.body.classList.add('pre-connected'); + }); + + document.body.addEventListener('datatables:connect', (event: any) => { + dataTable = event.detail.table; + document.body.classList.add('connected'); + }); + + document.body.innerHTML = tableHtml; + + const tableElement = document.querySelector('table'); + if (!tableElement) { + throw 'Missing table element'; + } + + await waitFor(() => { + expect(document.body).toHaveClass('pre-connected'); + expect(document.body).toHaveClass('connected'); + }); + + if (!dataTable) { + throw 'Missing DataTable instance'; + } + + return { table: tableElement, dataTable }; +}; + +describe('DataTableController', () => { + beforeAll(() => { + const application = Application.start(); + application.register('datatables', DataTableController); + }); + + afterEach(() => { + document.body.innerHTML = ''; + }); + + it('connect without data', async () => { + const { dataTable } = await startDataTableTest(` +
+ `); + + expect(dataTable.data().toArray()).toEqual([]); + }); + + it('connect with data', async () => { + const { dataTable } = await startDataTableTest(` +
+ `); + + expect(dataTable.data().toArray()).toEqual([ + ['John', 'Doe'], + ['Jane', 'Smith'], + ]); + }); +}); diff --git a/src/DataTables/composer.json b/src/DataTables/composer.json new file mode 100644 index 00000000000..a93d8bea15a --- /dev/null +++ b/src/DataTables/composer.json @@ -0,0 +1,53 @@ +{ + "name": "symfony/ux-datatables", + "type": "symfony-bundle", + "description": "DataTables.net integration for Symfony", + "keywords": [ + "symfony-ux" + ], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Tanguy LemariƩ", + "email": "tanguy.lemarie6@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "autoload": { + "psr-4": { + "Symfony\\UX\\DataTables\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Symfony\\UX\\DataTables\\Tests\\": "tests/" + } + }, + "require": { + "php": ">=8.1", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/stimulus-bundle": "^2.9.1" + }, + "require-dev": { + "symfony/framework-bundle": "^5.4|^6.0|^7.0", + "symfony/phpunit-bridge": "^5.4|^6.0|^7.0", + "symfony/twig-bundle": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "conflict": { + "symfony/flex": "<1.13" + }, + "extra": { + "thanks": { + "name": "symfony/ux", + "url": "https://github.com/symfony/ux" + } + }, + "minimum-stability": "dev" +} diff --git a/src/DataTables/doc/index.rst b/src/DataTables/doc/index.rst new file mode 100644 index 00000000000..dfd5b0b67df --- /dev/null +++ b/src/DataTables/doc/index.rst @@ -0,0 +1,146 @@ +Symfony UX DataTables +===================== + +Symfony UX DataTables is a Symfony bundle integrating the +`DataTables`_ library in Symfony applications. +It is part of `the Symfony UX initiative`_. + +Installation +------------ + +.. caution:: + + Before you start, make sure you have `StimulusBundle configured in your app`_. + +Install the bundle using Composer and Symfony Flex: + +.. code-block:: terminal + + $ composer require symfony/ux-datatables + +If you're using WebpackEncore, install your assets and restart Encore (not +needed if you're using AssetMapper): + +.. code-block:: terminal + + $ npm install --force + $ npm run watch + + # or use yarn + $ yarn install --force + $ yarn watch + +Usage +----- + +To use Symfony UX DataTables, inject the ``DataTableBuilderInterface`` service +and create tables in PHP:: + + // ... + use Symfony\UX\DataTables\Builder\DataTableBuilderInterface; + use Symfony\UX\DataTables\Model\DataTable; + + class HomeController extends AbstractController + { + #[Route('/', name: 'app_homepage')] + public function index(DataTableBuilderInterface $tableBuilder): Response + { + $table = $tableBuilder->createDataTable('usersTable'); + + $table->setData([ + 'columns' => ['First name', 'Last name'], + 'data' => [ + ['John', 'Doe'], + ['Jane', 'Smith'], + ], + ]); + + $table->setOptions([ + 'order' => [ + ['idx' => 1, 'dir' => 'asc'] + ], + ]); + + return $this->render('home/index.html.twig', [ + 'table' => $table, + ]); + } + } + +All options and data are provided as-is to DataTables. You can read +`DataTables documentation`_ to discover them all. + +Once created in PHP, a table can be displayed using Twig: + +.. code-block:: html+twig + + {{ render_datatable(table) }} + + {# You can pass HTML attributes as a second argument to add them on the tag #} + {{ render_datatable(table, {'class': 'my-table'}) }} + +Extend the default behavior +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Symfony UX DataTables allows you to extend its default behavior using a +custom Stimulus controller: + +.. code-block:: javascript + + // mytable_controller.js + + import { Controller } from '@hotwired/stimulus'; + + export default class extends Controller { + connect() { + this.element.addEventListener('datatables:pre-connect', this._onPreConnect); + this.element.addEventListener('datatables:connect', this._onConnect); + } + + disconnect() { + // You should always remove listeners when the controller is disconnected to avoid side effects + this.element.removeEventListener('datatables:pre-connect', this._onPreConnect); + this.element.removeEventListener('datatables:connect', this._onConnect); + } + + _onPreConnect(event) { + // The table is not yet created + // You can access the config that will be passed to "new DataTable()" + console.log(event.detail.config); + + // For instance you can define a render callback for a given column + event.detail.config.columns[0].render = function (data, type, row, meta) { + return 'Download'; + } + } + + _onConnect(event) { + // The table was just created + console.log(event.detail.table); // You can access the table instance using the event details + + // For instance you can listen to additional events + event.detail.table.on('init', (event) => { + /* ... */ + }; + event.detail.table.on('draw', (event) => { + /* ... */ + }; + } + } + +Then in your render call, add your controller as an HTML attribute: + +.. code-block:: twig + + {{ render_datatable(table, {'data-controller': 'mytable'}) }} + +Backward Compatibility promise +------------------------------ + +This bundle aims at following the same Backward Compatibility promise as +the Symfony framework: https://symfony.com/doc/current/contributing/code/bc.html. + +.. _`DataTables`: https://datatables.net +.. _`the Symfony UX initiative`: https://ux.symfony.com/ +.. _`DataTables documentation`: https://datatables.net/manual/ +.. _StimulusBundle configured in your app: https://symfony.com/bundles/StimulusBundle/current/index.html \ No newline at end of file diff --git a/src/DataTables/phpunit.xml.dist b/src/DataTables/phpunit.xml.dist new file mode 100644 index 00000000000..ba15dfc214b --- /dev/null +++ b/src/DataTables/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + tests + + + + + + ./src + + + diff --git a/src/DataTables/src/Builder/DataTableBuilder.php b/src/DataTables/src/Builder/DataTableBuilder.php new file mode 100644 index 00000000000..8bb532b1307 --- /dev/null +++ b/src/DataTables/src/Builder/DataTableBuilder.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\UX\DataTables\Builder; + +use Symfony\UX\DataTables\Model\DataTable; + +class DataTableBuilder implements DataTableBuilderInterface +{ + public function createDataTable(?string $id = null): DataTable + { + return new DataTable($id); + } +} diff --git a/src/DataTables/src/Builder/DataTableBuilderInterface.php b/src/DataTables/src/Builder/DataTableBuilderInterface.php new file mode 100644 index 00000000000..20393d20211 --- /dev/null +++ b/src/DataTables/src/Builder/DataTableBuilderInterface.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\UX\DataTables\Builder; + +use Symfony\UX\DataTables\Model\DataTable; + +interface DataTableBuilderInterface +{ + public function createDataTable(?string $id = null): DataTable; +} diff --git a/src/DataTables/src/DataTablesBundle.php b/src/DataTables/src/DataTablesBundle.php new file mode 100644 index 00000000000..8282e7b880d --- /dev/null +++ b/src/DataTables/src/DataTablesBundle.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\UX\DataTables; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class DataTablesBundle extends Bundle +{ + public function getPath(): string + { + return \dirname(__DIR__); + } +} diff --git a/src/DataTables/src/DependencyInjection/DataTablesExtension.php b/src/DataTables/src/DependencyInjection/DataTablesExtension.php new file mode 100644 index 00000000000..c3bd82f2d0e --- /dev/null +++ b/src/DataTables/src/DependencyInjection/DataTablesExtension.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\UX\DataTables\DependencyInjection; + +use Symfony\Component\AssetMapper\AssetMapperInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Extension\Extension; +use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\UX\DataTables\Builder\DataTableBuilder; +use Symfony\UX\DataTables\Builder\DataTableBuilderInterface; + +class DataTablesExtension extends Extension implements PrependExtensionInterface +{ + public function load(array $configs, ContainerBuilder $container): void + { + $container + ->setDefinition('datatables.builder', new Definition(DataTableBuilder::class)) + ->setPublic(false) + ; + + $container + ->setAlias(DataTableBuilderInterface::class, 'datatables.builder') + ->setPublic(false) + ; + + $container + ->setDefinition('datatables.twig_extension', new Definition(\Symfony\UX\DataTables\Twig\DataTablesExtension::class)) + ->addArgument(new Reference('stimulus.helper')) + ->addTag('twig.extension') + ->setPublic(false) + ; + } + + public function prepend(ContainerBuilder $container): void + { + if (!$this->isAssetMapperAvailable($container)) { + return; + } + + $container->prependExtensionConfig('framework', [ + 'asset_mapper' => [ + 'paths' => [ + __DIR__.'/../../assets/dist' => '@symfony/ux-datatables', + ], + ], + ]); + } + + private function isAssetMapperAvailable(ContainerBuilder $container): bool + { + if (!interface_exists(AssetMapperInterface::class)) { + return false; + } + + // check that FrameworkBundle 6.3 or higher is installed + $bundlesMetadata = $container->getParameter('kernel.bundles_metadata'); + if (!isset($bundlesMetadata['FrameworkBundle'])) { + return false; + } + + return is_file($bundlesMetadata['FrameworkBundle']['path'].'/Resources/config/asset_mapper.php'); + } +} diff --git a/src/DataTables/src/Model/DataTable.php b/src/DataTables/src/Model/DataTable.php new file mode 100644 index 00000000000..5344718c040 --- /dev/null +++ b/src/DataTables/src/Model/DataTable.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\UX\DataTables\Model; + +class DataTable +{ + private readonly ?string $id; + + private array $options = []; + + private array $attributes = []; + + public function __construct(?string $id = null) + { + $this->id = $id; + } + + public function getId(): ?string + { + return $this->id; + } + + public function getOptions(): array + { + return $this->options; + } + + public function setOptions(array $options): static + { + $this->options = $options; + + return $this; + } + + public function getAttributes(): array + { + return $this->attributes; + } + + public function setAttributes(array $attributes): static + { + $this->attributes = $attributes; + + return $this; + } + + public function getDataController(): ?string + { + return $this->attributes['data-controller'] ?? null; + } +} diff --git a/src/DataTables/src/Twig/DataTablesExtension.php b/src/DataTables/src/Twig/DataTablesExtension.php new file mode 100644 index 00000000000..5857279a684 --- /dev/null +++ b/src/DataTables/src/Twig/DataTablesExtension.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\UX\DataTables\Twig; + +use Symfony\UX\DataTables\Model\DataTable; +use Symfony\UX\StimulusBundle\Helper\StimulusHelper; +use Twig\Extension\AbstractExtension; +use Twig\TwigFunction; + +class DataTablesExtension extends AbstractExtension +{ + public function __construct( + private StimulusHelper $stimulus, + ) { + } + + public function getFunctions(): array + { + return [ + new TwigFunction('render_datatable', [$this, 'renderDataTable'], ['is_safe' => ['html']]), + ]; + } + + public function renderDataTable(DataTable $table, array $attributes = []): string + { + $table->setAttributes(array_merge($table->getAttributes(), $attributes)); + + $controllers = []; + if ($table->getDataController()) { + $controllers[$table->getDataController()] = []; + } + $controllers['@symfony/ux-datatables/datatable'] = ['view' => $table->getOptions()]; + + $stimulusAttributes = $this->stimulus->createStimulusAttributes(); + foreach ($controllers as $name => $controllerValues) { + $stimulusAttributes->addController($name, $controllerValues); + } + + foreach ($table->getAttributes() as $name => $value) { + if ('data-controller' === $name) { + continue; + } + + if (true === $value) { + $stimulusAttributes->addAttribute($name, $name); + } elseif (false !== $value) { + $stimulusAttributes->addAttribute($name, $value); + } + } + + return \sprintf('
', $table->getId(), $stimulusAttributes); + } +} diff --git a/src/DataTables/tests/DataTablesBundleTest.php b/src/DataTables/tests/DataTablesBundleTest.php new file mode 100644 index 00000000000..8424a0ae2ed --- /dev/null +++ b/src/DataTables/tests/DataTablesBundleTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\UX\DataTables\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\UX\DataTables\Tests\Kernel\TwigAppKernel; + +class DataTablesBundleTest extends TestCase +{ + public function testBootKernel() + { + $kernel = new TwigAppKernel('test', true); + $kernel->boot(); + $this->assertArrayHasKey('DataTablesBundle', $kernel->getBundles()); + } +} diff --git a/src/DataTables/tests/Kernel/TwigAppKernel.php b/src/DataTables/tests/Kernel/TwigAppKernel.php new file mode 100644 index 00000000000..23cea940fd6 --- /dev/null +++ b/src/DataTables/tests/Kernel/TwigAppKernel.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\UX\DataTables\Tests\Kernel; + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\TwigBundle\TwigBundle; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\UX\DataTables\DataTablesBundle; +use Symfony\UX\StimulusBundle\StimulusBundle; + +class TwigAppKernel extends Kernel +{ + public function registerBundles(): iterable + { + return [new FrameworkBundle(), new TwigBundle(), new StimulusBundle(), new DataTablesBundle()]; + } + + public function registerContainerConfiguration(LoaderInterface $loader): void + { + $loader->load(function (ContainerBuilder $container) { + $container->loadFromExtension('framework', ['secret' => '$ecret', 'test' => true, 'http_method_override' => false]); + $container->loadFromExtension('twig', ['default_path' => __DIR__.'/templates', 'strict_variables' => true, 'exception_controller' => null]); + + $container->setAlias('test.datatables.builder', 'datatables.builder')->setPublic(true); + $container->setAlias('test.datatables.twig_extension', 'datatables.twig_extension')->setPublic(true); + }); + } +} diff --git a/src/DataTables/tests/Twig/DataTablesExtensionTest.php b/src/DataTables/tests/Twig/DataTablesExtensionTest.php new file mode 100644 index 00000000000..4ad40940922 --- /dev/null +++ b/src/DataTables/tests/Twig/DataTablesExtensionTest.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\UX\DataTables\Tests\Twig; + +use PHPUnit\Framework\TestCase; +use Symfony\UX\DataTables\Builder\DataTableBuilderInterface; +use Symfony\UX\DataTables\Tests\Kernel\TwigAppKernel; + +class DataTablesExtensionTest extends TestCase +{ + public function testRenderDataTable(): void + { + $kernel = new TwigAppKernel('test', true); + $kernel->boot(); + $container = $kernel->getContainer()->get('test.service_container'); + + /** @var DataTableBuilderInterface $builder */ + $builder = $container->get('test.datatables.builder'); + + $table = $builder->createDataTable('table'); + + $table->setOptions([ + 'columns' => [ + ['title' => 'Column 1'], + ['title' => 'Column 2'], + ], + 'data' => [ + ['Row 1 Column 1', 'Row 1 Column 2'], + ['Row 2 Column 1', 'Row 2 Column 2'], + ], + ]); + + $rendered = $container->get('test.datatables.twig_extension')->renderDataTable( + $table, + ['data-controller' => 'mycontroller', 'class' => 'myclass'] + ); + + $this->assertSame( + expected: '
', + actual: $rendered + ); + } +}