From aff4388b7d3c786d3e2ec34742b035f1308ac188 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 18:50:10 +0100 Subject: [PATCH 01/99] Create HeadingRenderer.php --- .../Markdown/Processing/HeadingRenderer.php | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 packages/framework/src/Markdown/Processing/HeadingRenderer.php diff --git a/packages/framework/src/Markdown/Processing/HeadingRenderer.php b/packages/framework/src/Markdown/Processing/HeadingRenderer.php new file mode 100644 index 00000000000..7edce1b1209 --- /dev/null +++ b/packages/framework/src/Markdown/Processing/HeadingRenderer.php @@ -0,0 +1,32 @@ +renderNodes($node->children()); + + return view('hyde::components.markdown-heading', [ + 'level' => $node->getLevel(), + 'slot' => $content, + ])->render(); + } +} From ed7f281675da62922d181ce7d59621d5730b987a Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 18:50:14 +0100 Subject: [PATCH 02/99] Create markdown-heading.blade.php --- .../views/components/markdown-heading.blade.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 packages/framework/resources/views/components/markdown-heading.blade.php diff --git a/packages/framework/resources/views/components/markdown-heading.blade.php b/packages/framework/resources/views/components/markdown-heading.blade.php new file mode 100644 index 00000000000..5994c137dfa --- /dev/null +++ b/packages/framework/resources/views/components/markdown-heading.blade.php @@ -0,0 +1,13 @@ +@props(['level' => 1, 'id' => null]) + +@php + $tag = 'h' . $level; + $id = $id ?? \Illuminate\Support\Str::slug($slot); +@endphp + +<{{ $tag }} {{ $attributes->merge(['id' => $id]) }}> +{{ $slot }} +@if(config('markdown.features.permalinks', true)) + +@endif + \ No newline at end of file From 7e452976c4ee92e0aafe64fe001c69c399614f9a Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 18:50:22 +0100 Subject: [PATCH 03/99] Render slot literally --- .../resources/views/components/markdown-heading.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/resources/views/components/markdown-heading.blade.php b/packages/framework/resources/views/components/markdown-heading.blade.php index 5994c137dfa..12c7ab7df21 100644 --- a/packages/framework/resources/views/components/markdown-heading.blade.php +++ b/packages/framework/resources/views/components/markdown-heading.blade.php @@ -6,7 +6,7 @@ @endphp <{{ $tag }} {{ $attributes->merge(['id' => $id]) }}> -{{ $slot }} +{!! $slot !!} @if(config('markdown.features.permalinks', true)) @endif From 5607c6d63b8f90b5fe568ee361f5acbff15bf441 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 18:50:41 +0100 Subject: [PATCH 04/99] Indent code --- .../resources/views/components/markdown-heading.blade.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/framework/resources/views/components/markdown-heading.blade.php b/packages/framework/resources/views/components/markdown-heading.blade.php index 12c7ab7df21..7af1dd8ab8c 100644 --- a/packages/framework/resources/views/components/markdown-heading.blade.php +++ b/packages/framework/resources/views/components/markdown-heading.blade.php @@ -6,8 +6,8 @@ @endphp <{{ $tag }} {{ $attributes->merge(['id' => $id]) }}> -{!! $slot !!} -@if(config('markdown.features.permalinks', true)) - -@endif + {!! $slot !!} + @if(config('markdown.features.permalinks', true)) + + @endif \ No newline at end of file From ec6b2c6b120ce4d37a12d23772f410779c7c3dcb Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 19:02:03 +0100 Subject: [PATCH 05/99] Add an attribute for controlling permalink state --- .../resources/views/components/markdown-heading.blade.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/framework/resources/views/components/markdown-heading.blade.php b/packages/framework/resources/views/components/markdown-heading.blade.php index 7af1dd8ab8c..5b7bf42759a 100644 --- a/packages/framework/resources/views/components/markdown-heading.blade.php +++ b/packages/framework/resources/views/components/markdown-heading.blade.php @@ -1,4 +1,4 @@ -@props(['level' => 1, 'id' => null]) +@props(['level' => 1, 'id' => null, 'addPermalink' => config('markdown.features.permalinks', true)]) @php $tag = 'h' . $level; @@ -7,7 +7,7 @@ <{{ $tag }} {{ $attributes->merge(['id' => $id]) }}> {!! $slot !!} - @if(config('markdown.features.permalinks', true)) + @if($addPermalink === true) @endif \ No newline at end of file From 98ddab10f67f0818c03d7db8e977f0eccc556ff3 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 19:02:30 +0100 Subject: [PATCH 06/99] Set state from class --- packages/framework/src/Markdown/Processing/HeadingRenderer.php | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/framework/src/Markdown/Processing/HeadingRenderer.php b/packages/framework/src/Markdown/Processing/HeadingRenderer.php index 7edce1b1209..ac02180733e 100644 --- a/packages/framework/src/Markdown/Processing/HeadingRenderer.php +++ b/packages/framework/src/Markdown/Processing/HeadingRenderer.php @@ -27,6 +27,7 @@ public function render(Node $node, ChildNodeRendererInterface $childRenderer): s return view('hyde::components.markdown-heading', [ 'level' => $node->getLevel(), 'slot' => $content, + 'addPermalink' => config('markdown.features.permalinks', true), ])->render(); } } From e1ccda8fdd2f414637390fdab799952f88b5675b Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 19:02:40 +0100 Subject: [PATCH 07/99] Fix recursion issue --- packages/framework/src/Markdown/Processing/HeadingRenderer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/src/Markdown/Processing/HeadingRenderer.php b/packages/framework/src/Markdown/Processing/HeadingRenderer.php index ac02180733e..e68528f3f24 100644 --- a/packages/framework/src/Markdown/Processing/HeadingRenderer.php +++ b/packages/framework/src/Markdown/Processing/HeadingRenderer.php @@ -27,7 +27,7 @@ public function render(Node $node, ChildNodeRendererInterface $childRenderer): s return view('hyde::components.markdown-heading', [ 'level' => $node->getLevel(), 'slot' => $content, - 'addPermalink' => config('markdown.features.permalinks', true), + 'addPermalink' => config('markdown.features.permalinks', true) && ! str_contains($content, 'class="heading-permalink"'), ])->render(); } } From 24e459fc780cc5a6001c4411e11eb38b0d0ebbf0 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 19:22:11 +0100 Subject: [PATCH 08/99] Support extra attributes --- .../resources/views/components/markdown-heading.blade.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/framework/resources/views/components/markdown-heading.blade.php b/packages/framework/resources/views/components/markdown-heading.blade.php index 5b7bf42759a..6e87150fa0b 100644 --- a/packages/framework/resources/views/components/markdown-heading.blade.php +++ b/packages/framework/resources/views/components/markdown-heading.blade.php @@ -1,11 +1,11 @@ -@props(['level' => 1, 'id' => null, 'addPermalink' => config('markdown.features.permalinks', true)]) +@props(['level' => 1, 'id' => null, 'addPermalink' => config('markdown.features.permalinks', true), 'extraAttributes' => []]) @php $tag = 'h' . $level; $id = $id ?? \Illuminate\Support\Str::slug($slot); @endphp -<{{ $tag }} {{ $attributes->merge(['id' => $id]) }}> +<{{ $tag }} {{ $attributes->merge(['id' => $id, ...$extraAttributes]) }}> {!! $slot !!} @if($addPermalink === true) From 2c3e07b78c12d5003a88ebaedbfec54da3f388c9 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 19:22:18 +0100 Subject: [PATCH 09/99] Forward node attributes --- packages/framework/src/Markdown/Processing/HeadingRenderer.php | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/framework/src/Markdown/Processing/HeadingRenderer.php b/packages/framework/src/Markdown/Processing/HeadingRenderer.php index e68528f3f24..97d1789d35e 100644 --- a/packages/framework/src/Markdown/Processing/HeadingRenderer.php +++ b/packages/framework/src/Markdown/Processing/HeadingRenderer.php @@ -28,6 +28,7 @@ public function render(Node $node, ChildNodeRendererInterface $childRenderer): s 'level' => $node->getLevel(), 'slot' => $content, 'addPermalink' => config('markdown.features.permalinks', true) && ! str_contains($content, 'class="heading-permalink"'), + 'extraAttributes' => $node->data->get('attributes') ])->render(); } } From 6631d8689000fc12eb7ad775fdda83c444c61c64 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 19:22:47 +0100 Subject: [PATCH 10/99] Format props --- .../resources/views/components/markdown-heading.blade.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/framework/resources/views/components/markdown-heading.blade.php b/packages/framework/resources/views/components/markdown-heading.blade.php index 6e87150fa0b..f2f72c26954 100644 --- a/packages/framework/resources/views/components/markdown-heading.blade.php +++ b/packages/framework/resources/views/components/markdown-heading.blade.php @@ -1,4 +1,9 @@ -@props(['level' => 1, 'id' => null, 'addPermalink' => config('markdown.features.permalinks', true), 'extraAttributes' => []]) +@props([ + 'level' => 1, + 'id' => null, + 'extraAttributes' => [], + 'addPermalink' => config('markdown.features.permalinks', true), +]) @php $tag = 'h' . $level; From 07d5e97aee166e1f86c5f1447af9e6b16c549df7 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 19:27:03 +0100 Subject: [PATCH 11/99] Update added config setting --- .../resources/views/components/markdown-heading.blade.php | 2 +- packages/framework/src/Markdown/Processing/HeadingRenderer.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/framework/resources/views/components/markdown-heading.blade.php b/packages/framework/resources/views/components/markdown-heading.blade.php index f2f72c26954..67ca05c66a5 100644 --- a/packages/framework/resources/views/components/markdown-heading.blade.php +++ b/packages/framework/resources/views/components/markdown-heading.blade.php @@ -2,7 +2,7 @@ 'level' => 1, 'id' => null, 'extraAttributes' => [], - 'addPermalink' => config('markdown.features.permalinks', true), + 'addPermalink' => config('markdown.permalinks.enabled', true), ]) @php diff --git a/packages/framework/src/Markdown/Processing/HeadingRenderer.php b/packages/framework/src/Markdown/Processing/HeadingRenderer.php index 97d1789d35e..6440c09b074 100644 --- a/packages/framework/src/Markdown/Processing/HeadingRenderer.php +++ b/packages/framework/src/Markdown/Processing/HeadingRenderer.php @@ -27,7 +27,7 @@ public function render(Node $node, ChildNodeRendererInterface $childRenderer): s return view('hyde::components.markdown-heading', [ 'level' => $node->getLevel(), 'slot' => $content, - 'addPermalink' => config('markdown.features.permalinks', true) && ! str_contains($content, 'class="heading-permalink"'), + 'addPermalink' => config('markdown.permalinks.enabled', true) && ! str_contains($content, 'class="heading-permalink"'), 'extraAttributes' => $node->data->get('attributes') ])->render(); } From c430b9bcb4ae27b04bbdba6d06689058a1e40b7f Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Sun, 1 Dec 2024 18:27:49 +0000 Subject: [PATCH 12/99] Apply fixes from StyleCI --- packages/framework/src/Markdown/Processing/HeadingRenderer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/src/Markdown/Processing/HeadingRenderer.php b/packages/framework/src/Markdown/Processing/HeadingRenderer.php index 6440c09b074..6182cb9922e 100644 --- a/packages/framework/src/Markdown/Processing/HeadingRenderer.php +++ b/packages/framework/src/Markdown/Processing/HeadingRenderer.php @@ -28,7 +28,7 @@ public function render(Node $node, ChildNodeRendererInterface $childRenderer): s 'level' => $node->getLevel(), 'slot' => $content, 'addPermalink' => config('markdown.permalinks.enabled', true) && ! str_contains($content, 'class="heading-permalink"'), - 'extraAttributes' => $node->data->get('attributes') + 'extraAttributes' => $node->data->get('attributes'), ])->render(); } } From b0a4ef923a1198f1863a44ae881e5181560313e9 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 19:27:40 +0100 Subject: [PATCH 13/99] Register the custom heading renderer --- .../src/Framework/Services/MarkdownService.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/framework/src/Framework/Services/MarkdownService.php b/packages/framework/src/Framework/Services/MarkdownService.php index 9eb8bc8fc79..c3ccf42388a 100644 --- a/packages/framework/src/Framework/Services/MarkdownService.php +++ b/packages/framework/src/Framework/Services/MarkdownService.php @@ -7,6 +7,7 @@ use Hyde\Facades\Config; use Hyde\Facades\Features; use Hyde\Markdown\Models\MarkdownDocument; +use Hyde\Markdown\Processing\HeadingRenderer; use Hyde\Framework\Concerns\Internal\SetsUpMarkdownConverter; use Hyde\Pages\DocumentationPage; use Hyde\Markdown\MarkdownConverter; @@ -14,6 +15,7 @@ use Hyde\Markdown\Contracts\MarkdownPostProcessorContract as PostProcessor; use League\CommonMark\Extension\HeadingPermalink\HeadingPermalinkExtension; use League\CommonMark\Extension\DisallowedRawHtml\DisallowedRawHtmlExtension; +use League\CommonMark\Extension\CommonMark\Node\Block\Heading; use function str_contains; use function str_replace; @@ -87,6 +89,8 @@ protected function setupConverter(): void $this->initializeExtension($extension); } + $this->configureCustomHeadingRenderer(); + $this->registerPreProcessors(); $this->registerPostProcessors(); } @@ -272,4 +276,10 @@ protected static function findLineContentPositions(array $lines): array return [0, 0]; } + + protected function configureCustomHeadingRenderer(): void + { + $environment = $this->converter->getEnvironment(); + $environment->addRenderer(Heading::class, new HeadingRenderer()); + } } From 87f03f7acd6da73aefdd50b2a30b2281d0fe8266 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 19:29:45 +0100 Subject: [PATCH 14/99] Extract helper method --- .../framework/src/Markdown/Processing/HeadingRenderer.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/framework/src/Markdown/Processing/HeadingRenderer.php b/packages/framework/src/Markdown/Processing/HeadingRenderer.php index 6182cb9922e..6d31b653a84 100644 --- a/packages/framework/src/Markdown/Processing/HeadingRenderer.php +++ b/packages/framework/src/Markdown/Processing/HeadingRenderer.php @@ -27,8 +27,13 @@ public function render(Node $node, ChildNodeRendererInterface $childRenderer): s return view('hyde::components.markdown-heading', [ 'level' => $node->getLevel(), 'slot' => $content, - 'addPermalink' => config('markdown.permalinks.enabled', true) && ! str_contains($content, 'class="heading-permalink"'), + 'addPermalink' => $this->canAddPermalink($content), 'extraAttributes' => $node->data->get('attributes'), ])->render(); } + + protected function canAddPermalink(string $content): bool + { + return config('markdown.permalinks.enabled', true) && ! str_contains($content, 'class="heading-permalink"'); + } } From 585764db303e0d47dc02c7e0f19a6c338d2937d4 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 19:29:58 +0100 Subject: [PATCH 15/99] Create heading permalinks configuration --- config/markdown.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/config/markdown.php b/config/markdown.php index 0ec940714ab..07365a78799 100644 --- a/config/markdown.php +++ b/config/markdown.php @@ -95,4 +95,23 @@ */ 'prose_classes' => 'prose dark:prose-invert', + + /* + |-------------------------------------------------------------------------- + | Heading Permalinks Configuration + |-------------------------------------------------------------------------- + | + | Here you can specify which page classes should have heading permalinks. + | By default, only documentation pages have permalinks enabled, but you + | are free to enable it for any kind of page by adding the page class. + | + */ + + 'permalinks' => [ + 'enabled' => true, + 'page_classes' => [ + \Hyde\Pages\DocumentationPage::class, + ], + ], + ]; From bfc93e3939b50dc4f8fa4e5c9c1260cbe8487cde Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 19:32:56 +0100 Subject: [PATCH 16/99] Better option name --- config/markdown.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/markdown.php b/config/markdown.php index 07365a78799..7e8d142084f 100644 --- a/config/markdown.php +++ b/config/markdown.php @@ -109,7 +109,7 @@ 'permalinks' => [ 'enabled' => true, - 'page_classes' => [ + 'pages' => [ \Hyde\Pages\DocumentationPage::class, ], ], From cb6c70e3a887b647c7212fcd1ba4ff11bb37c82c Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 19:33:06 +0100 Subject: [PATCH 17/99] Construct with page class --- .../framework/src/Framework/Services/MarkdownService.php | 2 +- .../src/Markdown/Processing/HeadingRenderer.php | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/framework/src/Framework/Services/MarkdownService.php b/packages/framework/src/Framework/Services/MarkdownService.php index c3ccf42388a..cf61708457e 100644 --- a/packages/framework/src/Framework/Services/MarkdownService.php +++ b/packages/framework/src/Framework/Services/MarkdownService.php @@ -280,6 +280,6 @@ protected static function findLineContentPositions(array $lines): array protected function configureCustomHeadingRenderer(): void { $environment = $this->converter->getEnvironment(); - $environment->addRenderer(Heading::class, new HeadingRenderer()); + $environment->addRenderer(Heading::class, new HeadingRenderer($this->pageClass)); } } diff --git a/packages/framework/src/Markdown/Processing/HeadingRenderer.php b/packages/framework/src/Markdown/Processing/HeadingRenderer.php index 6d31b653a84..e218bab86f1 100644 --- a/packages/framework/src/Markdown/Processing/HeadingRenderer.php +++ b/packages/framework/src/Markdown/Processing/HeadingRenderer.php @@ -16,6 +16,15 @@ */ class HeadingRenderer implements NodeRendererInterface { + /** @var class-string<\Hyde\Pages\Concerns\HydePage> */ + protected string $pageClass; + + /** @param class-string<\Hyde\Pages\Concerns\HydePage> $pageClass */ + public function __construct(string $pageClass) + { + $this->pageClass = $pageClass; + } + public function render(Node $node, ChildNodeRendererInterface $childRenderer): string { if (! ($node instanceof Heading)) { From 94cada0cd548150d9be2cdba4f89fe6b138813f9 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 19:35:32 +0100 Subject: [PATCH 18/99] Add permalinks only for configured pages --- packages/framework/src/Markdown/Processing/HeadingRenderer.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/framework/src/Markdown/Processing/HeadingRenderer.php b/packages/framework/src/Markdown/Processing/HeadingRenderer.php index e218bab86f1..62c8f18d51d 100644 --- a/packages/framework/src/Markdown/Processing/HeadingRenderer.php +++ b/packages/framework/src/Markdown/Processing/HeadingRenderer.php @@ -4,6 +4,7 @@ namespace Hyde\Markdown\Processing; +use Hyde\Pages\DocumentationPage; use League\CommonMark\Extension\CommonMark\Node\Block\Heading; use League\CommonMark\Node\Node; use League\CommonMark\Renderer\ChildNodeRendererInterface; @@ -43,6 +44,6 @@ public function render(Node $node, ChildNodeRendererInterface $childRenderer): s protected function canAddPermalink(string $content): bool { - return config('markdown.permalinks.enabled', true) && ! str_contains($content, 'class="heading-permalink"'); + return config('markdown.permalinks.enabled', true) && ! str_contains($content, 'class="heading-permalink"') && in_array($this->pageClass, config('markdown.permalinks.pages', [DocumentationPage::class])); } } From bcf5cddde41cde644ba566d99c48fbdad0944b40 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 19:41:04 +0100 Subject: [PATCH 19/99] Format long line --- .../framework/src/Markdown/Processing/HeadingRenderer.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/framework/src/Markdown/Processing/HeadingRenderer.php b/packages/framework/src/Markdown/Processing/HeadingRenderer.php index 62c8f18d51d..a9bd051cdba 100644 --- a/packages/framework/src/Markdown/Processing/HeadingRenderer.php +++ b/packages/framework/src/Markdown/Processing/HeadingRenderer.php @@ -44,6 +44,8 @@ public function render(Node $node, ChildNodeRendererInterface $childRenderer): s protected function canAddPermalink(string $content): bool { - return config('markdown.permalinks.enabled', true) && ! str_contains($content, 'class="heading-permalink"') && in_array($this->pageClass, config('markdown.permalinks.pages', [DocumentationPage::class])); + return config('markdown.permalinks.enabled', true) + && ! str_contains($content, 'class="heading-permalink"') + && in_array($this->pageClass, config('markdown.permalinks.pages', [DocumentationPage::class])); } } From 22d30c56264e705593e21f8e259ee92d9a1bd514 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 19:41:41 +0100 Subject: [PATCH 20/99] Nullable class string --- .../framework/src/Markdown/Processing/HeadingRenderer.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/framework/src/Markdown/Processing/HeadingRenderer.php b/packages/framework/src/Markdown/Processing/HeadingRenderer.php index a9bd051cdba..f21e79bb3da 100644 --- a/packages/framework/src/Markdown/Processing/HeadingRenderer.php +++ b/packages/framework/src/Markdown/Processing/HeadingRenderer.php @@ -17,11 +17,11 @@ */ class HeadingRenderer implements NodeRendererInterface { - /** @var class-string<\Hyde\Pages\Concerns\HydePage> */ - protected string $pageClass; + /** @var ?class-string<\Hyde\Pages\Concerns\HydePage> */ + protected ?string $pageClass = null; - /** @param class-string<\Hyde\Pages\Concerns\HydePage> $pageClass */ - public function __construct(string $pageClass) + /** @param ?class-string<\Hyde\Pages\Concerns\HydePage> $pageClass */ + public function __construct(string $pageClass = null) { $this->pageClass = $pageClass; } From 6fbb65620f3b441877c4d59742d25484f90db14b Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 19:42:07 +0100 Subject: [PATCH 21/99] Remove auto-configuration for HeadingPermalinkExtension --- .../Internal/SetsUpMarkdownConverter.php | 4 ---- .../src/Framework/Services/MarkdownService.php | 17 ----------------- 2 files changed, 21 deletions(-) diff --git a/packages/framework/src/Framework/Concerns/Internal/SetsUpMarkdownConverter.php b/packages/framework/src/Framework/Concerns/Internal/SetsUpMarkdownConverter.php index 307c2caa81d..73dc6ab4a1d 100644 --- a/packages/framework/src/Framework/Concerns/Internal/SetsUpMarkdownConverter.php +++ b/packages/framework/src/Framework/Concerns/Internal/SetsUpMarkdownConverter.php @@ -25,10 +25,6 @@ trait SetsUpMarkdownConverter { protected function enableDynamicExtensions(): void { - if ($this->canEnablePermalinks()) { - $this->configurePermalinksExtension(); - } - if ($this->canEnableTorchlight()) { $this->addExtension(TorchlightExtension::class); } diff --git a/packages/framework/src/Framework/Services/MarkdownService.php b/packages/framework/src/Framework/Services/MarkdownService.php index cf61708457e..c7b799a48dc 100644 --- a/packages/framework/src/Framework/Services/MarkdownService.php +++ b/packages/framework/src/Framework/Services/MarkdownService.php @@ -13,7 +13,6 @@ use Hyde\Markdown\MarkdownConverter; use Hyde\Markdown\Contracts\MarkdownPreProcessorContract as PreProcessor; use Hyde\Markdown\Contracts\MarkdownPostProcessorContract as PostProcessor; -use League\CommonMark\Extension\HeadingPermalink\HeadingPermalinkExtension; use League\CommonMark\Extension\DisallowedRawHtml\DisallowedRawHtmlExtension; use League\CommonMark\Extension\CommonMark\Node\Block\Heading; @@ -204,22 +203,6 @@ protected function injectTorchlightAttribution(): string )); } - protected function configurePermalinksExtension(): void - { - $this->addExtension(HeadingPermalinkExtension::class); - - $this->config = array_merge([ - 'heading_permalink' => [ - 'id_prefix' => '', - 'fragment_prefix' => '', - 'symbol' => '', - 'insert' => 'after', - 'min_heading_level' => 2, - 'aria_hidden' => false, - ], - ], $this->config); - } - protected function enableAllHtmlElements(): void { $this->addExtension(DisallowedRawHtmlExtension::class); From b7f0509d8a308867821811a7e00cf6f099e7f4c8 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 20:10:30 +0100 Subject: [PATCH 22/99] Change component to match markup made by extension --- .../resources/views/components/markdown-heading.blade.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/framework/resources/views/components/markdown-heading.blade.php b/packages/framework/resources/views/components/markdown-heading.blade.php index 67ca05c66a5..a0dec20101f 100644 --- a/packages/framework/resources/views/components/markdown-heading.blade.php +++ b/packages/framework/resources/views/components/markdown-heading.blade.php @@ -10,9 +10,9 @@ $id = $id ?? \Illuminate\Support\Str::slug($slot); @endphp -<{{ $tag }} {{ $attributes->merge(['id' => $id, ...$extraAttributes]) }}> +<{{ $tag }} {{ $attributes->merge([...$extraAttributes]) }}> {!! $slot !!} @if($addPermalink === true) - + @endif \ No newline at end of file From 539a8ebcaaefe1ce26de26df88800f7dfe92a418 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 20:14:49 +0100 Subject: [PATCH 23/99] Use more explicit assertions --- .../Feature/Services/HydeSmartDocsTest.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/framework/tests/Feature/Services/HydeSmartDocsTest.php b/packages/framework/tests/Feature/Services/HydeSmartDocsTest.php index cdf2540614a..e22136b9b57 100644 --- a/packages/framework/tests/Feature/Services/HydeSmartDocsTest.php +++ b/packages/framework/tests/Feature/Services/HydeSmartDocsTest.php @@ -18,32 +18,32 @@ public function testClassTokenizesDocument() { $article = $this->makeArticle("# Header Content \n\n Body Content"); - $this->assertEquals('

Header Content

', $article->renderHeader()); - $this->assertEquals('

Body Content

', $article->renderBody()); + $this->assertSame('

Header Content

', $article->renderHeader()->toHtml()); + $this->assertSame('

Body Content

', $article->renderBody()->toHtml()); } public function testClassCanHandleDocumentWithNoHeader() { $article = $this->makeArticle('Body Content'); - $this->assertEquals('', $article->renderHeader()); - $this->assertEquals('

Body Content

', $article->renderBody()); + $this->assertSame('', $article->renderHeader()->toHtml()); + $this->assertSame('

Body Content

', $article->renderBody()->toHtml()); } public function testClassCanHandleDocumentWithOnlyHeader() { $article = $this->makeArticle('# Header Content'); - $this->assertEquals('

Header Content

', $article->renderHeader()); - $this->assertEquals('', $article->renderBody()); + $this->assertSame('

Header Content

', $article->renderHeader()->toHtml()); + $this->assertSame('', $article->renderBody()->toHtml()); } public function testClassCanHandleEmptyDocument() { $article = $this->makeArticle(''); - $this->assertEquals('', $article->renderHeader()); - $this->assertEquals('', $article->renderBody()); + $this->assertSame('', $article->renderHeader()->toHtml()); + $this->assertSame('', $article->renderBody()->toHtml()); } public function testRenderedContentIsHtmlable() @@ -61,7 +61,7 @@ public function testCreateHelperCreatesNewInstanceAndProcessesIt() $this->assertInstanceOf(SemanticDocumentationArticle::class, $article); - $this->assertEquals('

Hello world.

', $article->renderBody()); + $this->assertSame('

Hello world.

', $article->renderBody()->toHtml()); } public function testRenderHeaderReturnsTheExtractedHeader() From 53b158fc83e4b75889a99c1d525c88b2229ea3d2 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 20:19:46 +0100 Subject: [PATCH 24/99] Make the permalink headings level configurable --- .../framework/src/Markdown/Processing/HeadingRenderer.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/framework/src/Markdown/Processing/HeadingRenderer.php b/packages/framework/src/Markdown/Processing/HeadingRenderer.php index f21e79bb3da..3fef190e219 100644 --- a/packages/framework/src/Markdown/Processing/HeadingRenderer.php +++ b/packages/framework/src/Markdown/Processing/HeadingRenderer.php @@ -37,14 +37,16 @@ public function render(Node $node, ChildNodeRendererInterface $childRenderer): s return view('hyde::components.markdown-heading', [ 'level' => $node->getLevel(), 'slot' => $content, - 'addPermalink' => $this->canAddPermalink($content), + 'addPermalink' => $this->canAddPermalink($content, $node->getLevel()), 'extraAttributes' => $node->data->get('attributes'), ])->render(); } - protected function canAddPermalink(string $content): bool + protected function canAddPermalink(string $content, int $level): bool { return config('markdown.permalinks.enabled', true) + && $level >= config('markdown.permalinks.min_level', 1) + && $level <= config('markdown.permalinks.max_level', 6) && ! str_contains($content, 'class="heading-permalink"') && in_array($this->pageClass, config('markdown.permalinks.pages', [DocumentationPage::class])); } From bb9fd0aca40c3ae6f94644ed7abe09b9ec74da83 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 20:20:00 +0100 Subject: [PATCH 25/99] Default to minimum level of 2 --- packages/framework/src/Markdown/Processing/HeadingRenderer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/src/Markdown/Processing/HeadingRenderer.php b/packages/framework/src/Markdown/Processing/HeadingRenderer.php index 3fef190e219..682aaeca011 100644 --- a/packages/framework/src/Markdown/Processing/HeadingRenderer.php +++ b/packages/framework/src/Markdown/Processing/HeadingRenderer.php @@ -45,7 +45,7 @@ public function render(Node $node, ChildNodeRendererInterface $childRenderer): s protected function canAddPermalink(string $content, int $level): bool { return config('markdown.permalinks.enabled', true) - && $level >= config('markdown.permalinks.min_level', 1) + && $level >= config('markdown.permalinks.min_level', 2) && $level <= config('markdown.permalinks.max_level', 6) && ! str_contains($content, 'class="heading-permalink"') && in_array($this->pageClass, config('markdown.permalinks.pages', [DocumentationPage::class])); From d8896ad16bc8b4e292a9d3bbc61a649f52e00d9a Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 20:22:32 +0100 Subject: [PATCH 26/99] Introduce local variable --- .../framework/src/Markdown/Processing/HeadingRenderer.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/framework/src/Markdown/Processing/HeadingRenderer.php b/packages/framework/src/Markdown/Processing/HeadingRenderer.php index 682aaeca011..52e5d42cb50 100644 --- a/packages/framework/src/Markdown/Processing/HeadingRenderer.php +++ b/packages/framework/src/Markdown/Processing/HeadingRenderer.php @@ -34,12 +34,14 @@ public function render(Node $node, ChildNodeRendererInterface $childRenderer): s $content = $childRenderer->renderNodes($node->children()); - return view('hyde::components.markdown-heading', [ + $rendered = view('hyde::components.markdown-heading', [ 'level' => $node->getLevel(), 'slot' => $content, 'addPermalink' => $this->canAddPermalink($content, $node->getLevel()), 'extraAttributes' => $node->data->get('attributes'), ])->render(); + + return $rendered; } protected function canAddPermalink(string $content, int $level): bool From 2c5396345f38758736b72211b6013e2e25b110c7 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 20:27:28 +0100 Subject: [PATCH 27/99] Post process to normalize result to CommonMark implementation --- .../src/Markdown/Processing/HeadingRenderer.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/framework/src/Markdown/Processing/HeadingRenderer.php b/packages/framework/src/Markdown/Processing/HeadingRenderer.php index 52e5d42cb50..a6a71ae150d 100644 --- a/packages/framework/src/Markdown/Processing/HeadingRenderer.php +++ b/packages/framework/src/Markdown/Processing/HeadingRenderer.php @@ -41,7 +41,7 @@ public function render(Node $node, ChildNodeRendererInterface $childRenderer): s 'extraAttributes' => $node->data->get('attributes'), ])->render(); - return $rendered; + return $this->postProcess($rendered); } protected function canAddPermalink(string $content, int $level): bool @@ -52,4 +52,16 @@ protected function canAddPermalink(string $content, int $level): bool && ! str_contains($content, 'class="heading-permalink"') && in_array($this->pageClass, config('markdown.permalinks.pages', [DocumentationPage::class])); } + + protected function postProcess(string $html): string + { + $html = str_replace('

', '

', $html); + $html = str_replace('

', '

', $html); + $html = str_replace('

', '

', $html); + $html = str_replace('

', '

', $html); + $html = str_replace('

', '
', $html); + $html = str_replace('
', '
', $html); + + return implode('', array_map('trim', explode("\n", $html))); + } } From 358c7996680f233091fa973a5add3ae3de82cd9a Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 20:28:06 +0100 Subject: [PATCH 28/99] Replace multiple replacements with single Regex --- .../framework/src/Markdown/Processing/HeadingRenderer.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/framework/src/Markdown/Processing/HeadingRenderer.php b/packages/framework/src/Markdown/Processing/HeadingRenderer.php index a6a71ae150d..40fdbbffbc3 100644 --- a/packages/framework/src/Markdown/Processing/HeadingRenderer.php +++ b/packages/framework/src/Markdown/Processing/HeadingRenderer.php @@ -55,12 +55,7 @@ protected function canAddPermalink(string $content, int $level): bool protected function postProcess(string $html): string { - $html = str_replace('

', '

', $html); - $html = str_replace('

', '

', $html); - $html = str_replace('

', '

', $html); - $html = str_replace('

', '

', $html); - $html = str_replace('

', '
', $html); - $html = str_replace('
', '
', $html); + $html = preg_replace('//', '', $html); return implode('', array_map('trim', explode("\n", $html))); } From cc8cc6d5b57b5683d58018cb1fce2c1588934a1e Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 20:32:20 +0100 Subject: [PATCH 29/99] Add a test for when heading permalinks are disabled --- .../tests/Feature/Services/HydeSmartDocsTest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/framework/tests/Feature/Services/HydeSmartDocsTest.php b/packages/framework/tests/Feature/Services/HydeSmartDocsTest.php index e22136b9b57..9648ce4b941 100644 --- a/packages/framework/tests/Feature/Services/HydeSmartDocsTest.php +++ b/packages/framework/tests/Feature/Services/HydeSmartDocsTest.php @@ -214,6 +214,16 @@ public function renderHeader(): HtmlString $this->assertStringContainsString('

Hello world.

', $rendered); } + public function testClassCanParseDocumentWithDisabledPermalinks() + { + config(['markdown.permalinks.enabled' => false]); + + $article = $this->makeArticle("# Header Content \n\n Body Content"); + + $this->assertSame('

Header Content

', $article->renderHeader()->toHtml()); + $this->assertSame('

Body Content

', $article->renderBody()->toHtml()); + } + protected function makeArticle(string $sourceFileContents = "# Foo\n\nHello world."): SemanticDocumentationArticle { $this->file('_docs/foo.md', $sourceFileContents); From 2475010f63053af3a956a804fe9ed989fe14df7a Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 20:36:18 +0100 Subject: [PATCH 30/99] Sync configuration files --- packages/framework/config/markdown.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/framework/config/markdown.php b/packages/framework/config/markdown.php index 0ec940714ab..7e8d142084f 100644 --- a/packages/framework/config/markdown.php +++ b/packages/framework/config/markdown.php @@ -95,4 +95,23 @@ */ 'prose_classes' => 'prose dark:prose-invert', + + /* + |-------------------------------------------------------------------------- + | Heading Permalinks Configuration + |-------------------------------------------------------------------------- + | + | Here you can specify which page classes should have heading permalinks. + | By default, only documentation pages have permalinks enabled, but you + | are free to enable it for any kind of page by adding the page class. + | + */ + + 'permalinks' => [ + 'enabled' => true, + 'pages' => [ + \Hyde\Pages\DocumentationPage::class, + ], + ], + ]; From 30469f429822eb2f0982ecdd27e149497783aedc Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 21:22:09 +0100 Subject: [PATCH 31/99] Mock the View factory --- .../framework/tests/Unit/IncludesFacadeUnitTest.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/framework/tests/Unit/IncludesFacadeUnitTest.php b/packages/framework/tests/Unit/IncludesFacadeUnitTest.php index 17f982db1de..7e0d6244a76 100644 --- a/packages/framework/tests/Unit/IncludesFacadeUnitTest.php +++ b/packages/framework/tests/Unit/IncludesFacadeUnitTest.php @@ -4,6 +4,7 @@ namespace Hyde\Framework\Testing\Unit; +use Illuminate\Contracts\View\Factory; use Mockery; use Closure; use Hyde\Hyde; @@ -34,6 +35,16 @@ protected function setUp(): void { Blade::swap(Mockery::mock()); + // Mock the View factory + $viewFactory = Mockery::mock(Factory::class); + $viewFactory->shouldReceive('make') + ->with('hyde::components.markdown-heading', Mockery::any()) + ->andReturn(Mockery::mock([ + 'render' => '

Mocked Heading

' + ])); + + app()->instance('view', $viewFactory); + $this->setupTestKernel()->setRoutes(collect()); } From a6bcaa18bfb09d83c12b565c5b738a0cc5baa7fe Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 21:23:11 +0100 Subject: [PATCH 32/99] Fix mock bindings --- packages/framework/tests/Unit/IncludesFacadeUnitTest.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/framework/tests/Unit/IncludesFacadeUnitTest.php b/packages/framework/tests/Unit/IncludesFacadeUnitTest.php index 7e0d6244a76..05da1556e13 100644 --- a/packages/framework/tests/Unit/IncludesFacadeUnitTest.php +++ b/packages/framework/tests/Unit/IncludesFacadeUnitTest.php @@ -4,7 +4,6 @@ namespace Hyde\Framework\Testing\Unit; -use Illuminate\Contracts\View\Factory; use Mockery; use Closure; use Hyde\Hyde; @@ -35,8 +34,7 @@ protected function setUp(): void { Blade::swap(Mockery::mock()); - // Mock the View factory - $viewFactory = Mockery::mock(Factory::class); + $viewFactory = Mockery::mock(\Illuminate\Contracts\View\Factory::class); $viewFactory->shouldReceive('make') ->with('hyde::components.markdown-heading', Mockery::any()) ->andReturn(Mockery::mock([ @@ -44,6 +42,7 @@ protected function setUp(): void ])); app()->instance('view', $viewFactory); + app()->instance(\Illuminate\Contracts\View\Factory::class, $viewFactory); $this->setupTestKernel()->setRoutes(collect()); } From 567b505977f7d7a08af43ab6547873460e451bed Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 21:37:00 +0100 Subject: [PATCH 33/99] Granular mocks --- .../tests/Unit/IncludesFacadeUnitTest.php | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/packages/framework/tests/Unit/IncludesFacadeUnitTest.php b/packages/framework/tests/Unit/IncludesFacadeUnitTest.php index 05da1556e13..37e830b33fe 100644 --- a/packages/framework/tests/Unit/IncludesFacadeUnitTest.php +++ b/packages/framework/tests/Unit/IncludesFacadeUnitTest.php @@ -4,6 +4,7 @@ namespace Hyde\Framework\Testing\Unit; +use Illuminate\Contracts\View\Factory; use Mockery; use Closure; use Hyde\Hyde; @@ -34,16 +35,6 @@ protected function setUp(): void { Blade::swap(Mockery::mock()); - $viewFactory = Mockery::mock(\Illuminate\Contracts\View\Factory::class); - $viewFactory->shouldReceive('make') - ->with('hyde::components.markdown-heading', Mockery::any()) - ->andReturn(Mockery::mock([ - 'render' => '

Mocked Heading

' - ])); - - app()->instance('view', $viewFactory); - app()->instance(\Illuminate\Contracts\View\Factory::class, $viewFactory); - $this->setupTestKernel()->setRoutes(collect()); } @@ -160,6 +151,8 @@ public function testMarkdownReturnsRenderedPartial() $filesystem->shouldReceive('get')->with($this->includesPath($filename))->andReturn($content); }); + $this->mockViewFactory($expected); + $this->assertHtmlStringIsSame($expected, Includes::markdown($filename)); } @@ -173,6 +166,8 @@ public function testMarkdownReturnsRenderedDefaultValueWhenNotFound() $filesystem->shouldReceive('exists')->with($this->includesPath($filename))->andReturn(false); }); + $this->mockViewFactory($expected); + $this->assertNull(Includes::markdown($filename)); $this->assertHtmlStringIsSame($expected, Includes::markdown($filename, $default)); } @@ -189,6 +184,8 @@ public function testMarkdownWithAndWithoutExtension() $filesystem->shouldReceive('get')->with($this->includesPath($filename))->andReturn($content); }); + $this->mockViewFactory($expected); + $this->assertHtmlStringIsSame($expected, Includes::markdown('foo.md')); $this->assertHtmlStringIsSame(Includes::markdown('foo.md'), Includes::markdown('foo')); $this->assertHtmlStringIsSame(Includes::markdown('foo.md'), Includes::markdown('foo.md')); @@ -252,6 +249,18 @@ protected function mockFilesystemFromClosure(Closure $config): void app()->instance(Filesystem::class, $filesystem); } + protected function mockViewFactory(string $result): void + { + $viewFactory = Mockery::mock(Factory::class); + $viewFactory->shouldReceive('make') + ->andReturn(Mockery::mock([ + 'render' => $result, + ]))->byDefault(); + + app()->instance('view', $viewFactory); + app()->instance(Factory::class, $viewFactory); + } + protected function includesPath(string $filename): string { return Hyde::path('resources/includes/'.$filename); From 22a7745f0a60e8a0b5898abef11b2e1a5e8ebf03 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 21:44:27 +0100 Subject: [PATCH 34/99] Update test to test the automatic permalinks feature --- packages/framework/tests/Feature/MarkdownServiceTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/tests/Feature/MarkdownServiceTest.php b/packages/framework/tests/Feature/MarkdownServiceTest.php index 3f8823f7d64..ad33b21372e 100644 --- a/packages/framework/tests/Feature/MarkdownServiceTest.php +++ b/packages/framework/tests/Feature/MarkdownServiceTest.php @@ -29,7 +29,7 @@ public function testServiceCanParseMarkdownToHtmlWithPermalinks() { $markdown = '## Hello World!'; - $html = (new MarkdownService($markdown))->withPermalinks()->parse(); + $html = (new MarkdownService($markdown, DocumentationPage::class))->parse(); $this->assertIsString($html); $this->assertSame( From 1100719e57f7244387e0a9b84bf66b5735f884ce Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 21:48:23 +0100 Subject: [PATCH 35/99] Remove the `MarkdownService::withPermalinks` method See https://github.com/hydephp/develop/pull/2047#issuecomment-2510243423 --- RELEASE_NOTES.md | 1 + .../src/Framework/Services/MarkdownService.php | 7 ------- .../tests/Feature/MarkdownServiceTest.php | 16 ---------------- 3 files changed, 1 insertion(+), 23 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index fe8f2f7fb88..eeb595d630b 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -132,6 +132,7 @@ This serves two purposes: - This also removes the `` and `` Blade components, replaced by the new `` component. - Removed the `.torchlight-enabled` CSS class in https://github.com/hydephp/develop/pull/2036. - Removed The `hyde.css` file from HydeFront in https://github.com/hydephp/develop/pull/2037 as all styles were refactored to Tailwind in https://github.com/hydephp/develop/pull/2024. +- Removed the `MarkdownService::withPermalinks` method in https://github.com/hydephp/develop/pull/2047 ### Fixed diff --git a/packages/framework/src/Framework/Services/MarkdownService.php b/packages/framework/src/Framework/Services/MarkdownService.php index c7b799a48dc..a5eb175ca7d 100644 --- a/packages/framework/src/Framework/Services/MarkdownService.php +++ b/packages/framework/src/Framework/Services/MarkdownService.php @@ -144,13 +144,6 @@ public function addFeature(string $feature): static return $this; } - public function withPermalinks(): static - { - $this->addFeature('permalinks'); - - return $this; - } - public function isDocumentationPage(): bool { return isset($this->pageClass) && $this->pageClass === DocumentationPage::class; diff --git a/packages/framework/tests/Feature/MarkdownServiceTest.php b/packages/framework/tests/Feature/MarkdownServiceTest.php index ad33b21372e..a5ba860dbc6 100644 --- a/packages/framework/tests/Feature/MarkdownServiceTest.php +++ b/packages/framework/tests/Feature/MarkdownServiceTest.php @@ -157,14 +157,6 @@ public function testMethodWithTableOfContentsMethodChainAddsTheTableOfContentsFe $this->assertContains('table-of-contents', $service->features); } - public function testMethodWithPermalinksMethodChainAddsThePermalinksFeature() - { - $service = $this->makeService(); - - $service->withPermalinks(); - $this->assertContains('permalinks', $service->features); - } - public function testHasFeatureReturnsTrueIfTheFeatureIsInTheArray() { $service = $this->makeService(); @@ -180,14 +172,6 @@ public function testHasFeatureReturnsFalseIfTheFeatureIsNotInTheArray() $this->assertFalse($service->hasFeature('test')); } - public function testMethodCanEnablePermalinksReturnsTrueIfThePermalinksFeatureIsInTheArray() - { - $service = $this->makeService(); - - $service->addFeature('permalinks'); - $this->assertTrue($service->canEnablePermalinks()); - } - public function testMethodCanEnablePermalinksIsAutomaticallyForDocumentationPages() { $service = new MarkdownServiceTestClass(pageClass: DocumentationPage::class); From d03dbf303f1d077ee7f9847ad998100c2c51700f Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 21:52:02 +0100 Subject: [PATCH 36/99] Add todo --- packages/framework/src/Markdown/Processing/HeadingRenderer.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/framework/src/Markdown/Processing/HeadingRenderer.php b/packages/framework/src/Markdown/Processing/HeadingRenderer.php index 40fdbbffbc3..29e03175d9c 100644 --- a/packages/framework/src/Markdown/Processing/HeadingRenderer.php +++ b/packages/framework/src/Markdown/Processing/HeadingRenderer.php @@ -14,6 +14,8 @@ * Renders a heading node, and supports built-in permalink generation. * * @see \League\CommonMark\Extension\CommonMark\Renderer\Block\HeadingRenderer + * + * @todo Add more options to customize the permalink. */ class HeadingRenderer implements NodeRendererInterface { From 9311bd1971dba26b35a6e1426bd0d46867ee29d5 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 21:59:07 +0100 Subject: [PATCH 37/99] Create HeadingRendererUnitTest.php --- .../tests/Unit/HeadingRendererUnitTest.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 packages/framework/tests/Unit/HeadingRendererUnitTest.php diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php new file mode 100644 index 00000000000..7ccc77f4aed --- /dev/null +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -0,0 +1,17 @@ + Date: Sun, 1 Dec 2024 21:59:29 +0100 Subject: [PATCH 38/99] Create MarkdownHeadingRendererTest.php --- .../Feature/MarkdownHeadingRendererTest.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 packages/framework/tests/Feature/MarkdownHeadingRendererTest.php diff --git a/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php b/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php new file mode 100644 index 00000000000..11b5075f75e --- /dev/null +++ b/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php @@ -0,0 +1,17 @@ + Date: Sun, 1 Dec 2024 22:34:33 +0100 Subject: [PATCH 39/99] Create a minimal view environment --- .../tests/Unit/HeadingRendererUnitTest.php | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index 7ccc77f4aed..5aa4fb1d382 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -5,6 +5,12 @@ namespace Hyde\Framework\Testing\Unit; use Hyde\Testing\UnitTestCase; +use Illuminate\Contracts\View\Factory as FactoryContract; +use Illuminate\Events\Dispatcher; +use Illuminate\View\Engines\EngineResolver; +use Illuminate\View\Factory; +use Illuminate\View\FileViewFinder; +use Illuminate\Filesystem\Filesystem; /** * @covers \Hyde\Markdown\Processing\HeadingRenderer @@ -13,5 +19,19 @@ */ class HeadingRendererUnitTest extends UnitTestCase { - // + protected function setUp(): void + { + // Create a minimal view environment without the full service provider + $view = new Factory( + new EngineResolver(), + new FileViewFinder( + new Filesystem(), + [realpath(__DIR__ . '/../../resources/views')] + ), + new Dispatcher() + ); + + app()->instance('view', $view); + app()->instance(FactoryContract::class, $view); + } } From c7eb6bbcd94c2eb8d60e7185a65883c525e8c730 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 22:41:25 +0100 Subject: [PATCH 40/99] Register finder path --- packages/framework/tests/Unit/HeadingRendererUnitTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index 5aa4fb1d382..aa1ba677be0 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -24,13 +24,15 @@ protected function setUp(): void // Create a minimal view environment without the full service provider $view = new Factory( new EngineResolver(), - new FileViewFinder( + $finder = new FileViewFinder( new Filesystem(), [realpath(__DIR__ . '/../../resources/views')] ), new Dispatcher() ); + $finder->addNamespace('hyde', realpath(__DIR__ . '/../../resources/views')); + app()->instance('view', $view); app()->instance(FactoryContract::class, $view); } From bad9eb30af1234096f4ebb6bd5b0e21a0f89d922 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 22:42:42 +0100 Subject: [PATCH 41/99] Create and configure the engine resolver --- .../tests/Unit/HeadingRendererUnitTest.php | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index aa1ba677be0..cf7b1f8d06e 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -7,6 +7,8 @@ use Hyde\Testing\UnitTestCase; use Illuminate\Contracts\View\Factory as FactoryContract; use Illuminate\Events\Dispatcher; +use Illuminate\View\Compilers\BladeCompiler; +use Illuminate\View\Engines\CompilerEngine; use Illuminate\View\Engines\EngineResolver; use Illuminate\View\Factory; use Illuminate\View\FileViewFinder; @@ -22,10 +24,23 @@ class HeadingRendererUnitTest extends UnitTestCase protected function setUp(): void { // Create a minimal view environment without the full service provider + + // Create and configure the engine resolver + $resolver = new EngineResolver(); + $filesystem = new Filesystem(); + + // Register the blade engine + $resolver->register('blade', function () use ($filesystem) { + return new CompilerEngine( + new BladeCompiler($filesystem, sys_get_temp_dir()) + ); + }); + + // Create the view factory with the configured resolver $view = new Factory( - new EngineResolver(), + $resolver, $finder = new FileViewFinder( - new Filesystem(), + $filesystem, [realpath(__DIR__ . '/../../resources/views')] ), new Dispatcher() From fd6df97f1c2edcb3311c7a32621fa5daf2bf8413 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 22:43:49 +0100 Subject: [PATCH 42/99] Extract helper method --- .../framework/tests/Unit/HeadingRendererUnitTest.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index cf7b1f8d06e..247c278bcc5 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -23,12 +23,15 @@ class HeadingRendererUnitTest extends UnitTestCase { protected function setUp(): void { - // Create a minimal view environment without the full service provider - + $this->createRealBladeCompilerEnvironment(); + } + + protected function createRealBladeCompilerEnvironment(): void + { // Create and configure the engine resolver $resolver = new EngineResolver(); $filesystem = new Filesystem(); - + // Register the blade engine $resolver->register('blade', function () use ($filesystem) { return new CompilerEngine( From 4995016fe0627b41aa20d81264538e8c03717629 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 22:49:44 +0100 Subject: [PATCH 43/99] Basic constructor tests --- .../tests/Unit/HeadingRendererUnitTest.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index 247c278bcc5..d51307fbef9 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -11,6 +11,8 @@ use Illuminate\View\Engines\CompilerEngine; use Illuminate\View\Engines\EngineResolver; use Illuminate\View\Factory; +use Hyde\Markdown\Processing\HeadingRenderer; +use Hyde\Pages\DocumentationPage; use Illuminate\View\FileViewFinder; use Illuminate\Filesystem\Filesystem; @@ -26,6 +28,18 @@ protected function setUp(): void $this->createRealBladeCompilerEnvironment(); } + public function testCanConstruct() + { + $renderer = new HeadingRenderer(DocumentationPage::class); + $this->assertInstanceOf(HeadingRenderer::class, $renderer); + } + + public function testCanConstructWithPageClass() + { + $renderer = new HeadingRenderer(DocumentationPage::class); + $this->assertInstanceOf(HeadingRenderer::class, $renderer); + } + protected function createRealBladeCompilerEnvironment(): void { // Create and configure the engine resolver From a13f1e8ab6af3724a9ca789e35345f006f17cb4a Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 22:55:09 +0100 Subject: [PATCH 44/99] Cleanup helper method code --- .../tests/Unit/HeadingRendererUnitTest.php | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index d51307fbef9..bbecaab5a1a 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -42,29 +42,20 @@ public function testCanConstructWithPageClass() protected function createRealBladeCompilerEnvironment(): void { - // Create and configure the engine resolver $resolver = new EngineResolver(); $filesystem = new Filesystem(); - // Register the blade engine $resolver->register('blade', function () use ($filesystem) { return new CompilerEngine( new BladeCompiler($filesystem, sys_get_temp_dir()) ); }); - // Create the view factory with the configured resolver - $view = new Factory( - $resolver, - $finder = new FileViewFinder( - $filesystem, - [realpath(__DIR__ . '/../../resources/views')] - ), - new Dispatcher() - ); - + $finder = new FileViewFinder($filesystem, [realpath(__DIR__ . '/../../resources/views')]); $finder->addNamespace('hyde', realpath(__DIR__ . '/../../resources/views')); + $view = new Factory($resolver, $finder, new Dispatcher()); + app()->instance('view', $view); app()->instance(FactoryContract::class, $view); } From 0d08cc57a76a5d48e39bbc5771ef6baa99bea824 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 22:55:21 +0100 Subject: [PATCH 45/99] Helper to mock child node renderer --- .../framework/tests/Unit/HeadingRendererUnitTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index bbecaab5a1a..9b883585e54 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -59,4 +59,13 @@ protected function createRealBladeCompilerEnvironment(): void app()->instance('view', $view); app()->instance(FactoryContract::class, $view); } + + protected function mockChildNodeRenderer(string $contents): ChildNodeRendererInterface + { + $childRenderer = Mockery::mock(ChildNodeRendererInterface::class); + $childRenderer->shouldReceive('renderNodes') + ->andReturn($contents); + + return $childRenderer; + } } From 6dc009516a5969a403b2f8c72ccca51dbe7f4af4 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 22:55:42 +0100 Subject: [PATCH 46/99] Improve formatting Apply fixes from StyleCI Co-Authored-By: StyleCI Bot --- packages/framework/tests/Unit/HeadingRendererUnitTest.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index 9b883585e54..07e88ebbd9c 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -51,8 +51,8 @@ protected function createRealBladeCompilerEnvironment(): void ); }); - $finder = new FileViewFinder($filesystem, [realpath(__DIR__ . '/../../resources/views')]); - $finder->addNamespace('hyde', realpath(__DIR__ . '/../../resources/views')); + $finder = new FileViewFinder($filesystem, [realpath(__DIR__.'/../../resources/views')]); + $finder->addNamespace('hyde', realpath(__DIR__.'/../../resources/views')); $view = new Factory($resolver, $finder, new Dispatcher()); @@ -63,8 +63,7 @@ protected function createRealBladeCompilerEnvironment(): void protected function mockChildNodeRenderer(string $contents): ChildNodeRendererInterface { $childRenderer = Mockery::mock(ChildNodeRendererInterface::class); - $childRenderer->shouldReceive('renderNodes') - ->andReturn($contents); + $childRenderer->shouldReceive('renderNodes')->andReturn($contents); return $childRenderer; } From a6b26beba66031eea4aa7aacde00c5921cac6607 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 22:59:21 +0100 Subject: [PATCH 47/99] Set default value --- packages/framework/tests/Unit/HeadingRendererUnitTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index 07e88ebbd9c..94b3f61470f 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -60,7 +60,7 @@ protected function createRealBladeCompilerEnvironment(): void app()->instance(FactoryContract::class, $view); } - protected function mockChildNodeRenderer(string $contents): ChildNodeRendererInterface + protected function mockChildNodeRenderer(string $contents = 'Test Heading'): ChildNodeRendererInterface { $childRenderer = Mockery::mock(ChildNodeRendererInterface::class); $childRenderer->shouldReceive('renderNodes')->andReturn($contents); From 598d9c93cd3228851f041558cdef0e3bc3d89d4d Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 23:04:22 +0100 Subject: [PATCH 48/99] Implement initial unit test --- .../tests/Unit/HeadingRendererUnitTest.php | 116 +++++++++++++++++- 1 file changed, 113 insertions(+), 3 deletions(-) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index 94b3f61470f..1adca4c7b5e 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -4,17 +4,22 @@ namespace Hyde\Framework\Testing\Unit; +use Hyde\Markdown\Processing\HeadingRenderer; +use Hyde\Pages\DocumentationPage; use Hyde\Testing\UnitTestCase; use Illuminate\Contracts\View\Factory as FactoryContract; use Illuminate\Events\Dispatcher; +use Illuminate\Filesystem\Filesystem; use Illuminate\View\Compilers\BladeCompiler; use Illuminate\View\Engines\CompilerEngine; use Illuminate\View\Engines\EngineResolver; use Illuminate\View\Factory; -use Hyde\Markdown\Processing\HeadingRenderer; -use Hyde\Pages\DocumentationPage; use Illuminate\View\FileViewFinder; -use Illuminate\Filesystem\Filesystem; +use InvalidArgumentException; +use League\CommonMark\Extension\CommonMark\Node\Block\Heading; +use League\CommonMark\Node\Node; +use League\CommonMark\Renderer\ChildNodeRendererInterface; +use Mockery; /** * @covers \Hyde\Markdown\Processing\HeadingRenderer @@ -23,6 +28,8 @@ */ class HeadingRendererUnitTest extends UnitTestCase { + protected static bool $needsConfig = true; + protected function setUp(): void { $this->createRealBladeCompilerEnvironment(); @@ -40,6 +47,109 @@ public function testCanConstructWithPageClass() $this->assertInstanceOf(HeadingRenderer::class, $renderer); } + public function testCanRenderHeading() + { + $childRenderer = $this->mockChildNodeRenderer(); + $renderer = new HeadingRenderer(DocumentationPage::class); + $rendered = $renderer->render(new Heading(2), $childRenderer); + + $this->assertStringContainsString('Test Heading', $rendered); + $this->assertStringContainsString('assertStringContainsString('
', $rendered); + + $this->assertSame('

Test Heading

', $rendered); + } + + public function testAddsPermalinkToValidHeadings() + { + $childRenderer = $this->mockChildNodeRenderer(); + $renderer = new HeadingRenderer(DocumentationPage::class); + $heading = new Heading(2); + $rendered = $renderer->render($heading, $childRenderer); + + $this->assertStringContainsString('heading-permalink', $rendered); + $this->assertStringContainsString('#test-heading', $rendered); + } + + public function testDoesNotAddPermalinkToLevelOneHeadingByDefault() + { + $childRenderer = $this->mockChildNodeRenderer(); + $renderer = new HeadingRenderer(DocumentationPage::class); + $rendered = $renderer->render(new Heading(1), $childRenderer); + + $this->assertStringNotContainsString('heading-permalink', $rendered); + } + + public function testAddsPermalinkToLevelOneHeadingIfConfigured() + { + self::mockConfig(['markdown.permalinks.min_level' => 1]); + + $childRenderer = $this->mockChildNodeRenderer(); + $renderer = new HeadingRenderer(DocumentationPage::class); + $rendered = $renderer->render(new Heading(1), $childRenderer); + + $this->assertStringContainsString('heading-permalink', $rendered); + } + + public function testThrowsExceptionForInvalidNode() + { + $invalidNode = Mockery::mock(Node::class); + + $this->expectException(InvalidArgumentException::class); + $renderer = new HeadingRenderer(DocumentationPage::class); + + $childRenderer = Mockery::mock(ChildNodeRendererInterface::class); + $renderer->render($invalidNode, $childRenderer); + } + + public function testDoesNotAddPermalinksIfDisabledInConfiguration() + { + self::mockConfig(['markdown.permalinks.enabled' => false]); + + $childRenderer = $this->mockChildNodeRenderer(); + $renderer = new HeadingRenderer(DocumentationPage::class); + $rendered = $renderer->render(new Heading(2), $childRenderer); + + $this->assertStringNotContainsString('heading-permalink', $rendered); + } + + public function testForwardsHeadingAttributes() + { + $childRenderer = $this->mockChildNodeRenderer('Test Content'); + + $renderer = new HeadingRenderer(DocumentationPage::class); + $heading = new Heading(2); + $heading->data->set('attributes', ['class' => 'custom-class']); + $rendered = $renderer->render($heading, $childRenderer); + + $this->assertStringContainsString('class="custom-class"', $rendered); + } + + public function testForwardsArbitraryHeadingAttributes() + { + $childRenderer = $this->mockChildNodeRenderer('Test Content'); + + $renderer = new HeadingRenderer(DocumentationPage::class); + $heading = new Heading(2); + $heading->data->set('attributes', ['foo' => 'bar']); + $rendered = $renderer->render($heading, $childRenderer); + + $this->assertStringContainsString('foo="bar"', $rendered); + } + + public function testMergesAllHeadingAttributes() + { + $childRenderer = $this->mockChildNodeRenderer('Test Content'); + + $renderer = new HeadingRenderer(DocumentationPage::class); + $heading = new Heading(2); + $heading->data->set('attributes', ['class' => 'custom-class', 'foo' => 'bar']); + $rendered = $renderer->render($heading, $childRenderer); + + $this->assertStringContainsString('class="custom-class"', $rendered); + $this->assertStringContainsString('foo="bar"', $rendered); + } + protected function createRealBladeCompilerEnvironment(): void { $resolver = new EngineResolver(); From 5b00e792a0d7e7e2fde545e08d9072e52bdaf568 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 23:14:01 +0100 Subject: [PATCH 49/99] Test can add permalink based on configuration --- .../tests/Unit/HeadingRendererUnitTest.php | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index 1adca4c7b5e..236f98f0c08 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -113,6 +113,32 @@ public function testDoesNotAddPermalinksIfDisabledInConfiguration() $this->assertStringNotContainsString('heading-permalink', $rendered); } + public function testCanAddPermalinkBasedOnConfiguration() + { + $renderer = new HeadingRenderer(DocumentationPage::class); + $heading = new Heading(2); + $childRenderer = Mockery::mock(ChildNodeRendererInterface::class); + + $childRenderer->shouldReceive('renderNodes') + ->andReturn('Test Content'); + + // Test with different heading levels + foreach (range(1, 2) as $level) { + $heading->setLevel($level); + $rendered = $renderer->render($heading, $childRenderer); + + $this->assertStringNotContainsString('heading-permalink', $rendered); + } + + // Test with different heading levels + foreach (range(3, 6) as $level) { + $heading->setLevel($level); + $rendered = $renderer->render($heading, $childRenderer); + + $this->assertStringContainsString('heading-permalink', $rendered); + } + } + public function testForwardsHeadingAttributes() { $childRenderer = $this->mockChildNodeRenderer('Test Content'); From bc876e8d162bf80c64c66b9a5456e18b346496e5 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 23:17:34 +0100 Subject: [PATCH 50/99] Refactor to data provider attribute --- .../tests/Unit/HeadingRendererUnitTest.php | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index 236f98f0c08..6a478aff6b8 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -20,6 +20,7 @@ use League\CommonMark\Node\Node; use League\CommonMark\Renderer\ChildNodeRendererInterface; use Mockery; +use PHPUnit\Framework\Attributes\TestWith; /** * @covers \Hyde\Markdown\Processing\HeadingRenderer @@ -113,29 +114,22 @@ public function testDoesNotAddPermalinksIfDisabledInConfiguration() $this->assertStringNotContainsString('heading-permalink', $rendered); } - public function testCanAddPermalinkBasedOnConfiguration() + #[TestWith([1, false])] + #[TestWith([2, true])] + #[TestWith([3, true])] + #[TestWith([4, true])] + #[TestWith([5, true])] + #[TestWith([6, true])] + public function testCanAddPermalinkBasedOnConfiguration(int $level, bool $shouldHavePermalink): void { + $childRenderer = $this->mockChildNodeRenderer('Test Content'); $renderer = new HeadingRenderer(DocumentationPage::class); - $heading = new Heading(2); - $childRenderer = Mockery::mock(ChildNodeRendererInterface::class); - - $childRenderer->shouldReceive('renderNodes') - ->andReturn('Test Content'); - - // Test with different heading levels - foreach (range(1, 2) as $level) { - $heading->setLevel($level); - $rendered = $renderer->render($heading, $childRenderer); - - $this->assertStringNotContainsString('heading-permalink', $rendered); - } - - // Test with different heading levels - foreach (range(3, 6) as $level) { - $heading->setLevel($level); - $rendered = $renderer->render($heading, $childRenderer); + $rendered = $renderer->render(new Heading($level), $childRenderer); + if ($shouldHavePermalink) { $this->assertStringContainsString('heading-permalink', $rendered); + } else { + $this->assertStringNotContainsString('heading-permalink', $rendered); } } From 10e905a719d6f2bed1679dbccdbd73dd283f7034 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 23:17:40 +0100 Subject: [PATCH 51/99] Clean up after test --- packages/framework/tests/Unit/HeadingRendererUnitTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index 6a478aff6b8..6522d07208f 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -112,6 +112,8 @@ public function testDoesNotAddPermalinksIfDisabledInConfiguration() $rendered = $renderer->render(new Heading(2), $childRenderer); $this->assertStringNotContainsString('heading-permalink', $rendered); + + self::mockConfig(['markdown.permalinks.enabled' => true]); } #[TestWith([1, false])] From 703e70cc8a7508227049f19649d6701c1617a86c Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 23:24:57 +0100 Subject: [PATCH 52/99] Automatically reset to cached config default --- packages/framework/tests/Unit/HeadingRendererUnitTest.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index 6522d07208f..9eaae81b55b 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -30,10 +30,13 @@ class HeadingRendererUnitTest extends UnitTestCase { protected static bool $needsConfig = true; + protected static ?array $cachedConfig = null; protected function setUp(): void { $this->createRealBladeCompilerEnvironment(); + + self::mockConfig(['markdown' => self::$cachedConfig ??= require __DIR__.'/../../config/markdown.php']); } public function testCanConstruct() @@ -112,8 +115,6 @@ public function testDoesNotAddPermalinksIfDisabledInConfiguration() $rendered = $renderer->render(new Heading(2), $childRenderer); $this->assertStringNotContainsString('heading-permalink', $rendered); - - self::mockConfig(['markdown.permalinks.enabled' => true]); } #[TestWith([1, false])] From 039c11a1851d2060b73b7775405cdd8ae153e256 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 23:27:04 +0100 Subject: [PATCH 53/99] More extensive range testing --- .../tests/Unit/HeadingRendererUnitTest.php | 90 ++++++++++++++++--- 1 file changed, 79 insertions(+), 11 deletions(-) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index 9eaae81b55b..b6c9bbd35db 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -20,7 +20,6 @@ use League\CommonMark\Node\Node; use League\CommonMark\Renderer\ChildNodeRendererInterface; use Mockery; -use PHPUnit\Framework\Attributes\TestWith; /** * @covers \Hyde\Markdown\Processing\HeadingRenderer @@ -117,21 +116,90 @@ public function testDoesNotAddPermalinksIfDisabledInConfiguration() $this->assertStringNotContainsString('heading-permalink', $rendered); } - #[TestWith([1, false])] - #[TestWith([2, true])] - #[TestWith([3, true])] - #[TestWith([4, true])] - #[TestWith([5, true])] - #[TestWith([6, true])] - public function testCanAddPermalinkBasedOnConfiguration(int $level, bool $shouldHavePermalink): void + public function testPermalinksAreAddedDependingOnConfigurationWithDefaultRange(): void { $childRenderer = $this->mockChildNodeRenderer('Test Content'); $renderer = new HeadingRenderer(DocumentationPage::class); - $rendered = $renderer->render(new Heading($level), $childRenderer); - if ($shouldHavePermalink) { + foreach (range(1, 6) as $level) { + $rendered = $renderer->render(new Heading($level), $childRenderer); + + $shouldHavePermalink = $level >= 2; + + if ($shouldHavePermalink) { + $this->assertStringContainsString('heading-permalink', $rendered); + } else { + $this->assertStringNotContainsString('heading-permalink', $rendered); + } + } + } + + public function testPermalinksAreAddedDependingOnConfigurationWithFullRange(): void + { + self::mockConfig(['markdown.permalinks.min_level' => 1, 'markdown.permalinks.max_level' => 6]); + + $childRenderer = $this->mockChildNodeRenderer('Test Content'); + $renderer = new HeadingRenderer(DocumentationPage::class); + + foreach (range(1, 6) as $level) { + $rendered = $renderer->render(new Heading($level), $childRenderer); + $this->assertStringContainsString('heading-permalink', $rendered); - } else { + } + } + + public function testPermalinksAreAddedDependingOnConfigurationWithCustomRange(): void + { + self::mockConfig(['markdown.permalinks.min_level' => 3, 'markdown.permalinks.max_level' => 4]); + + $childRenderer = $this->mockChildNodeRenderer('Test Content'); + $renderer = new HeadingRenderer(DocumentationPage::class); + + foreach (range(1, 6) as $level) { + $rendered = $renderer->render(new Heading($level), $childRenderer); + + $shouldHavePermalink = $level >= 3 && $level <= 4; + + if ($shouldHavePermalink) { + $this->assertStringContainsString('heading-permalink', $rendered); + } else { + $this->assertStringNotContainsString('heading-permalink', $rendered); + } + } + } + + public function testPermalinksAreAddedDependingOnConfigurationWithNoRange(): void + { + self::mockConfig(['markdown.permalinks.min_level' => 0, 'markdown.permalinks.max_level' => 0]); + + $childRenderer = $this->mockChildNodeRenderer('Test Content'); + $renderer = new HeadingRenderer(DocumentationPage::class); + + foreach (range(1, 6) as $level) { + $rendered = $renderer->render(new Heading($level), $childRenderer); + + $this->assertStringNotContainsString('heading-permalink', $rendered); + } + + self::mockConfig(['markdown.permalinks.min_level' => 7, 'markdown.permalinks.max_level' => 8]); + + $childRenderer = $this->mockChildNodeRenderer('Test Content'); + $renderer = new HeadingRenderer(DocumentationPage::class); + + foreach (range(1, 6) as $level) { + $rendered = $renderer->render(new Heading($level), $childRenderer); + + $this->assertStringNotContainsString('heading-permalink', $rendered); + } + + self::mockConfig(['markdown.permalinks.min_level' => -6, 'markdown.permalinks.max_level' => -5]); + + $childRenderer = $this->mockChildNodeRenderer('Test Content'); + $renderer = new HeadingRenderer(DocumentationPage::class); + + foreach (range(1, 6) as $level) { + $rendered = $renderer->render(new Heading($level), $childRenderer); + $this->assertStringNotContainsString('heading-permalink', $rendered); } } From 3a709ddf8a5ed7925f89804447abfd35e321e82b Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 23:28:20 +0100 Subject: [PATCH 54/99] Revert "More extensive range testing" This reverts commit 039c11a1851d2060b73b7775405cdd8ae153e256. --- .../tests/Unit/HeadingRendererUnitTest.php | 90 +++---------------- 1 file changed, 11 insertions(+), 79 deletions(-) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index b6c9bbd35db..9eaae81b55b 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -20,6 +20,7 @@ use League\CommonMark\Node\Node; use League\CommonMark\Renderer\ChildNodeRendererInterface; use Mockery; +use PHPUnit\Framework\Attributes\TestWith; /** * @covers \Hyde\Markdown\Processing\HeadingRenderer @@ -116,90 +117,21 @@ public function testDoesNotAddPermalinksIfDisabledInConfiguration() $this->assertStringNotContainsString('heading-permalink', $rendered); } - public function testPermalinksAreAddedDependingOnConfigurationWithDefaultRange(): void + #[TestWith([1, false])] + #[TestWith([2, true])] + #[TestWith([3, true])] + #[TestWith([4, true])] + #[TestWith([5, true])] + #[TestWith([6, true])] + public function testCanAddPermalinkBasedOnConfiguration(int $level, bool $shouldHavePermalink): void { $childRenderer = $this->mockChildNodeRenderer('Test Content'); $renderer = new HeadingRenderer(DocumentationPage::class); + $rendered = $renderer->render(new Heading($level), $childRenderer); - foreach (range(1, 6) as $level) { - $rendered = $renderer->render(new Heading($level), $childRenderer); - - $shouldHavePermalink = $level >= 2; - - if ($shouldHavePermalink) { - $this->assertStringContainsString('heading-permalink', $rendered); - } else { - $this->assertStringNotContainsString('heading-permalink', $rendered); - } - } - } - - public function testPermalinksAreAddedDependingOnConfigurationWithFullRange(): void - { - self::mockConfig(['markdown.permalinks.min_level' => 1, 'markdown.permalinks.max_level' => 6]); - - $childRenderer = $this->mockChildNodeRenderer('Test Content'); - $renderer = new HeadingRenderer(DocumentationPage::class); - - foreach (range(1, 6) as $level) { - $rendered = $renderer->render(new Heading($level), $childRenderer); - + if ($shouldHavePermalink) { $this->assertStringContainsString('heading-permalink', $rendered); - } - } - - public function testPermalinksAreAddedDependingOnConfigurationWithCustomRange(): void - { - self::mockConfig(['markdown.permalinks.min_level' => 3, 'markdown.permalinks.max_level' => 4]); - - $childRenderer = $this->mockChildNodeRenderer('Test Content'); - $renderer = new HeadingRenderer(DocumentationPage::class); - - foreach (range(1, 6) as $level) { - $rendered = $renderer->render(new Heading($level), $childRenderer); - - $shouldHavePermalink = $level >= 3 && $level <= 4; - - if ($shouldHavePermalink) { - $this->assertStringContainsString('heading-permalink', $rendered); - } else { - $this->assertStringNotContainsString('heading-permalink', $rendered); - } - } - } - - public function testPermalinksAreAddedDependingOnConfigurationWithNoRange(): void - { - self::mockConfig(['markdown.permalinks.min_level' => 0, 'markdown.permalinks.max_level' => 0]); - - $childRenderer = $this->mockChildNodeRenderer('Test Content'); - $renderer = new HeadingRenderer(DocumentationPage::class); - - foreach (range(1, 6) as $level) { - $rendered = $renderer->render(new Heading($level), $childRenderer); - - $this->assertStringNotContainsString('heading-permalink', $rendered); - } - - self::mockConfig(['markdown.permalinks.min_level' => 7, 'markdown.permalinks.max_level' => 8]); - - $childRenderer = $this->mockChildNodeRenderer('Test Content'); - $renderer = new HeadingRenderer(DocumentationPage::class); - - foreach (range(1, 6) as $level) { - $rendered = $renderer->render(new Heading($level), $childRenderer); - - $this->assertStringNotContainsString('heading-permalink', $rendered); - } - - self::mockConfig(['markdown.permalinks.min_level' => -6, 'markdown.permalinks.max_level' => -5]); - - $childRenderer = $this->mockChildNodeRenderer('Test Content'); - $renderer = new HeadingRenderer(DocumentationPage::class); - - foreach (range(1, 6) as $level) { - $rendered = $renderer->render(new Heading($level), $childRenderer); - + } else { $this->assertStringNotContainsString('heading-permalink', $rendered); } } From 496260cf1be10ac15f40bf3a77a251cf65fc9a76 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 23:28:57 +0100 Subject: [PATCH 55/99] Make protected helper methods public internal --- .../framework/src/Markdown/Processing/HeadingRenderer.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/framework/src/Markdown/Processing/HeadingRenderer.php b/packages/framework/src/Markdown/Processing/HeadingRenderer.php index 29e03175d9c..8a9433104e5 100644 --- a/packages/framework/src/Markdown/Processing/HeadingRenderer.php +++ b/packages/framework/src/Markdown/Processing/HeadingRenderer.php @@ -46,7 +46,8 @@ public function render(Node $node, ChildNodeRendererInterface $childRenderer): s return $this->postProcess($rendered); } - protected function canAddPermalink(string $content, int $level): bool + /** @internal */ + public function canAddPermalink(string $content, int $level): bool { return config('markdown.permalinks.enabled', true) && $level >= config('markdown.permalinks.min_level', 2) @@ -55,7 +56,8 @@ protected function canAddPermalink(string $content, int $level): bool && in_array($this->pageClass, config('markdown.permalinks.pages', [DocumentationPage::class])); } - protected function postProcess(string $html): string + /** @internal */ + public function postProcess(string $html): string { $html = preg_replace('//', '', $html); From 90ff9295151f25bb83a5ac791b4d706a48b1320b Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 23:37:12 +0100 Subject: [PATCH 56/99] Remove tests for later refactor --- .../tests/Unit/HeadingRendererUnitTest.php | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index 9eaae81b55b..f90376142cc 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -75,26 +75,6 @@ public function testAddsPermalinkToValidHeadings() $this->assertStringContainsString('#test-heading', $rendered); } - public function testDoesNotAddPermalinkToLevelOneHeadingByDefault() - { - $childRenderer = $this->mockChildNodeRenderer(); - $renderer = new HeadingRenderer(DocumentationPage::class); - $rendered = $renderer->render(new Heading(1), $childRenderer); - - $this->assertStringNotContainsString('heading-permalink', $rendered); - } - - public function testAddsPermalinkToLevelOneHeadingIfConfigured() - { - self::mockConfig(['markdown.permalinks.min_level' => 1]); - - $childRenderer = $this->mockChildNodeRenderer(); - $renderer = new HeadingRenderer(DocumentationPage::class); - $rendered = $renderer->render(new Heading(1), $childRenderer); - - $this->assertStringContainsString('heading-permalink', $rendered); - } - public function testThrowsExceptionForInvalidNode() { $invalidNode = Mockery::mock(Node::class); From 0382947ca6f988b02049bc48d426c011dad7ff5c Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Sun, 1 Dec 2024 23:37:20 +0100 Subject: [PATCH 57/99] Cleaner and extended testing --- .../tests/Unit/HeadingRendererUnitTest.php | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index f90376142cc..ec6ff4d173b 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -97,23 +97,22 @@ public function testDoesNotAddPermalinksIfDisabledInConfiguration() $this->assertStringNotContainsString('heading-permalink', $rendered); } - #[TestWith([1, false])] - #[TestWith([2, true])] - #[TestWith([3, true])] - #[TestWith([4, true])] - #[TestWith([5, true])] - #[TestWith([6, true])] - public function testCanAddPermalinkBasedOnConfiguration(int $level, bool $shouldHavePermalink): void + public function testAddsPermalinksBasedOnConfiguration(): void { $childRenderer = $this->mockChildNodeRenderer('Test Content'); $renderer = new HeadingRenderer(DocumentationPage::class); - $rendered = $renderer->render(new Heading($level), $childRenderer); - if ($shouldHavePermalink) { - $this->assertStringContainsString('heading-permalink', $rendered); - } else { - $this->assertStringNotContainsString('heading-permalink', $rendered); - } + self::mockConfig(['markdown.permalinks.min_level' => 2, 'markdown.permalinks.max_level' => 6]); + + $this->validateHeadingPermalinkStates($renderer, $childRenderer); + + self::mockConfig(['markdown.permalinks.min_level' => 3, 'markdown.permalinks.max_level' => 4]); + + $this->validateHeadingPermalinkStates($renderer, $childRenderer); + + self::mockConfig(['markdown.permalinks.min_level' => 0, 'markdown.permalinks.max_level' => 0]); + + $this->validateHeadingPermalinkStates($renderer, $childRenderer); } public function testForwardsHeadingAttributes() @@ -180,4 +179,19 @@ protected function mockChildNodeRenderer(string $contents = 'Test Heading'): Chi return $childRenderer; } + + protected function validateHeadingPermalinkStates(HeadingRenderer $renderer, ChildNodeRendererInterface $childRenderer): void + { + foreach (range(1, 6) as $level) { + $rendered = $renderer->render(new Heading($level), $childRenderer); + + $shouldHavePermalink = $level >= config('markdown.permalinks.min_level') && $level <= config('markdown.permalinks.max_level'); + + if ($shouldHavePermalink) { + $this->assertStringContainsString('heading-permalink', $rendered); + } else { + $this->assertStringNotContainsString('heading-permalink', $rendered); + } + } + } } From 240e3dd9d615bd4bdaf609789779455d93146586 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 2 Dec 2024 11:01:30 +0100 Subject: [PATCH 58/99] Test more code paths --- .../tests/Unit/HeadingRendererUnitTest.php | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index ec6ff4d173b..ee52b2905fa 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -6,6 +6,7 @@ use Hyde\Markdown\Processing\HeadingRenderer; use Hyde\Pages\DocumentationPage; +use Hyde\Pages\MarkdownPage; use Hyde\Testing\UnitTestCase; use Illuminate\Contracts\View\Factory as FactoryContract; use Illuminate\Events\Dispatcher; @@ -152,6 +153,36 @@ public function testMergesAllHeadingAttributes() $this->assertStringContainsString('foo="bar"', $rendered); } + public function testCanAddPermalinkReturnsFalseForExistingPermalink(): void + { + $renderer = new HeadingRenderer(DocumentationPage::class); + $content = 'Test Content'; + + $result = $renderer->canAddPermalink($content, 2); + + $this->assertFalse($result); + } + + public function testCanAddPermalinkReturnsFalseForNotEnabledPageClass(): void + { + $renderer = new HeadingRenderer(MarkdownPage::class); + $result = $renderer->canAddPermalink('Test Content', 2); + + $this->assertFalse($result); + } + + public function testCanAddPermalinkWithCustomPageClasses(): void + { + self::mockConfig([ + 'markdown.permalinks.pages' => [DocumentationPage::class, MarkdownPage::class], + ]); + + $renderer = new HeadingRenderer(MarkdownPage::class); + $result = $renderer->canAddPermalink('Test Content', 2); + + $this->assertTrue($result); + } + protected function createRealBladeCompilerEnvironment(): void { $resolver = new EngineResolver(); From bbd5a0b37669d5c78166a75048d3ab9d04ae295f Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 2 Dec 2024 11:02:17 +0100 Subject: [PATCH 59/99] Clarify test name --- packages/framework/tests/Unit/HeadingRendererUnitTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index ee52b2905fa..c9bab60eb2c 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -98,7 +98,7 @@ public function testDoesNotAddPermalinksIfDisabledInConfiguration() $this->assertStringNotContainsString('heading-permalink', $rendered); } - public function testAddsPermalinksBasedOnConfiguration(): void + public function testAddsPermalinksBasedOnConfiguredHeadingLevels(): void { $childRenderer = $this->mockChildNodeRenderer('Test Content'); $renderer = new HeadingRenderer(DocumentationPage::class); From 1195704d08179699bcb1d919755eed746538d4d7 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Mon, 2 Dec 2024 10:02:51 +0000 Subject: [PATCH 60/99] Apply fixes from StyleCI --- packages/framework/tests/Unit/HeadingRendererUnitTest.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index c9bab60eb2c..5a13e4d06e7 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -21,7 +21,6 @@ use League\CommonMark\Node\Node; use League\CommonMark\Renderer\ChildNodeRendererInterface; use Mockery; -use PHPUnit\Framework\Attributes\TestWith; /** * @covers \Hyde\Markdown\Processing\HeadingRenderer @@ -157,9 +156,9 @@ public function testCanAddPermalinkReturnsFalseForExistingPermalink(): void { $renderer = new HeadingRenderer(DocumentationPage::class); $content = 'Test Content'; - + $result = $renderer->canAddPermalink($content, 2); - + $this->assertFalse($result); } @@ -179,7 +178,7 @@ public function testCanAddPermalinkWithCustomPageClasses(): void $renderer = new HeadingRenderer(MarkdownPage::class); $result = $renderer->canAddPermalink('Test Content', 2); - + $this->assertTrue($result); } From 65cc1ffd1d3071cfcc2afaf8d6fc6d13dfb4476d Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 2 Dec 2024 11:14:37 +0100 Subject: [PATCH 61/99] Unit test the post processing --- .../tests/Unit/HeadingRendererUnitTest.php | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index 5a13e4d06e7..f06bdc0b2b9 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -182,6 +182,61 @@ public function testCanAddPermalinkWithCustomPageClasses(): void $this->assertTrue($result); } + public function testPostProcessMethodNormalizesInputToMatchCommonMark() + { + $renderer = new HeadingRenderer(DocumentationPage::class); + + // Actual HTML output returned from Blade + $html = <<<'HTML' +

+ Test Heading + +

+ HTML; + + // What CommonMark would generate from the same input Markdown + $expected = '

Test Heading

'; + + $processedHtml = $renderer->postProcess($html); + $this->assertSame($expected, $processedHtml); + } + + public function testPostProcessRemovesSpacesAfterHeadingTags() + { + $renderer = new HeadingRenderer(); + $html = "

Title

\n

Subtitle

"; + $processedHtml = $renderer->postProcess($html); + + $this->assertSame('

Title

Subtitle

', $processedHtml); + } + + public function testPostProcessTrimsWhitespaceAndIndentationFromLines() + { + $renderer = new HeadingRenderer(); + $html = "

Title

\n

Subtitle

"; + $processedHtml = $renderer->postProcess($html); + + $this->assertSame('

Title

Subtitle

', $processedHtml); + } + + public function testPostProcessHandlesEmptyString() + { + $renderer = new HeadingRenderer(); + $html = ""; + $processedHtml = $renderer->postProcess($html); + + $this->assertSame('', $processedHtml); + } + + public function testPostProcessHandlesNoHeadingTags() + { + $renderer = new HeadingRenderer(); + $html = "

Paragraph

"; + $processedHtml = $renderer->postProcess($html); + + $this->assertSame('

Paragraph

', $processedHtml); + } + protected function createRealBladeCompilerEnvironment(): void { $resolver = new EngineResolver(); From 2dd5c32a267872b2e54302a7818e68df116b7708 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 2 Dec 2024 11:15:22 +0100 Subject: [PATCH 62/99] Clarify test name to specify the reason behind it --- packages/framework/tests/Unit/HeadingRendererUnitTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index f06bdc0b2b9..e37b5a6d407 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -201,7 +201,7 @@ public function testPostProcessMethodNormalizesInputToMatchCommonMark() $this->assertSame($expected, $processedHtml); } - public function testPostProcessRemovesSpacesAfterHeadingTags() + public function testPostProcessRemovesSpacesCausedByNoExtraBladeAttributes() { $renderer = new HeadingRenderer(); $html = "

Title

\n

Subtitle

"; From b1b62669636ba5bdf9d4237c58963365889f48fe Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 2 Dec 2024 11:16:42 +0100 Subject: [PATCH 63/99] Add extra test case --- .../framework/tests/Unit/HeadingRendererUnitTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index e37b5a6d407..9e2d87b2bee 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -210,6 +210,15 @@ public function testPostProcessRemovesSpacesCausedByNoExtraBladeAttributes() $this->assertSame('

Title

Subtitle

', $processedHtml); } + public function testPostProcessRemovesSpacesCausedByNoExtraBladeAttributesButLeavesExtraAttributesAlone() + { + $renderer = new HeadingRenderer(); + $html = "

Title

\n

Subtitle

"; + $processedHtml = $renderer->postProcess($html); + + $this->assertSame('

Title

Subtitle

', $processedHtml); + } + public function testPostProcessTrimsWhitespaceAndIndentationFromLines() { $renderer = new HeadingRenderer(); From 2f31b9b9949bd2860260600b80b8a14bf0e82ffc Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Mon, 2 Dec 2024 10:16:51 +0000 Subject: [PATCH 64/99] Apply fixes from StyleCI --- packages/framework/tests/Unit/HeadingRendererUnitTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index 9e2d87b2bee..a2b5cf73f71 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -231,7 +231,7 @@ public function testPostProcessTrimsWhitespaceAndIndentationFromLines() public function testPostProcessHandlesEmptyString() { $renderer = new HeadingRenderer(); - $html = ""; + $html = ''; $processedHtml = $renderer->postProcess($html); $this->assertSame('', $processedHtml); @@ -240,7 +240,7 @@ public function testPostProcessHandlesEmptyString() public function testPostProcessHandlesNoHeadingTags() { $renderer = new HeadingRenderer(); - $html = "

Paragraph

"; + $html = '

Paragraph

'; $processedHtml = $renderer->postProcess($html); $this->assertSame('

Paragraph

', $processedHtml); From 3c3661ac77b1fc663d20bab2e421b7b291bdd7ee Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 2 Dec 2024 11:19:53 +0100 Subject: [PATCH 65/99] Clean up test code to remove focus from implementation details --- .../tests/Unit/HeadingRendererUnitTest.php | 31 +++++-------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/packages/framework/tests/Unit/HeadingRendererUnitTest.php b/packages/framework/tests/Unit/HeadingRendererUnitTest.php index a2b5cf73f71..d544209027d 100644 --- a/packages/framework/tests/Unit/HeadingRendererUnitTest.php +++ b/packages/framework/tests/Unit/HeadingRendererUnitTest.php @@ -165,9 +165,8 @@ public function testCanAddPermalinkReturnsFalseForExistingPermalink(): void public function testCanAddPermalinkReturnsFalseForNotEnabledPageClass(): void { $renderer = new HeadingRenderer(MarkdownPage::class); - $result = $renderer->canAddPermalink('Test Content', 2); - $this->assertFalse($result); + $this->assertFalse($renderer->canAddPermalink('Test Content', 2)); } public function testCanAddPermalinkWithCustomPageClasses(): void @@ -177,15 +176,12 @@ public function testCanAddPermalinkWithCustomPageClasses(): void ]); $renderer = new HeadingRenderer(MarkdownPage::class); - $result = $renderer->canAddPermalink('Test Content', 2); - $this->assertTrue($result); + $this->assertTrue($renderer->canAddPermalink('Test Content', 2)); } public function testPostProcessMethodNormalizesInputToMatchCommonMark() { - $renderer = new HeadingRenderer(DocumentationPage::class); - // Actual HTML output returned from Blade $html = <<<'HTML'

@@ -197,53 +193,42 @@ public function testPostProcessMethodNormalizesInputToMatchCommonMark() // What CommonMark would generate from the same input Markdown $expected = '

Test Heading

'; - $processedHtml = $renderer->postProcess($html); - $this->assertSame($expected, $processedHtml); + $this->assertSame($expected, (new HeadingRenderer())->postProcess($html)); } public function testPostProcessRemovesSpacesCausedByNoExtraBladeAttributes() { - $renderer = new HeadingRenderer(); $html = "

Title

\n

Subtitle

"; - $processedHtml = $renderer->postProcess($html); - $this->assertSame('

Title

Subtitle

', $processedHtml); + $this->assertSame('

Title

Subtitle

', (new HeadingRenderer())->postProcess($html)); } public function testPostProcessRemovesSpacesCausedByNoExtraBladeAttributesButLeavesExtraAttributesAlone() { - $renderer = new HeadingRenderer(); $html = "

Title

\n

Subtitle

"; - $processedHtml = $renderer->postProcess($html); - $this->assertSame('

Title

Subtitle

', $processedHtml); + $this->assertSame('

Title

Subtitle

', (new HeadingRenderer())->postProcess($html)); } public function testPostProcessTrimsWhitespaceAndIndentationFromLines() { - $renderer = new HeadingRenderer(); $html = "

Title

\n

Subtitle

"; - $processedHtml = $renderer->postProcess($html); - $this->assertSame('

Title

Subtitle

', $processedHtml); + $this->assertSame('

Title

Subtitle

', (new HeadingRenderer())->postProcess($html)); } public function testPostProcessHandlesEmptyString() { - $renderer = new HeadingRenderer(); $html = ''; - $processedHtml = $renderer->postProcess($html); - $this->assertSame('', $processedHtml); + $this->assertSame('', (new HeadingRenderer())->postProcess($html)); } public function testPostProcessHandlesNoHeadingTags() { - $renderer = new HeadingRenderer(); $html = '

Paragraph

'; - $processedHtml = $renderer->postProcess($html); - $this->assertSame('

Paragraph

', $processedHtml); + $this->assertSame('

Paragraph

', (new HeadingRenderer())->postProcess($html)); } protected function createRealBladeCompilerEnvironment(): void From b00ea06a08fe9886850746279f8b34628251ca11 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 2 Dec 2024 11:35:11 +0100 Subject: [PATCH 66/99] Implement the high level feature test --- .../Feature/MarkdownHeadingRendererTest.php | 134 +++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php b/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php index 11b5075f75e..29daa66038c 100644 --- a/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php +++ b/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php @@ -5,6 +5,9 @@ namespace Hyde\Framework\Testing\Feature; use Hyde\Testing\TestCase; +use Hyde\Framework\Services\MarkdownService; +use Hyde\Pages\DocumentationPage; +use Hyde\Pages\MarkdownPage; /** * @covers \Hyde\Markdown\Processing\HeadingRenderer @@ -13,5 +16,134 @@ */ class MarkdownHeadingRendererTest extends TestCase { - // + public function testBasicHeadingRendering() + { + $markdown = <<<'MARKDOWN' + # Heading 1 + ## Heading 2 + ### Heading 3 + #### Heading 4 + ##### Heading 5 + ###### Heading 6 + MARKDOWN; + + $html = (new MarkdownService($markdown))->parse(); + + $this->assertStringContainsString('

Heading 1

', $html); + $this->assertStringContainsString('

Heading 2

', $html); + $this->assertStringContainsString('

Heading 3

', $html); + $this->assertStringContainsString('

Heading 4

', $html); + $this->assertStringContainsString('
Heading 5
', $html); + $this->assertStringContainsString('
Heading 6
', $html); + } + + public function testPermalinksInDocumentationPages() + { + $markdown = '## Documentation Heading'; + $html = (new MarkdownService($markdown, DocumentationPage::class))->parse(); + + $this->assertStringContainsString('heading-permalink', $html); + $this->assertStringContainsString('id="documentation-heading"', $html); + $this->assertStringContainsString('href="#documentation-heading"', $html); + } + + public function testPermalinksAreNotAddedToRegularMarkdownPages() + { + $markdown = '## Regular Page Heading'; + $html = (new MarkdownService($markdown, MarkdownPage::class))->parse(); + + $this->assertStringNotContainsString('heading-permalink', $html); + } + + public function testSetextStyleHeadings() + { + $markdown = <<<'MARKDOWN' + Heading 1 + ========= + + Heading 2 + --------- + MARKDOWN; + + $html = (new MarkdownService($markdown))->parse(); + + $this->assertStringContainsString('

Heading 1

', $html); + $this->assertStringContainsString('

Heading 2

', $html); + } + + public function testHeadingsWithCustomAttributes() + { + $markdown = <<<'MARKDOWN' + ## Heading {.custom-class #custom-id} + ### Another Heading {data-test="value"} + MARKDOWN; + + $html = (new MarkdownService($markdown, DocumentationPage::class))->parse(); + + $this->assertStringContainsString('class="custom-class"', $html); + $this->assertStringContainsString('id="custom-id"', $html); + $this->assertStringContainsString('data-test="value"', $html); + } + + public function testPermalinkConfigurationLevels() + { + config(['markdown.permalinks.min_level' => 2]); + config(['markdown.permalinks.max_level' => 4]); + + $markdown = <<<'MARKDOWN' + # H1 No Permalink + ## H2 Has Permalink + ### H3 Has Permalink + #### H4 Has Permalink + ##### H5 No Permalink + ###### H6 No Permalink + MARKDOWN; + + $html = (new MarkdownService($markdown, DocumentationPage::class))->parse(); + + $this->assertStringNotContainsString('

H1 No Permalink

assertStringContainsString('

H2 Has PermalinkassertStringContainsString('

H3 Has PermalinkassertStringContainsString('

H4 Has PermalinkassertStringNotContainsString('
H5 No Permalink
assertStringNotContainsString('
H6 No Permalink
false]); + + $markdown = '## Heading'; + $html = (new MarkdownService($markdown, DocumentationPage::class))->parse(); + + $this->assertStringNotContainsString('heading-permalink', $html); + } + + public function testHeadingsWithSpecialCharacters() + { + $markdown = <<<'MARKDOWN' + ## Heading with & special < > "characters" + ### Heading with émojis 🎉 + MARKDOWN; + + $html = (new MarkdownService($markdown, DocumentationPage::class))->parse(); + + $this->assertStringContainsString('Heading with & special < > "characters"', $html); + $this->assertStringContainsString('Heading with émojis 🎉', $html); + } + + public function testCustomPageClassConfiguration() + { + config(['markdown.permalinks.pages' => [MarkdownPage::class]]); + + $markdown = '## Test Heading'; + + // Should now have permalinks + $html = (new MarkdownService($markdown, MarkdownPage::class))->parse(); + $this->assertStringContainsString('heading-permalink', $html); + + // Should not have permalinks + $html = (new MarkdownService($markdown, DocumentationPage::class))->parse(); + $this->assertStringNotContainsString('heading-permalink', $html); + } } From a7b52d631feb3731acb84d8ae1db23a63e8fb0ed Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 2 Dec 2024 11:53:36 +0100 Subject: [PATCH 67/99] Add some more assertions --- .../framework/tests/Feature/MarkdownHeadingRendererTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php b/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php index 29daa66038c..a799652bb6c 100644 --- a/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php +++ b/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php @@ -45,6 +45,8 @@ public function testPermalinksInDocumentationPages() $this->assertStringContainsString('heading-permalink', $html); $this->assertStringContainsString('id="documentation-heading"', $html); $this->assertStringContainsString('href="#documentation-heading"', $html); + $this->assertStringContainsString('title="Permalink"', $html); + // $this->assertStringContainsString('aria-label="Permalink to this heading"', $html); } public function testPermalinksAreNotAddedToRegularMarkdownPages() From e67a64074692d4126740c990097fa91a81406843 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 2 Dec 2024 11:58:21 +0100 Subject: [PATCH 68/99] Assert on the full output when relevant --- .../tests/Feature/MarkdownHeadingRendererTest.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php b/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php index a799652bb6c..72dc6e3b64d 100644 --- a/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php +++ b/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php @@ -35,6 +35,16 @@ public function testBasicHeadingRendering() $this->assertStringContainsString('

Heading 4

', $html); $this->assertStringContainsString('
Heading 5
', $html); $this->assertStringContainsString('
Heading 6
', $html); + + $this->assertSame(<<<'HTML' +

Heading 1

+

Heading 2

+

Heading 3

+

Heading 4

+
Heading 5
+
Heading 6
+ + HTML, $html); } public function testPermalinksInDocumentationPages() @@ -47,6 +57,11 @@ public function testPermalinksInDocumentationPages() $this->assertStringContainsString('href="#documentation-heading"', $html); $this->assertStringContainsString('title="Permalink"', $html); // $this->assertStringContainsString('aria-label="Permalink to this heading"', $html); + + $this->assertSame(<<<'HTML' +

Documentation Heading

+ + HTML, $html); } public function testPermalinksAreNotAddedToRegularMarkdownPages() From 6fe5cb8fd4db2c86a3e91f8126c6607f1af75234 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 2 Dec 2024 12:03:05 +0100 Subject: [PATCH 69/99] Expand feature testing --- .../Feature/MarkdownHeadingRendererTest.php | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php b/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php index 72dc6e3b64d..92a85958ec6 100644 --- a/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php +++ b/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php @@ -95,11 +95,37 @@ public function testHeadingsWithCustomAttributes() ### Another Heading {data-test="value"} MARKDOWN; - $html = (new MarkdownService($markdown, DocumentationPage::class))->parse(); + $html = (new MarkdownService($markdown, MarkdownPage::class))->parse(); + $this->assertStringContainsString('id="custom-id"', $html); $this->assertStringContainsString('class="custom-class"', $html); + $this->assertStringContainsString('data-test="value"', $html); + + $this->assertSame(<<<'HTML' +

Heading

+

Another Heading

+ + HTML, $html); + } + + public function testHeadingsWithCustomAttributesAndPermalinks() + { + $markdown = <<<'MARKDOWN' + ## Heading {.custom-class #custom-id} + ### Another Heading {data-test="value"} + MARKDOWN; + + $html = (new MarkdownService($markdown, DocumentationPage::class))->parse(); + $this->assertStringContainsString('id="custom-id"', $html); + $this->assertStringContainsString('class="custom-class"', $html); $this->assertStringContainsString('data-test="value"', $html); + + $this->assertSame(<<<'HTML' +

Heading

+

Another Heading

+ + HTML, $html); } public function testPermalinkConfigurationLevels() @@ -124,6 +150,16 @@ public function testPermalinkConfigurationLevels() $this->assertStringContainsString('

H4 Has PermalinkassertStringNotContainsString('
H5 No Permalink
assertStringNotContainsString('
H6 No Permalink
assertSame(<<<'HTML' +

H1 No Permalink

+

H2 Has Permalink

+

H3 Has Permalink

+

H4 Has Permalink

+
H5 No Permalink
+
H6 No Permalink
+ + HTML, $html); } public function testDisablingPermalinksGlobally() @@ -147,6 +183,12 @@ public function testHeadingsWithSpecialCharacters() $this->assertStringContainsString('Heading with & special < > "characters"', $html); $this->assertStringContainsString('Heading with émojis 🎉', $html); + + $this->assertSame(<<<'HTML' +

Heading with & special < > "characters"

+

Heading with émojis 🎉

+ + HTML, $html); } public function testCustomPageClassConfiguration() From 3c285a7ba45d1cdc9dba227a1ff5ab89793fd557 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 2 Dec 2024 12:03:07 +0100 Subject: [PATCH 70/99] Add todo --- packages/framework/tests/Feature/MarkdownHeadingRendererTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php b/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php index 92a85958ec6..67bb0b3307f 100644 --- a/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php +++ b/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php @@ -184,6 +184,7 @@ public function testHeadingsWithSpecialCharacters() $this->assertStringContainsString('Heading with & special < > "characters"', $html); $this->assertStringContainsString('Heading with émojis 🎉', $html); + // Todo: Try to normalize to heading-with-special-characters? $this->assertSame(<<<'HTML'

Heading with & special < > "characters"

Heading with émojis 🎉

From 43d82a4327dcf2cd68d02a97cec041bdcd8dbe28 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Mon, 2 Dec 2024 12:07:31 +0100 Subject: [PATCH 71/99] Test escaping --- .../Feature/MarkdownHeadingRendererTest.php | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php b/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php index 67bb0b3307f..b2d9e70e77f 100644 --- a/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php +++ b/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php @@ -192,6 +192,41 @@ public function testHeadingsWithSpecialCharacters() HTML, $html); } + public function testHeadingsAllowMarkdownStyling() + { + $markdown = <<<'MARKDOWN' + ## Heading with **Markdown** styling + MARKDOWN; + + $html = (new MarkdownService($markdown, MarkdownPage::class))->parse(); + + $this->assertStringContainsString('Heading with Markdown styling', $html); + + $this->assertSame(<<<'HTML' +

Heading with Markdown styling

+ + HTML, $html); + } + + public function testHeadingsAllowBasicHtmlButEscapesDangerousInput() + { + $markdown = <<<'MARKDOWN' + ## Heading with HTML + ### Heading with + MARKDOWN; + + $html = (new MarkdownService($markdown, MarkdownPage::class))->parse(); + + $this->assertStringContainsString('Heading with HTML', $html); + $this->assertStringContainsString("Heading with <script>alert('XSS')</script>", $html); + + $this->assertSame(<<<'HTML' +

Heading with HTML

+

Heading with <script>alert('XSS')</script>

+ + HTML, $html); + } + public function testCustomPageClassConfiguration() { config(['markdown.permalinks.pages' => [MarkdownPage::class]]); From 4e80297fa9ae9f229eeb04ec8b49c837da5041c6 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 3 Dec 2024 20:48:12 +0100 Subject: [PATCH 72/99] Document custom Markdown heading renderer --- docs/digging-deeper/advanced-markdown.md | 32 ++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/docs/digging-deeper/advanced-markdown.md b/docs/digging-deeper/advanced-markdown.md index 2fe8bd3648d..d8a87c8de63 100644 --- a/docs/digging-deeper/advanced-markdown.md +++ b/docs/digging-deeper/advanced-markdown.md @@ -161,6 +161,38 @@ anything within the path label will be rendered as HTML. This means you can add The filepaths are hidden on mobile devices using CSS to prevent them from overlapping with the code block. +## Heading Permalinks + +Hyde automatically adds clickable permalink anchors to headings in documentation pages. When you hover over a heading, a `#` link appears that you can click to get a direct link to that section. + +### Usage & Configuration + +The feature is enabled by default for documentation pages. When enabled, Hyde will automatically add permalink anchors to headings between levels 2-4 (h2-h4). The permalinks are hidden by default and appear when hovering over the heading. + + +You can disable the feature, or enable it for other page types, in the `config/markdown.php` file. + +```php +// filepath: config/markdown.php +'permalinks' => [ + 'enabled' => true, + 'pages' => [ + \Hyde\Pages\DocumentationPage::class, + ], +], +``` + +### Advanced Customization + +Under the hood, Hyde uses a custom Blade-based heading renderer when converting Markdown to HTML. This allows for more flexibility and customization compared to standard Markdown parsers. You can also publish and customize the Blade component used to render the headings: + +```bash +php hyde publish:components +``` + +This will copy the `markdown-heading.blade.php` component to your views directory where you can modify its markup and behavior. + + ## Dynamic Markdown Links HydePHP provides a powerful feature for automatically converting Markdown links to source files to the corresponding routes in the built site. From f53eb4ddf951e6f5283e33800162a1f237d92cb9 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 3 Dec 2024 20:56:58 +0100 Subject: [PATCH 73/99] Remove the enabled key from the permalinks configuration --- config/markdown.php | 1 - docs/digging-deeper/advanced-markdown.md | 4 +--- packages/framework/config/markdown.php | 1 - .../framework/tests/Feature/MarkdownHeadingRendererTest.php | 2 +- 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/config/markdown.php b/config/markdown.php index 7e8d142084f..cd9afdea0e5 100644 --- a/config/markdown.php +++ b/config/markdown.php @@ -108,7 +108,6 @@ */ 'permalinks' => [ - 'enabled' => true, 'pages' => [ \Hyde\Pages\DocumentationPage::class, ], diff --git a/docs/digging-deeper/advanced-markdown.md b/docs/digging-deeper/advanced-markdown.md index d8a87c8de63..6a2a0d52c98 100644 --- a/docs/digging-deeper/advanced-markdown.md +++ b/docs/digging-deeper/advanced-markdown.md @@ -169,13 +169,11 @@ Hyde automatically adds clickable permalink anchors to headings in documentation The feature is enabled by default for documentation pages. When enabled, Hyde will automatically add permalink anchors to headings between levels 2-4 (h2-h4). The permalinks are hidden by default and appear when hovering over the heading. - -You can disable the feature, or enable it for other page types, in the `config/markdown.php` file. +You can enable it for other page types by adding the page class to the `permalinks.pages` array in the `config/markdown.php` file, or disable it for all pages by setting the array to an empty array. ```php // filepath: config/markdown.php 'permalinks' => [ - 'enabled' => true, 'pages' => [ \Hyde\Pages\DocumentationPage::class, ], diff --git a/packages/framework/config/markdown.php b/packages/framework/config/markdown.php index 7e8d142084f..cd9afdea0e5 100644 --- a/packages/framework/config/markdown.php +++ b/packages/framework/config/markdown.php @@ -108,7 +108,6 @@ */ 'permalinks' => [ - 'enabled' => true, 'pages' => [ \Hyde\Pages\DocumentationPage::class, ], diff --git a/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php b/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php index b2d9e70e77f..d52b02ea5b2 100644 --- a/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php +++ b/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php @@ -164,7 +164,7 @@ public function testPermalinkConfigurationLevels() public function testDisablingPermalinksGlobally() { - config(['markdown.permalinks.enabled' => false]); + config(['markdown.permalinks.pages' => []]); $markdown = '## Heading'; $html = (new MarkdownService($markdown, DocumentationPage::class))->parse(); From ce21d665d36f9a00eb0eac235538db5920d49179 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 3 Dec 2024 21:10:51 +0100 Subject: [PATCH 74/99] Remove the unused `canEnablePermalinks` from `MarkdownService` --- RELEASE_NOTES.md | 1 + .../src/Framework/Services/MarkdownService.php | 13 ------------- .../tests/Feature/MarkdownServiceTest.php | 16 ---------------- 3 files changed, 1 insertion(+), 29 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index eeb595d630b..6efc73d5857 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -133,6 +133,7 @@ This serves two purposes: - Removed the `.torchlight-enabled` CSS class in https://github.com/hydephp/develop/pull/2036. - Removed The `hyde.css` file from HydeFront in https://github.com/hydephp/develop/pull/2037 as all styles were refactored to Tailwind in https://github.com/hydephp/develop/pull/2024. - Removed the `MarkdownService::withPermalinks` method in https://github.com/hydephp/develop/pull/2047 +- Removed the `MarkdownService::canEnablePermalinks` method in https://github.com/hydephp/develop/pull/2047 ### Fixed diff --git a/packages/framework/src/Framework/Services/MarkdownService.php b/packages/framework/src/Framework/Services/MarkdownService.php index a5eb175ca7d..8835c33667e 100644 --- a/packages/framework/src/Framework/Services/MarkdownService.php +++ b/packages/framework/src/Framework/Services/MarkdownService.php @@ -162,19 +162,6 @@ public function canEnableTorchlight(): bool Features::hasTorchlight(); } - public function canEnablePermalinks(): bool - { - if ($this->hasFeature('permalinks')) { - return true; - } - - if ($this->isDocumentationPage() && DocumentationPage::hasTableOfContents()) { - return true; - } - - return false; - } - public function hasFeature(string $feature): bool { return in_array($feature, $this->features); diff --git a/packages/framework/tests/Feature/MarkdownServiceTest.php b/packages/framework/tests/Feature/MarkdownServiceTest.php index a5ba860dbc6..2f4d10b75f7 100644 --- a/packages/framework/tests/Feature/MarkdownServiceTest.php +++ b/packages/framework/tests/Feature/MarkdownServiceTest.php @@ -172,22 +172,6 @@ public function testHasFeatureReturnsFalseIfTheFeatureIsNotInTheArray() $this->assertFalse($service->hasFeature('test')); } - public function testMethodCanEnablePermalinksIsAutomaticallyForDocumentationPages() - { - $service = new MarkdownServiceTestClass(pageClass: DocumentationPage::class); - - Config::set('docs.sidebar.table_of_contents.enabled', true); - - $this->assertTrue($service->canEnablePermalinks()); - } - - public function testMethodCanEnablePermalinksReturnsFalseIfThePermalinksFeatureIsNotInTheArray() - { - $service = $this->makeService(); - - $this->assertFalse($service->canEnablePermalinks()); - } - public function testMethodCanEnableTorchlightReturnsTrueIfTheTorchlightFeatureIsInTheArray() { $service = $this->makeService(); From 3d3ed48eaebec035a821651237eace64f2da9b0b Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 3 Dec 2024 21:11:04 +0100 Subject: [PATCH 75/99] Improve the test --- .../tests/Feature/MarkdownServiceTest.php | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/packages/framework/tests/Feature/MarkdownServiceTest.php b/packages/framework/tests/Feature/MarkdownServiceTest.php index 2f4d10b75f7..cd16cbdb4bc 100644 --- a/packages/framework/tests/Feature/MarkdownServiceTest.php +++ b/packages/framework/tests/Feature/MarkdownServiceTest.php @@ -6,6 +6,7 @@ use Hyde\Framework\Services\MarkdownService; use Hyde\Pages\DocumentationPage; +use Hyde\Pages\MarkdownPage; use Hyde\Testing\TestCase; use Illuminate\Support\Facades\Config; @@ -25,18 +26,39 @@ public function testServiceCanParseMarkdownToHtml() $this->assertSame("

Hello World!

\n", $html); } - public function testServiceCanParseMarkdownToHtmlWithPermalinks() + public function testServiceCanParseMarkdownToHtmlWithPermalinksDependingOnConfiguration() { $markdown = '## Hello World!'; $html = (new MarkdownService($markdown, DocumentationPage::class))->parse(); $this->assertIsString($html); + $this->assertStringContainsString('heading-permalink', $html, 'Permalink should be added to documentation pages by default'); $this->assertSame( - '

Hello World!

'."\n", - $html + '

Hello World!

'."\n", $html ); + + $html = (new MarkdownService($markdown))->parse(); + $this->assertStringNotContainsString('heading-permalink', $html, 'Permalink should not be be added when the page class is not set'); + + $html = (new MarkdownService($markdown, MarkdownPage::class))->parse(); + $this->assertStringNotContainsString('heading-permalink', $html, 'Permalink should not be added to other pages by default'); + + Config::set('markdown.permalinks.pages', [MarkdownPage::class]); + + $html = (new MarkdownService($markdown, MarkdownPage::class))->parse(); + $this->assertStringContainsString('heading-permalink', $html, 'Permalink should be added to pages when configured'); + + $html = (new MarkdownService($markdown, DocumentationPage::class))->parse(); + $this->assertStringNotContainsString('heading-permalink', $html, 'Permalink should not be added to pages when not configured'); + + Config::set('markdown.permalinks.pages', []); + + $html = (new MarkdownService($markdown, DocumentationPage::class))->parse(); + $this->assertStringNotContainsString('heading-permalink', $html, 'Permalinks should not be added to any pages when disabled'); + + $html = (new MarkdownService($markdown, MarkdownPage::class))->parse(); + $this->assertStringNotContainsString('heading-permalink', $html, 'Permalinks should not be added to any pages when disabled'); } public function testTorchlightExtensionIsNotEnabledByDefault() From 7f79bee5fa7fad051e5ab2970c84edf4ccfd4ee2 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 4 Dec 2024 09:38:59 +0100 Subject: [PATCH 76/99] Update RELEASE_NOTES.md --- RELEASE_NOTES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 6efc73d5857..8d927a0193a 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -104,6 +104,7 @@ This serves two purposes: - Normalized default Tailwind Typography Prose code block styles to match Torchlight's theme, ensuring consistent styling across Markdown and Torchlight code blocks in https://github.com/hydephp/develop/pull/2036. - Extracted CSS component partials in HydeFront in https://github.com/hydephp/develop/pull/2038 - Replaced HydeFront styles with Tailwind in https://github.com/hydephp/develop/pull/2024 +- The `id` attributes for heading permalinks have been moved from the anchor to the heading element in https://github.com/hydephp/develop/pull/2052 ### Deprecated From 0710e95dd1d79cea105a7c254a770ec0fd3aa9c8 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 4 Dec 2024 09:25:08 +0100 Subject: [PATCH 77/99] More semantic Markdown heading permalinks --- .../resources/views/components/markdown-heading.blade.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/framework/resources/views/components/markdown-heading.blade.php b/packages/framework/resources/views/components/markdown-heading.blade.php index a0dec20101f..a41d8dd22bb 100644 --- a/packages/framework/resources/views/components/markdown-heading.blade.php +++ b/packages/framework/resources/views/components/markdown-heading.blade.php @@ -10,9 +10,9 @@ $id = $id ?? \Illuminate\Support\Str::slug($slot); @endphp -<{{ $tag }} {{ $attributes->merge([...$extraAttributes]) }}> +<{{ $tag }} {{ $attributes->merge(['id' => $id, ...$extraAttributes]) }}> {!! $slot !!} @if($addPermalink === true) - + @endif \ No newline at end of file From 8737a5240ce51da23961776356a958e0007fdb37 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 4 Dec 2024 09:27:42 +0100 Subject: [PATCH 78/99] Conditionally add element identifier --- .../resources/views/components/markdown-heading.blade.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/framework/resources/views/components/markdown-heading.blade.php b/packages/framework/resources/views/components/markdown-heading.blade.php index a41d8dd22bb..f4af7c2a80a 100644 --- a/packages/framework/resources/views/components/markdown-heading.blade.php +++ b/packages/framework/resources/views/components/markdown-heading.blade.php @@ -8,9 +8,13 @@ @php $tag = 'h' . $level; $id = $id ?? \Illuminate\Support\Str::slug($slot); + + if ($addPermalink === true) { + $extraAttributes['id'] = $id; + } @endphp -<{{ $tag }} {{ $attributes->merge(['id' => $id, ...$extraAttributes]) }}> +<{{ $tag }} {{ $attributes->merge([...$extraAttributes]) }}> {!! $slot !!} @if($addPermalink === true) From 04aa325e77b9be0f860c95833ef7ced192593995 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 4 Dec 2024 09:32:18 +0100 Subject: [PATCH 79/99] Remove support for custom identifier when it breaks permalinks --- .../framework/tests/Feature/MarkdownHeadingRendererTest.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php b/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php index d52b02ea5b2..e4a4d24db93 100644 --- a/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php +++ b/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php @@ -111,18 +111,17 @@ public function testHeadingsWithCustomAttributes() public function testHeadingsWithCustomAttributesAndPermalinks() { $markdown = <<<'MARKDOWN' - ## Heading {.custom-class #custom-id} + ## Heading {.custom-class} ### Another Heading {data-test="value"} MARKDOWN; $html = (new MarkdownService($markdown, DocumentationPage::class))->parse(); - $this->assertStringContainsString('id="custom-id"', $html); $this->assertStringContainsString('class="custom-class"', $html); $this->assertStringContainsString('data-test="value"', $html); $this->assertSame(<<<'HTML' -

Heading

+

Heading

Another Heading

HTML, $html); From e7be1522416740ad5e8d0f103c4e053f9ff7f191 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 4 Dec 2024 09:34:30 +0100 Subject: [PATCH 80/99] Expect semantic heading identifiers --- .../Feature/MarkdownHeadingRendererTest.php | 22 +++++++++---------- .../tests/Feature/MarkdownServiceTest.php | 2 +- ...aticSiteBuilderDocumentationModuleTest.php | 4 ++-- .../tests/Unit/HeadingRendererUnitTest.php | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php b/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php index e4a4d24db93..cd20a8545f1 100644 --- a/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php +++ b/packages/framework/tests/Feature/MarkdownHeadingRendererTest.php @@ -59,7 +59,7 @@ public function testPermalinksInDocumentationPages() // $this->assertStringContainsString('aria-label="Permalink to this heading"', $html); $this->assertSame(<<<'HTML' -

Documentation Heading

+

Documentation Heading

HTML, $html); } @@ -121,8 +121,8 @@ public function testHeadingsWithCustomAttributesAndPermalinks() $this->assertStringContainsString('data-test="value"', $html); $this->assertSame(<<<'HTML' -

Heading

-

Another Heading

+

Heading

+

Another Heading

HTML, $html); } @@ -144,17 +144,17 @@ public function testPermalinkConfigurationLevels() $html = (new MarkdownService($markdown, DocumentationPage::class))->parse(); $this->assertStringNotContainsString('

H1 No Permalink

assertStringContainsString('

H2 Has PermalinkassertStringContainsString('

H3 Has PermalinkassertStringContainsString('

H4 Has PermalinkassertStringContainsString('