]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
tpl/tplimpl: Add details shortcode
authorracehd <7813112-racehd@users.noreply.gitlab.com>
Sun, 1 Dec 2024 15:30:34 +0000 (10:30 -0500)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Fri, 13 Dec 2024 08:24:20 +0000 (09:24 +0100)
- Add new shortcode to render details HTML element.
- Implement integration tests to check: default state, custom summary, open state,  attribute sanitization, allowed attributes, and localization of default summary text.
- Update docs to include details shortcode.

Closes # 13090

docs/content/en/content-management/shortcodes.md
docs/data/embedded_template_urls.toml
tpl/tplimpl/embedded/templates/shortcodes/details.html [new file with mode: 0644]
tpl/tplimpl/tplimpl_integration_test.go

index 8e345f2fb363809958f91d69cd130340b3b5388b..7a589a340089c31269be6df6890fc8d4b6ecdf26 100644 (file)
@@ -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
+{{</* details summary="Custom Summary Text" */>}}
+Showing custom `summary` text.
+{{</* /details */>}}
+```
+
+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 %}}
index 38b437fe15d82bd3bf6b5cd6d12c084cbb8b75b8..b7247f2727fba586f75aa180c6e9562892131996 100644 (file)
@@ -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 (file)
index 0000000..9322895
--- /dev/null
@@ -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. */}}
+<details{{- if $open }} open{{ end }}{{- if $name }} name="{{ $name }}"{{- end }}{{- if $class }} class="{{ $class }}"{{- end }}>
+    <summary>{{ $summary | .Page.RenderString }}</summary>
+    {{ .Inner | .Page.RenderString (dict "display" "block") -}}
+</details>
\ No newline at end of file
index c7e118e825915fead0136d4c9058298fb1262a8a..36355598df7eb8a2dc5c93ee4c27121d325004fe 100644 (file)
@@ -600,3 +600,118 @@ a{{< comment >}}b{{< /comment >}}c
        b := hugolib.Test(t, files)
        b.AssertFileContent("public/index.html", "<p>ac</p>")
 }
+
+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<details>\n    <summary>Details</summary>\n    <p>Basic example without summary</p>\n</details>\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<details>\n    <summary>Custom Summary</summary>\n    <p>Example with custom summary text</p>\n</details>\n",
+       )
+
+       // Test3: open state
+       b.AssertFileContentEquals("public/d3/index.html",
+               "\n<details open>\n    <summary>Test Open State</summary>\n    <p>Example with open state</p>\n</details>\n",
+       )
+
+       // Test4: Test sanitization
+       b.AssertFileContentEquals("public/d4/index.html",
+               "\n<details>\n    <summary>Test Attribute sanitization</summary>\n    <p>Example testing attribute sanitization</p>\n</details>\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<details class=\"custom-class\">\n    <summary>Details</summary>\n    <p>Example with allowed class attribute</p>\n</details>\n",
+       )
+
+       // Test6: name attribute
+       b.AssertFileContentEquals("public/d6/index.html",
+               "\n<details name=\"custom-name\">\n    <summary>Details</summary>\n    <p>Example with allowed name attribute</p>\n</details>\n",
+       )
+
+       // Test7: localization
+       b.AssertFileContentEquals("public/es/d7/index.html",
+               "\n<details>\n    <summary>Detalles</summary>\n    <p>Localization example without summary</p>\n</details>\n",
+       )
+}