-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
195 additions
and
83 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
src/DependencyInjection/Compiler/StorybookRuntimeLoaderPass.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<?php | ||
|
||
namespace Storybook\DependencyInjection\Compiler; | ||
|
||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; | ||
use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
use Symfony\Component\DependencyInjection\Reference; | ||
use function array_merge; | ||
|
||
/** | ||
* Prepends custom runtime loader in Storybook Twig environment. | ||
* | ||
* @author Nicolas Rigaud <[email protected]> | ||
*/ | ||
final class StorybookRuntimeLoaderPass implements CompilerPassInterface | ||
{ | ||
public function process(ContainerBuilder $container): void | ||
{ | ||
$twig = $container->getDefinition('storybook.twig'); | ||
|
||
$addRuntimeLoaderMethodCall = [ | ||
'addRuntimeLoader', | ||
[new Reference('storybook.twig.runtime_loader')], | ||
]; | ||
|
||
// Prepend the Storybook runtime loader to the default Twig ones | ||
$twig->setMethodCalls(array_merge( | ||
[$addRuntimeLoaderMethodCall], | ||
$twig->getMethodCalls()) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,8 +13,8 @@ | |
use Storybook\Exception\UnauthorizedStoryException; | ||
use Storybook\Mock\ComponentProxyFactory; | ||
use Storybook\StoryRenderer; | ||
use Storybook\Twig\StorybookEnvironment; | ||
use Storybook\Twig\StorybookEnvironmentConfigurator; | ||
use Storybook\Twig\StorybookRuntimeLoader; | ||
use Storybook\Twig\StoryExtension; | ||
use Storybook\Twig\TwigComponentSubscriber; | ||
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; | ||
|
@@ -30,6 +30,7 @@ | |
use Symfony\Component\DependencyInjection\Reference; | ||
use Twig\Extension\SandboxExtension; | ||
use Twig\Sandbox\SecurityPolicy; | ||
use function array_merge_recursive; | ||
|
||
/** | ||
* @author Nicolas Rigaud <[email protected]> | ||
|
@@ -70,6 +71,8 @@ static function (ChildDefinition $definition, AsComponentMock $attributeInstance | |
|
||
$config = (new Processor())->processConfiguration($this, $configs); | ||
|
||
$this->configureStorybookTwigEnvironment($container, $config); | ||
|
||
// Proxy listener | ||
$container->register('storybook.listener.proxy_request', ProxyRequestListener::class) | ||
->addTag('kernel.event_subscriber'); | ||
|
@@ -82,6 +85,39 @@ static function (ChildDefinition $definition, AsComponentMock $attributeInstance | |
; | ||
|
||
// Story renderer | ||
$container->register('storybook.story_renderer', StoryRenderer::class) | ||
->setArgument(0, new Reference('storybook.twig')) | ||
; | ||
|
||
// Args processors | ||
$container->register('storybook.args_processor', StorybookArgsProcessor::class); | ||
|
||
// Proxy factory | ||
$container->register('storybook.component_proxy_factory', ComponentProxyFactory::class) | ||
->setArgument(0, new AbstractArgument(\sprintf('Provided in "%s".', ComponentMockPass::class))); | ||
|
||
// Internal commands | ||
$container->register('storybook.generate_preview_command', GeneratePreviewCommand::class) | ||
->setArgument(0, new Reference('twig')) | ||
->setArgument(1, new Reference('event_dispatcher')) | ||
->addTag('console.command', ['name' => 'storybook:generate-preview']) | ||
; | ||
|
||
// Init command | ||
$container->register('storybook.init_command', StorybookInitCommand::class) | ||
->setArgument(0, $container->getParameter('kernel.project_dir')) | ||
->addTag('console.command', ['name' => 'storybook:init']); | ||
|
||
// Component subscriber | ||
$container->register('storybook.twig.on_pre_render_listener', TwigComponentSubscriber::class) | ||
->setArgument(0, new Reference('request_stack')) | ||
->setArgument(1, new Reference('event_dispatcher')) | ||
->addTag('kernel.event_subscriber'); | ||
} | ||
|
||
private function configureStorybookTwigEnvironment(ContainerBuilder $container, array $config): void | ||
{ | ||
// Sandbox | ||
$defaultSandboxConfig = [ | ||
'allowedTags' => ['component'], | ||
'allowedFunctions' => ['component'], | ||
|
@@ -100,13 +136,16 @@ static function (ChildDefinition $definition, AsComponentMock $attributeInstance | |
->setArgument(4, $sandboxConfig['allowedFunctions']) | ||
; | ||
|
||
// Storybook Twig extensions | ||
// Storybook Twig environment | ||
$container->setDefinition('storybook.twig', new ChildDefinition('twig')) | ||
->setClass(StorybookEnvironment::class) | ||
->addMethodCall('setComponentRuntime', [new Reference('storybook.twig.component_runtime')]) | ||
->setConfigurator([new Reference('storybook.twig.environment_configurator'), 'configure']) | ||
; | ||
|
||
$container->register('storybook.twig.runtime_loader', StorybookRuntimeLoader::class) | ||
->addMethodCall('addRuntime', [new Reference('storybook.twig.component_runtime')]) | ||
; | ||
|
||
|
||
$container->register('storybook.twig.extension.sandbox', SandboxExtension::class) | ||
->setArgument(0, new Reference('storybook.twig.security_policy')) | ||
->addTag('storybook.twig.extension') | ||
|
@@ -123,42 +162,14 @@ static function (ChildDefinition $definition, AsComponentMock $attributeInstance | |
->setArgument(2, $config['cache'] ?? false) | ||
; | ||
|
||
$container->setDefinition('storybook.twig.component_runtime', new ChildDefinition('.ux.twig_component.twig.component_runtime')) | ||
$container->setDefinition('storybook.twig.component_runtime', new ChildDefinition('ux.twig_component.twig.component_runtime')) | ||
->replaceArgument(0, new Reference('storybook.twig.component_renderer')) | ||
; | ||
|
||
$container->setDefinition('storybook.twig.component_renderer', new ChildDefinition('ux.twig_component.component_renderer')) | ||
->replaceArgument(0, new Reference('storybook.twig')) | ||
; | ||
|
||
$container->register('storybook.story_renderer', StoryRenderer::class) | ||
->setArgument(0, new Reference('storybook.twig')) | ||
; | ||
|
||
// Args processors | ||
$container->register('storybook.args_processor', StorybookArgsProcessor::class); | ||
|
||
// Proxy factory | ||
$container->register('storybook.component_proxy_factory', ComponentProxyFactory::class) | ||
->setArgument(0, new AbstractArgument(\sprintf('Provided in "%s".', ComponentMockPass::class))); | ||
|
||
// Internal commands | ||
$container->register('storybook.generate_preview_command', GeneratePreviewCommand::class) | ||
->setArgument(0, new Reference('twig')) | ||
->setArgument(1, new Reference('event_dispatcher')) | ||
->addTag('console.command', ['name' => 'storybook:generate-preview']) | ||
; | ||
|
||
// Init command | ||
$container->register('storybook.init_command', StorybookInitCommand::class) | ||
->setArgument(0, $container->getParameter('kernel.project_dir')) | ||
->addTag('console.command', ['name' => 'storybook:init']); | ||
|
||
// Component subscriber | ||
$container->register('storybook.twig.on_pre_render_listener', TwigComponentSubscriber::class) | ||
->setArgument(0, new Reference('request_stack')) | ||
->setArgument(1, new Reference('event_dispatcher')) | ||
->addTag('kernel.event_subscriber'); | ||
} | ||
|
||
public function getConfigTreeBuilder(): TreeBuilder | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<?php | ||
|
||
namespace Storybook\Twig; | ||
|
||
use InvalidArgumentException; | ||
use Twig\RuntimeLoader\RuntimeLoaderInterface; | ||
|
||
/** | ||
* Runtime loader for custom runtimes that have to be used in a Storybook context. | ||
* | ||
* @author Nicolas Rigaud <[email protected]> | ||
* | ||
* @internal | ||
*/ | ||
final class StorybookRuntimeLoader implements RuntimeLoaderInterface | ||
{ | ||
/** | ||
* @var array<string, object> | ||
*/ | ||
private array $map = []; | ||
|
||
public function load(string $class): ?object | ||
{ | ||
return $this->map[$class] ?? null; | ||
} | ||
|
||
public function addRuntime(object $runtime): void | ||
{ | ||
$class = $runtime::class; | ||
if (isset($this->map[$class])) { | ||
throw new InvalidArgumentException(sprintf('Runtime "%s" is already registered.', $class)); | ||
} | ||
|
||
$this->map[$class] = $runtime; | ||
} | ||
} |
38 changes: 38 additions & 0 deletions
38
tests/Unit/DependencyInjection/Compiler/StorybookRuntimeLoaderPassTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
<?php | ||
|
||
namespace Storybook\Tests\Unit\DependencyInjection\Compiler; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
use Storybook\DependencyInjection\Compiler\StorybookRuntimeLoaderPass; | ||
use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
use Symfony\Component\DependencyInjection\Definition; | ||
use Symfony\Component\DependencyInjection\Reference; | ||
|
||
final class StorybookRuntimeLoaderPassTest extends TestCase | ||
{ | ||
public function testProcessAddsStorybookRuntimeLoaderBeforeTheOriginalOnes() | ||
{ | ||
$container = new ContainerBuilder(); | ||
$twigDefinition = new Definition(); | ||
$container->setDefinition('storybook.twig', $twigDefinition); | ||
|
||
$twigDefinition->addMethodCall( | ||
'addRuntimeLoader', | ||
[new Reference('native_loader')], | ||
); | ||
|
||
$pass = new StorybookRuntimeLoaderPass(); | ||
$pass->process($container); | ||
|
||
$runtimeLoaderCalls = array_filter( | ||
$twigDefinition->getMethodCalls(), | ||
static fn (array $call) => 'addRuntimeLoader' === $call[0], | ||
); | ||
|
||
self::assertCount(2, $runtimeLoaderCalls); | ||
self::assertEquals( | ||
new Reference('storybook.twig.runtime_loader'), | ||
$runtimeLoaderCalls[0][1][0], | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<?php | ||
|
||
namespace Storybook\Tests\Unit\Twig; | ||
|
||
use InvalidArgumentException; | ||
use PHPUnit\Framework\TestCase; | ||
use Storybook\Twig\StorybookRuntimeLoader; | ||
|
||
final class StorybookRuntimeLoaderTest extends TestCase | ||
{ | ||
public function testAddRuntime() | ||
{ | ||
$runtimeLoader = new StorybookRuntimeLoader(); | ||
|
||
$runtime = new DummyRuntime(); | ||
$runtimeLoader->addRuntime($runtime); | ||
|
||
self::assertSame($runtime, $runtimeLoader->load(DummyRuntime::class)); | ||
} | ||
|
||
public function testAddingTheSameRuntimeMultipleTimesThrowsException() | ||
{ | ||
$runtimeLoader = new StorybookRuntimeLoader(); | ||
|
||
$runtime = new DummyRuntime(); | ||
$runtimeLoader->addRuntime($runtime); | ||
|
||
$sameRuntime = new DummyRuntime(); | ||
|
||
$this->expectException(InvalidArgumentException::class); | ||
$runtimeLoader->addRuntime($sameRuntime); | ||
} | ||
} | ||
|
||
final class DummyRuntime | ||
{ | ||
} |