diff --git a/docs/syntax/admonitions.md b/docs/syntax/admonitions.md index 90438e123..edfe09eaf 100644 --- a/docs/syntax/admonitions.md +++ b/docs/syntax/admonitions.md @@ -150,26 +150,31 @@ It can *span* multiple lines and supports inline formatting. ::::: -## Collapsible admonitions +## Applies to information -:::{warning} -Collapsible admonitions are deprecated. Do not use them. Use [dropdowns](./dropdowns.md) instead. -::: - -Use `:open: ` to make an admonition collapsible. +Admonitions support the `applies_to` property to indicate which products or versions the information applies to. :::::{tab-set} ::::{tab-item} Output :::{note} -:open: +:applies_to: stack: ga 9.1.0 + +This note applies to the Elastic Stack GA version 9.1.0. +::: -Longer content can be collapsed to take less space. +:::{warning} +:applies_to: serverless: ga -Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +This warning applies to serverless GA. ::: +:::{tip} +:applies_to: { ess:, ece: } + +This tip applies to ECH and ECE. +::: :::: @@ -177,11 +182,21 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor i ```markdown :::{note} -:open: +:applies_to: stack: ga 9.1.0 + +This note applies to the Elastic Stack GA version 9.1.0. +::: + +:::{warning} +:applies_to: serverless: ga + +This warning applies to serverless GA. +::: -Longer content can be collapsed to take less space. +:::{tip} +:applies_to: { ess:, ece: } -Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +This tip applies to ECH and ECE. ::: ``` diff --git a/src/Elastic.Documentation.Site/Assets/markdown/admonition.css b/src/Elastic.Documentation.Site/Assets/markdown/admonition.css index 61fc059c0..f930e7fd7 100644 --- a/src/Elastic.Documentation.Site/Assets/markdown/admonition.css +++ b/src/Elastic.Documentation.Site/Assets/markdown/admonition.css @@ -2,10 +2,22 @@ .admonition { @apply mt-4 rounded-sm border-1 pb-4; + .admonition-header { + @apply flex items-center gap-3 rounded-t-sm px-4 py-2 font-bold; + } + .admonition-title { - @apply flex items-center gap-2 rounded-t-sm px-4 py-2 font-bold; - svg { - @apply size-6; + @apply text-sm tracking-wider uppercase; + } + + .applies-admonition { + @apply flex items-center gap-1 font-normal; + .applicable-name, + .applicable-meta { + @apply text-xs; + } + .applicable-separator { + mix-blend-mode: multiply; } } .admonition-content { @@ -18,39 +30,57 @@ &.note { @apply border-blue-elastic-40 bg-blue-elastic-10; - .admonition-title { + + .admonition-header { @apply text-blue-elastic-110 bg-blue-elastic-20; + + .applicable-info { + @apply border-blue-elastic-40; + } } } &.tip { @apply border-teal-40 bg-teal-10; - .admonition-title { + .admonition-header { @apply text-teal-110 bg-teal-20; + + .applicable-info { + @apply border-teal-40; + } } } &.warning { @apply border-yellow-40 bg-yellow-10; - .admonition-title { + .admonition-header { @apply text-yellow-110 bg-yellow-20; + .applicable-info { + @apply border-yellow-40; + } } } &.important { @apply border-purple-40 bg-purple-10; - .admonition-title { + .admonition-header { @apply text-purple-110 bg-purple-20; + .applicable-info { + @apply border-purple-40; + } } } &.plain { @apply border-grey-40 bg-grey-10; - .admonition-title { + .admonition-header { @apply text-grey-110 bg-grey-20; + .applicable-info { + @apply border-grey-40; + } } } } diff --git a/src/Elastic.Markdown/Myst/Directives/Admonition/AdmonitionBlock.cs b/src/Elastic.Markdown/Myst/Directives/Admonition/AdmonitionBlock.cs index f84bbf709..6446429dc 100644 --- a/src/Elastic.Markdown/Myst/Directives/Admonition/AdmonitionBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/Admonition/AdmonitionBlock.cs @@ -9,7 +9,7 @@ namespace Elastic.Markdown.Myst.Directives.Admonition; public class DropdownBlock(DirectiveBlockParser parser, ParserContext context) : AdmonitionBlock(parser, "dropdown", context); -public class AdmonitionBlock : DirectiveBlock, IBlockTitle +public class AdmonitionBlock : DirectiveBlock, IBlockTitle, IBlockAppliesTo { public AdmonitionBlock(DirectiveBlockParser parser, string admonition, ParserContext context) : base(parser, context) { @@ -65,6 +65,7 @@ public override void FinalizeAndValidate(ParserContext context) catch { // If parsing fails, return null + // Note: Error handling is done in the YamlSerialization.Deserialize method return null; } } diff --git a/src/Elastic.Markdown/Myst/Directives/Admonition/AdmonitionView.cshtml b/src/Elastic.Markdown/Myst/Directives/Admonition/AdmonitionView.cshtml index 517a86fb5..ba2adf61e 100644 --- a/src/Elastic.Markdown/Myst/Directives/Admonition/AdmonitionView.cshtml +++ b/src/Elastic.Markdown/Myst/Directives/Admonition/AdmonitionView.cshtml @@ -1,38 +1,21 @@ +@using Elastic.Markdown.Myst.Components @inherits RazorSlice
-
- - @{ - switch (Model.Directive) - { - case "tip": - - - - break; - case "warning": - - - - break; - case "note": - - - - break; - case "important": - - - - break; - default: - - - - break; - } +
+ @Model.Title + @if (Model.AppliesTo is not null) + { + + @await RenderPartialAsync(ApplicableToComponent.Create(new ApplicableToViewModel + { + AppliesTo = Model.AppliesTo, + Inline = true, + ShowTooltip = true, + VersionsConfig = Model.BuildContext.VersionsConfiguration + })) + + } - @Model.Title
@Model.RenderBlock()
diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveBlock.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveBlock.cs index 4cda54a91..31765c359 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveBlock.cs @@ -18,6 +18,11 @@ public interface IBlockTitle string Title { get; } } +public interface IBlockAppliesTo +{ + string? AppliesToDefinition { get; } +} + public interface IBlockExtension : IBlock { BuildContext Build { get; } diff --git a/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmBlockRenderers.cs b/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmBlockRenderers.cs index 70abcea02..7cb3c3a24 100644 --- a/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmBlockRenderers.cs +++ b/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmBlockRenderers.cs @@ -383,6 +383,13 @@ protected override void Write(LlmMarkdownRenderer renderer, DirectiveBlock obj) break; } + switch (obj) + { + case IBlockAppliesTo appliesBlock when !string.IsNullOrEmpty(appliesBlock.AppliesToDefinition): + renderer.Writer.Write($" applies-to=\"{appliesBlock.AppliesToDefinition}\""); + break; + } + renderer.WriteLine(">"); renderer.EnsureLine(); diff --git a/tests/Elastic.Markdown.Tests/Directives/AdmonitionTests.cs b/tests/Elastic.Markdown.Tests/Directives/AdmonitionTests.cs index 255729f01..7f976b892 100644 --- a/tests/Elastic.Markdown.Tests/Directives/AdmonitionTests.cs +++ b/tests/Elastic.Markdown.Tests/Directives/AdmonitionTests.cs @@ -236,3 +236,163 @@ public void ParsesAppliesToWithComplexValue() Block!.AppliesTo.Should().NotBeNull(); } } + +public class NoteAppliesToTests(ITestOutputHelper output) : DirectiveTest(output, +""" +:::{note} +:applies_to: stack: ga +This is a note with applies_to information +::: +A regular paragraph. +""" +) +{ + [Fact] + public void SetsCorrectAdmonitionType() => Block!.Admonition.Should().Be("note"); + + [Fact] + public void SetsTitle() => Block!.Title.Should().Be("Note"); + + [Fact] + public void SetsAppliesToDefinition() => Block!.AppliesToDefinition.Should().Be("stack: ga"); + + [Fact] + public void ParsesAppliesTo() => Block!.AppliesTo.Should().NotBeNull(); + + [Fact] + public void RendersAppliesToInHtml() + { + var html = Html; + html.Should().Contain("applies applies-admonition"); + html.Should().Contain("admonition-title__separator"); + html.Should().Contain("applicable-info"); + } +} + +public class WarningAppliesToTests(ITestOutputHelper output) : DirectiveTest(output, +""" +:::{warning} +:applies_to: stack: ga +This is a warning with applies_to information +::: +A regular paragraph. +""" +) +{ + [Fact] + public void SetsCorrectAdmonitionType() => Block!.Admonition.Should().Be("warning"); + + [Fact] + public void SetsTitle() => Block!.Title.Should().Be("Warning"); + + [Fact] + public void SetsAppliesToDefinition() => Block!.AppliesToDefinition.Should().Be("stack: ga"); + + [Fact] + public void ParsesAppliesTo() => Block!.AppliesTo.Should().NotBeNull(); + + [Fact] + public void RendersAppliesToInHtml() + { + var html = Html; + html.Should().Contain("applies applies-admonition"); + html.Should().Contain("admonition-title__separator"); + html.Should().Contain("applicable-info"); + } +} + +public class TipAppliesToTests(ITestOutputHelper output) : DirectiveTest(output, +""" +:::{tip} +:applies_to: stack: ga +This is a tip with applies_to information +::: +A regular paragraph. +""" +) +{ + [Fact] + public void SetsCorrectAdmonitionType() => Block!.Admonition.Should().Be("tip"); + + [Fact] + public void SetsTitle() => Block!.Title.Should().Be("Tip"); + + [Fact] + public void SetsAppliesToDefinition() => Block!.AppliesToDefinition.Should().Be("stack: ga"); + + [Fact] + public void ParsesAppliesTo() => Block!.AppliesTo.Should().NotBeNull(); + + [Fact] + public void RendersAppliesToInHtml() + { + var html = Html; + html.Should().Contain("applies applies-admonition"); + html.Should().Contain("admonition-title__separator"); + html.Should().Contain("applicable-info"); + } +} + +public class ImportantAppliesToTests(ITestOutputHelper output) : DirectiveTest(output, +""" +:::{important} +:applies_to: stack: ga +This is an important notice with applies_to information +::: +A regular paragraph. +""" +) +{ + [Fact] + public void SetsCorrectAdmonitionType() => Block!.Admonition.Should().Be("important"); + + [Fact] + public void SetsTitle() => Block!.Title.Should().Be("Important"); + + [Fact] + public void SetsAppliesToDefinition() => Block!.AppliesToDefinition.Should().Be("stack: ga"); + + [Fact] + public void ParsesAppliesTo() => Block!.AppliesTo.Should().NotBeNull(); + + [Fact] + public void RendersAppliesToInHtml() + { + var html = Html; + html.Should().Contain("applies applies-admonition"); + html.Should().Contain("admonition-title__separator"); + html.Should().Contain("applicable-info"); + } +} + +public class AdmonitionAppliesToTests(ITestOutputHelper output) : DirectiveTest(output, +""" +:::{admonition} Custom Admonition +:applies_to: stack: ga +This is a custom admonition with applies_to information +::: +A regular paragraph. +""" +) +{ + [Fact] + public void SetsCorrectAdmonitionType() => Block!.Admonition.Should().Be("admonition"); + + [Fact] + public void SetsCustomTitle() => Block!.Title.Should().Be("Custom Admonition"); + + [Fact] + public void SetsAppliesToDefinition() => Block!.AppliesToDefinition.Should().Be("stack: ga"); + + [Fact] + public void ParsesAppliesTo() => Block!.AppliesTo.Should().NotBeNull(); + + [Fact] + public void RendersAppliesToInHtml() + { + var html = Html; + html.Should().Contain("applies applies-admonition"); + html.Should().Contain("admonition-title__separator"); + html.Should().Contain("applicable-info"); + } +} diff --git a/tests/authoring/Blocks/Admonitions.fs b/tests/authoring/Blocks/Admonitions.fs index d4e6c5d8b..d8cdd4fc1 100644 --- a/tests/authoring/Blocks/Admonitions.fs +++ b/tests/authoring/Blocks/Admonitions.fs @@ -21,9 +21,6 @@ type ``admonition in list`` () =
  • List Item 1
    - - - Note
    @@ -36,6 +33,103 @@ type ``admonition in list`` () = [] let ``has no errors`` () = markdown |> hasNoErrors +type ``admonition with applies_to`` () = + static let markdown = Setup.Markdown """ +:::{note} +:applies_to: stack: ga +This is a note with applies_to information. +::: +:::{warning} +:applies_to: serverless: ga +This is a warning with applies_to information. +::: +:::{tip} +:applies_to: elasticsearch: preview +This is a tip with applies_to information. +::: +:::{important} +:applies_to: stack: ga, serverless: ga +This is an important notice with applies_to information. +::: +:::{admonition} Custom Admonition +:applies_to: stack: ga, serverless: ga, elasticsearch: preview +This is a custom admonition with applies_to information. +::: +""" + + [] + let ``validate HTML`` () = + markdown |> convertsToHtml """ +
    +
    + + + Stack + + + + + + Note +
    +
    +

    This is a note with applies_to information.

    +
    +
    +
    +
    + + + Serverless + + + + + + Warning +
    +
    +

    This is a warning with applies_to information.

    +
    +
    +
    +
    + + + Serverless Elasticsearch + + + Preview + + + + + Tip +
    +
    +

    This is a tip with applies_to information.

    +
    +
    +
    +
    + Important +
    +
    +

    This is an important notice with applies_to information.

    +
    +
    +
    +
    + Custom Admonition +
    +
    +

    This is a custom admonition with applies_to information.

    +
    +
    """ + + [] + let ``has no errors`` () = markdown |> hasNoErrors + type ``nested admonition in list`` () = static let markdown = Setup.Markdown """ :::{note} @@ -55,9 +149,6 @@ type ``nested admonition in list`` () = markdown |> convertsToHtml """
    - - - Note
    @@ -65,9 +156,6 @@ type ``nested admonition in list`` () =
  • List Item 1
    - - - Note
    @@ -114,9 +202,6 @@ type ``nested admonition in list 2`` () =
    - - - Note
    @@ -124,9 +209,6 @@ type ``nested admonition in list 2`` () =
  • List Item 1
    - - - Note
    @@ -172,9 +254,6 @@ type ``nested admonition in list 3`` () =
    - - - Note
    @@ -182,9 +261,6 @@ type ``nested admonition in list 3`` () =
  • List Item 1
    - - - Note
    diff --git a/tests/authoring/Container/DefinitionLists.fs b/tests/authoring/Container/DefinitionLists.fs index 389f019d1..8ef788a2c 100644 --- a/tests/authoring/Container/DefinitionLists.fs +++ b/tests/authoring/Container/DefinitionLists.fs @@ -51,13 +51,9 @@ This is my `definition`

    And this is the definition body - Which may contain multiple lines -

    + Which may contain multiple lines

    - - - Note
    diff --git a/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs b/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs index 7daec9c86..01e9f6992 100644 --- a/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs +++ b/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs @@ -187,6 +187,54 @@ Here is a list: """ +type ``admonition directive with applies_to`` () = + static let markdown = Setup.Document """ +:::{note} +:applies_to: stack: ga +This is a note admonition with applies_to information. +::: +:::{warning} +:applies_to: serverless: ga +This is a warning admonition with applies_to information. +::: +:::{tip} +:applies_to: elasticsearch: preview +This is a tip admonition with applies_to information. +::: +:::{important} +:applies_to: stack: ga, serverless: ga +This is an important admonition with applies_to information. +::: +:::{admonition} Custom Admonition +:applies_to: stack: ga, serverless: ga, elasticsearch: preview +This is a custom admonition with applies_to information. +::: +""" + + [] + let ``renders correctly with applies_to information`` () = + markdown |> convertsToNewLLM """ + + This is a note admonition with applies_to information. + + + + This is a warning admonition with applies_to information. + + + + This is a tip admonition with applies_to information. + + + + This is an important admonition with applies_to information. + + + + This is a custom admonition with applies_to information. + +""" + type ``image directive`` () = static let markdown = Setup.Document """ ```{image} /path/to/image.png @@ -361,6 +409,22 @@ This is where the content for the dropdown goes. """ +type ``dropdown with applies_to`` () = + static let markdown = Setup.Document """ +:::{dropdown} Dropdown title +:applies_to: stack: 9.1.0 +This is where the content for the dropdown goes. +::: +""" + + [] + let ``rendered correctly`` () = + markdown |> convertsToNewLLM """ + + This is where the content for the dropdown goes. + +""" + type ``definition list`` () = static let markdown = Setup.Document """ `First Term`