From 8c46bf3d2ec69a4e48ed73a8f68c059204883c03 Mon Sep 17 00:00:00 2001 From: Dimitar Date: Thu, 24 Oct 2024 11:15:36 +0300 Subject: [PATCH] Add support for custom actions in `TurboStream` and `TurboStreamResponse` --- src/Turbo/CHANGELOG.md | 3 +++ src/Turbo/src/Helper/TurboStream.php | 28 ++++++++++++++++++++++ src/Turbo/src/TurboStreamResponse.php | 12 ++++++++++ src/Turbo/tests/Helper/TurboStreamTest.php | 28 ++++++++++++++++++++++ 4 files changed, 71 insertions(+) diff --git a/src/Turbo/CHANGELOG.md b/src/Turbo/CHANGELOG.md index 872b94307bb..5fa306778d8 100644 --- a/src/Turbo/CHANGELOG.md +++ b/src/Turbo/CHANGELOG.md @@ -1,4 +1,7 @@ # CHANGELOG +## 2.22.0 + +- Add support for custom actions in `TurboStream` and `TurboStreamResponse` ## 2.22.0 diff --git a/src/Turbo/src/Helper/TurboStream.php b/src/Turbo/src/Helper/TurboStream.php index daea084e6ab..d3ecdc5b79c 100644 --- a/src/Turbo/src/Helper/TurboStream.php +++ b/src/Turbo/src/Helper/TurboStream.php @@ -86,6 +86,34 @@ public static function refresh(?string $requestId = null): string return \sprintf('', htmlspecialchars($requestId)); } + /** + * Custom action and attributes. + * + * Set boolean attributes (e.g., `disabled`) by providing the attribute name as key with `null` as value. + * + * @param array $attr + */ + public static function action(string $action, string $target, string $html, array $attr = []): string + { + if (\array_key_exists('action', $attr) || \array_key_exists('targets', $attr)) { + throw new \InvalidArgumentException('The "action" and "targets" attributes are reserved and cannot be used.'); + } + + $attrString = ''; + foreach ($attr as $key => $value) { + $key = htmlspecialchars($key); + if (null === $value) { + $attrString .= \sprintf(' %s', $key); + } elseif (\is_int($value) || \is_float($value)) { + $attrString .= \sprintf(' %s="%s"', $key, $value); + } else { + $attrString .= \sprintf(' %s="%s"', $key, htmlspecialchars($value)); + } + } + + return self::wrap(htmlspecialchars($action), $target, $html, $attrString); + } + private static function wrap(string $action, string $target, string $html, string $attr = ''): string { return \sprintf(<< $attr + * + * @return $this + */ + public function action(string $action, string $target, string $html, array $attr = []): static + { + $this->setContent($this->getContent().TurboStream::action($action, $target, $html, $attr)); + + return $this; + } } diff --git a/src/Turbo/tests/Helper/TurboStreamTest.php b/src/Turbo/tests/Helper/TurboStreamTest.php index 263e454effd..b404dca2ac8 100644 --- a/src/Turbo/tests/Helper/TurboStreamTest.php +++ b/src/Turbo/tests/Helper/TurboStreamTest.php @@ -76,4 +76,32 @@ public function testRefreshWithId(): void TurboStream::refresh('a"b') ); } + + public function testCustom(): void + { + $this->assertSame(<< + + + EOHTML, + TurboStream::action('customAction', 'some["selector"]', '
content
', ['someAttr' => 'someValue', 'boolAttr' => null, 'intAttr' => 0, 'floatAttr' => 3.14]) + ); + } + + /** + * @dataProvider customThrowsExceptionDataProvider + * + * @param array $attr + */ + public function testCustomThrowsException(string $action, string $target, string $html, array $attr): void + { + $this->expectException(\InvalidArgumentException::class); + TurboStream::action($action, $target, $html, $attr); + } + + public static function customThrowsExceptionDataProvider(): \Generator + { + yield ['customAction', 'some["selector"]', '
content
', ['action' => 'someAction']]; + yield ['customAction', 'some["selector"]', '
content
', ['targets' => 'someTargets']]; + } }