diff --git a/docs/content/en/content-management/shortcodes.md b/docs/content/en/content-management/shortcodes.md index 8e345f2fb36..7a589a34008 100644 --- a/docs/content/en/content-management/shortcodes.md +++ b/docs/content/en/content-management/shortcodes.md @@ -94,6 +94,40 @@ Example usage: Although you can call this shortcode using the `{{}}` notation, computationally it is more efficient to call it using the `{{%/* */%}}` notation as shown above. +### details + +{{< new-in 0.140.0 >}} + +{{% note %}} +To override Hugo's embedded `details` shortcode, copy the [source code] to a file with the same name in the layouts/shortcodes directory. + +This may be useful if you are wanting access to more global HTML attributes. + +[source code]: {{% eturl details %}} +{{% /note %}} + +Use the `details` shortcode to generate a collapsible details HTML element. For example: + +```text +{{}} +Showing custom `summary` text. +{{}} +``` + +Additional examples can be found in the source code. The `details` shortcode can use the following named arguments: + +summary +: (`string`) Optional. Specifies the content of the child summary element. Default is "Details" + +open +: (`bool`) Optional. Whether to initially display the contents of the details element. Default is `false`. + +name +: (`string`) Optional. The value of the element's name attribute. + +class +: (`string`) Optional. The value of the element's class attribute. + ### figure {{% note %}} diff --git a/docs/data/embedded_template_urls.toml b/docs/data/embedded_template_urls.toml index 38b437fe15d..b7247f2727f 100644 --- a/docs/data/embedded_template_urls.toml +++ b/docs/data/embedded_template_urls.toml @@ -25,6 +25,7 @@ # Shortcodes 'comment' = 'shortcodes/comment.html' +'details' = 'shortcodes/details.html' 'figure' = 'shortcodes/figure.html' 'gist' = 'shortcodes/gist.html' 'highlight' = 'shortcodes/highlight.html' diff --git a/tpl/tplimpl/embedded/templates/shortcodes/details.html b/tpl/tplimpl/embedded/templates/shortcodes/details.html new file mode 100644 index 00000000000..932289517a6 --- /dev/null +++ b/tpl/tplimpl/embedded/templates/shortcodes/details.html @@ -0,0 +1,68 @@ +{{- /* +Renders an HTML details element. + +@param {string} [summary] The content of the child summary element. +@param {bool} [open=false] Whether to initially display the contents of the details element. +@param {string} [class] The value of the element's class attribute. +@param {string} [name] The value of the element's name attribute. + +@reference https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details + +@examples + + {{< details >}} + A basic collapsible section. + {{< /details >}} + + {{< details summary="Custom Summary Text" >}} + Showing custom `summary` text. + {{< /details >}} + + {{< details summary="Open Details" open=true >}} + Contents displayed initially by using `open`. + {{< /details >}} + + {{< details summary="Styled Content" class="my-custom-class" >}} + Content can be styled with CSS by specifying a `class`. + + Target details element: + + ```css + details.my-custom-class { } + ``` + + Target summary element: + + ```css + details.my-custom-class > summary > * { } + ``` + + Target inner content: + + ```css + details.my-custom-class > :not(summary) { } + ``` + {{< /details >}} + + {{< details summary="Grouped Details" name="my-details" >}} + Specifying a `name` allows elements to be connected, with only one able to be open at a time. + {{< /details >}} + +*/}} + +{{- /* Get arguments. */}} +{{- $summary := or (.Get "summary") (T "shortcodes.details") "Details" }} +{{- $class := or (.Get "class") "" }} +{{- $name := or (.Get "name") "" }} +{{- $open := false }} +{{- if in (slice "false" false 0) (.Get "open") }} + {{- $open = false }} +{{- else if in (slice "true" true 1) (.Get "open")}} + {{- $open = true }} +{{- end }} + +{{- /* Render. */}} + + {{ $summary | .Page.RenderString }} + {{ .Inner | .Page.RenderString (dict "display" "block") -}} + \ No newline at end of file diff --git a/tpl/tplimpl/tplimpl_integration_test.go b/tpl/tplimpl/tplimpl_integration_test.go index c7e118e8259..36355598df7 100644 --- a/tpl/tplimpl/tplimpl_integration_test.go +++ b/tpl/tplimpl/tplimpl_integration_test.go @@ -600,3 +600,118 @@ a{{< comment >}}b{{< /comment >}}c b := hugolib.Test(t, files) b.AssertFileContent("public/index.html", "

ac

") } + +func TestDetailsShortcode(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['rss','section','sitemap','taxonomy','term'] +defaultContentLanguage = "en" +[languages] + [languages.en] + weight = 1 + [languages.es] + weight = 2 +-- i18n/en.toml -- +[shortcodes.details] +other = "Details" +-- i18n/es.toml -- +[shortcodes.details] +other = "Detalles" +-- layouts/_default/single.html -- +{{ .Content }} +-- content/d1.md -- +--- +title: Default State Test +--- +{{< details >}} +Basic example without summary +{{< /details >}} +-- content/d2.md -- +--- +title: Custom Summary Test +--- +{{< details summary="Custom Summary" >}} +Example with custom summary text +{{< /details >}} +-- content/d3.md -- +--- +title: Open State Test +--- +{{< details summary="Test Open State" open="true" >}} +Example with open state +{{< /details >}} +-- content/d4.md -- +--- +title: Attributes Test +--- +{{< details summary="Test Attribute sanitization" style="color: red; font-weight: bold; background-color: #eee" onclick="alert('test')" >}} +Example testing attribute sanitization +{{< /details >}} +-- content/d5.md -- +--- +title: Class Test +--- +{{< details class="custom-class" >}} +Example with allowed class attribute +{{< /details >}} +-- content/d6.md -- +--- +title: Name Test +--- +{{< details name="custom-name" >}} +Example with allowed name attribute +{{< /details >}} +-- content/d7.es.md -- +--- +title: Localization Test +--- +{{< details >}} +Localization example without summary +{{< /details >}} +` + b := hugolib.Test(t, files) + + // Test1: default state (closed by default) + b.AssertFileContentEquals("public/d1/index.html", + "\n
\n Details\n

Basic example without summary

\n
\n", + ) + content1 := b.FileContent("public/d1/index.html") + c := qt.New(t) + c.Assert(content1, qt.Not(qt.Contains), "open") + + // Test2: custom summary + b.AssertFileContentEquals("public/d2/index.html", + "\n
\n Custom Summary\n

Example with custom summary text

\n
\n", + ) + + // Test3: open state + b.AssertFileContentEquals("public/d3/index.html", + "\n
\n Test Open State\n

Example with open state

\n
\n", + ) + + // Test4: Test sanitization + b.AssertFileContentEquals("public/d4/index.html", + "\n
\n Test Attribute sanitization\n

Example testing attribute sanitization

\n
\n", + ) + content4 := b.FileContent("public/d4/index.html") + c.Assert(content4, qt.Not(qt.Contains), "style") + c.Assert(content4, qt.Not(qt.Contains), "onclick") + c.Assert(content4, qt.Not(qt.Contains), "alert") + + // Test5: class attribute + b.AssertFileContentEquals("public/d5/index.html", + "\n
\n Details\n

Example with allowed class attribute

\n
\n", + ) + + // Test6: name attribute + b.AssertFileContentEquals("public/d6/index.html", + "\n
\n Details\n

Example with allowed name attribute

\n
\n", + ) + + // Test7: localization + b.AssertFileContentEquals("public/es/d7/index.html", + "\n
\n Detalles\n

Localization example without summary

\n
\n", + ) +}