Skip to content

Commit 7ab92db

Browse files
committed
feature #161 Adding the "tabs" directive (weaverryan)
This PR was squashed before being merged into the main branch. Discussion ---------- Adding the "tabs" directive Hi! This adds a "tabs" directive, based off of https://sphinx-tabs.readthedocs.io/en/latest/ Example: ```rst .. tabs:: UX Installation .. tab:: Webpack Encore Webpack Encore stuff! .. code-block:: yaml I am yaml: .. tab:: AssetMapper AssetMapper stuff! And another paragraph. ``` This will render (with a little assistance from Javier) just like a "configuration block", except that you can put any content inside. My use-case is for some UX-related items where install steps will now be different if you're using Encore vs AssetMapper, and I think a tabbed setup will be better than 2 sections right after each other. Feedback appreciated! Commits ------- 200f906 Adding the "tabs" directive
2 parents faf2c5f + 200f906 commit 7ab92db

File tree

9 files changed

+185
-1
lines changed

9 files changed

+185
-1
lines changed

src/Directive/ConfigurationBlockDirective.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public function processSub(Parser $parser, ?Node $document, string $variable, st
6666
'directives/configuration-block.html.twig',
6767
[
6868
'blocks' => $blocks,
69+
'title' => 'Configuration formats',
6970
]
7071
);
7172

src/Directive/TabDirective.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Docs Builder package.
5+
* (c) Ryan Weaver <ryan@symfonycasts.com>
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
namespace SymfonyDocsBuilder\Directive;
11+
12+
use Doctrine\RST\Directives\SubDirective;
13+
use Doctrine\RST\Nodes\Node;
14+
use Doctrine\RST\Parser;
15+
use SymfonyDocsBuilder\Node\TabNode;
16+
17+
/**
18+
* Directive that only appears within the "tabs" directive.
19+
*/
20+
class TabDirective extends SubDirective
21+
{
22+
public function getName(): string
23+
{
24+
return 'tab';
25+
}
26+
27+
public function processSub(Parser $parser, ?Node $document, string $variable, string $data, array $options): ?Node
28+
{
29+
$tabName = $data;
30+
if (!$tabName) {
31+
throw new \RuntimeException(sprintf('The "tab" directive requires a tab name: ".. tab:: Tab Name".'));
32+
}
33+
34+
return new TabNode($document->getNodes(), $data);
35+
}
36+
}

src/Directive/TabsDirective.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Docs Builder package.
5+
* (c) Ryan Weaver <ryan@symfonycasts.com>
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
namespace SymfonyDocsBuilder\Directive;
11+
12+
use Doctrine\RST\Directives\SubDirective;
13+
use Doctrine\RST\Nodes\Node;
14+
use Doctrine\RST\Parser;
15+
use SymfonyDocsBuilder\Node\TabNode;
16+
17+
class TabsDirective extends SubDirective
18+
{
19+
public function getName(): string
20+
{
21+
return 'tabs';
22+
}
23+
24+
public function processSub(Parser $parser, ?Node $document, string $variable, string $data, array $options): ?Node
25+
{
26+
$tabsTitle = $data;
27+
if (!$tabsTitle) {
28+
throw new \RuntimeException(sprintf('The "tabs" directive requires a title: ".. tabs:: Title".'));
29+
}
30+
31+
$blocks = [];
32+
foreach ($document->getNodes() as $tabNode) {
33+
if (!$tabNode instanceof TabNode) {
34+
throw new \RuntimeException(sprintf('Only ".. tab::" content can appear within the "tabs" directive.'));
35+
}
36+
37+
$content = '';
38+
foreach ($tabNode->getNodes() as $node) {
39+
$content .= $node->render();
40+
}
41+
42+
$blocks[] = [
43+
'hash' => hash('sha1', $tabNode->getTabName()),
44+
'language_label' => $tabNode->getTabName(),
45+
'language' => $tabNode->getSluggedTabName(),
46+
'code' => $content,
47+
];
48+
}
49+
50+
$wrapperDiv = $parser->renderTemplate(
51+
'directives/configuration-block.html.twig',
52+
[
53+
'blocks' => $blocks,
54+
'title' => $tabsTitle,
55+
]
56+
);
57+
58+
return $parser->getNodeFactory()->createWrapperNode(null, $wrapperDiv, '</div>');
59+
}
60+
}

src/KernelFactory.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ private static function getDirectives(): array
9191
new SymfonyDirectives\ScreencastDirective(),
9292
new SymfonyDirectives\SeeAlsoDirective(),
9393
new SymfonyDirectives\SidebarDirective(),
94+
new SymfonyDirectives\TabDirective(),
95+
new SymfonyDirectives\TabsDirective(),
9496
new SymfonyDirectives\TipDirective(),
9597
new SymfonyDirectives\TopicDirective(),
9698
new SymfonyDirectives\WarningDirective(),

src/Node/TabNode.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
namespace SymfonyDocsBuilder\Node;
4+
5+
use Doctrine\RST\Nodes\Node;
6+
7+
/**
8+
* Wraps nodes + options in a TabDirective.
9+
*/
10+
class TabNode extends Node
11+
{
12+
/**
13+
* @var Node[]
14+
*/
15+
private array $nodes;
16+
17+
private string $tabName;
18+
19+
public function __construct(array $nodes, string $tabName)
20+
{
21+
$this->nodes = $nodes;
22+
$this->tabName = $tabName;
23+
24+
parent::__construct();
25+
}
26+
27+
public function getNodes(): array
28+
{
29+
return $this->nodes;
30+
}
31+
32+
public function getTabName(): string
33+
{
34+
return $this->tabName;
35+
}
36+
37+
public function getSluggedTabName(): string
38+
{
39+
return strtolower(str_replace(' ', '-', $this->tabName));
40+
}
41+
}

src/Templates/default/html/directives/configuration-block.html.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<div class="configuration-block">
2-
<div role="tablist" aria-label="Configuration formats" class="configuration-tabs configuration-tabs-length-{{ blocks|length }}">
2+
<div role="tablist" aria-label="{{ title }}" class="configuration-tabs configuration-tabs-length-{{ blocks|length }}">
33
{% for block in blocks %}
44
<button role="tab" type="button" data-language="{{ block.language }}"
55
aria-controls="{{ 'configuration-block-tabpanel-' ~ block.hash }}" aria-selected="{{ loop.first ? 'true' : 'false' }}"

tests/IntegrationTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@ public function parserUnitBlockProvider()
206206
'blockName' => 'directives/sidebar-code-block-nested',
207207
];
208208

209+
yield 'tabs' => [
210+
'blockName' => 'directives/tabs',
211+
];
212+
209213
yield 'class-reference' => [
210214
'blockName' => 'references/class',
211215
];
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<div class="configuration-block">
2+
<div role="tablist" aria-label="UX Installation" class="configuration-tabs configuration-tabs-length-2">
3+
<button role="tab" type="button" data-language="webpack-encore" aria-controls="configuration-block-tabpanel-97a055193fa707f2025ee25bb469c74aa29b2c6c" aria-selected="true" data-active="true">
4+
<span>Webpack Encore</span>
5+
</button>
6+
<button role="tab" type="button" data-language="assetmapper" aria-controls="configuration-block-tabpanel-1865de6b2b585de79e8700d7968050722e562c44" aria-selected="false" tabindex="-1">
7+
<span>AssetMapper</span>
8+
</button>
9+
</div>
10+
11+
<div role="tabpanel" id="configuration-block-tabpanel-97a055193fa707f2025ee25bb469c74aa29b2c6c" aria-label="Webpack Encore" class="configuration-codeblock" data-language="webpack-encore" style="">
12+
<p>Webpack Encore stuff!</p>
13+
<div translate="no" data-loc="1" class="notranslate codeblock codeblock-length-sm codeblock-yaml">
14+
<div class="codeblock-scroll">
15+
<pre class="codeblock-lines">1</pre>
16+
<pre class="codeblock-code"><code><span class="hljs-attr">I am yaml:</span></code></pre>
17+
</div>
18+
</div>
19+
</div>
20+
<div role="tabpanel" id="configuration-block-tabpanel-1865de6b2b585de79e8700d7968050722e562c44" aria-label="AssetMapper" class="configuration-codeblock" data-language="assetmapper" style="display: none">
21+
<p>AssetMapper stuff!</p>
22+
<p>And another paragraph.</p>
23+
</div>
24+
</div>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
.. tabs:: UX Installation
3+
4+
.. tab:: Webpack Encore
5+
6+
Webpack Encore stuff!
7+
8+
.. code-block:: yaml
9+
10+
I am yaml:
11+
12+
.. tab:: AssetMapper
13+
14+
AssetMapper stuff!
15+
16+
And another paragraph.

0 commit comments

Comments
 (0)