Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 29 additions & 13 deletions preview-src/property-tooltips-test.adoc
Original file line number Diff line number Diff line change
@@ -1,45 +1,65 @@
= Property Tooltips Test
:page-layout: default

Test page for Redpanda configuration property tooltips. Property tooltips are enabled by default on all pages. To disable on a specific page, use `:page-disable-property-tooltips: true`.
Test page for Redpanda configuration property tooltips. Tooltips are opt-in: only code elements emitted by the `prop:` AsciiDoc macro (class `property-ref` plus a `data-property-name` attribute) are decorated. This page simulates the macro's output with pass-through HTML because the UI preview doesn't run the docs macros. To disable tooltips on a specific page, use `:page-disable-property-tooltips: true`.

TIP: Hover over property names to see documentation tooltips!

== Cluster Properties in Paragraphs

When configuring Redpanda, you may need to adjust `log_segment_size` for optimal storage performance. The default segment size works for most workloads, but large messages may benefit from larger segments.
When configuring Redpanda, you may need to adjust +++<code class="property-ref" data-property-name="log_segment_size">log_segment_size</code>+++ for optimal storage performance. The default segment size works for most workloads, but large messages may benefit from larger segments.

To enable authentication, set `enable_sasl` to true. This requires clients to authenticate using SASL mechanisms.
To enable authentication, set +++<code class="property-ref" data-property-name="enable_sasl">enable_sasl</code>+++ to true. This requires clients to authenticate using SASL mechanisms.

== Topic Properties in Lists

Topic-level configuration properties:

* `retention_bytes` - Controls maximum partition size before deletion
* +++<code class="property-ref" data-property-name="retention_bytes">retention_bytes</code>+++ - Controls maximum partition size before deletion
* This is a topic-scoped property that can override cluster defaults

== Dotted Property Names (Topic-scoped)

These properties use dot notation and test the anchor slugification fix:

* `redpanda.storage.mode` - Controls storage mode (local vs tiered)
* `redpanda.remote.read` - Enables remote reads from object storage
* +++<code class="property-ref" data-property-name="redpanda.storage.mode">redpanda.storage.mode</code>+++ - Controls storage mode (local vs tiered)
* +++<code class="property-ref" data-property-name="redpanda.remote.read">redpanda.remote.read</code>+++ - Enables remote reads from object storage

The "View full documentation" link for `redpanda.storage.mode` should generate an anchor like `#redpanda-storage-mode` (dots replaced with hyphens), not `#redpanda.storage.mode`.

== Enterprise and Self-Hosted Properties

Some properties are enterprise-only:

* `cloud_storage_enabled` - Requires Enterprise license (self-hosted only)
* +++<code class="property-ref" data-property-name="cloud_storage_enabled">cloud_storage_enabled</code>+++ - Requires Enterprise license (self-hosted only)

== Broker Properties

Per-broker settings like `kafka_connections_max` control resource limits.
Per-broker settings like +++<code class="property-ref" data-property-name="kafka_connections_max">kafka_connections_max</code>+++ control resource limits.

== Macro Calls Inside Tooltip Descriptions

The +++<code class="property-ref" data-property-name="group_max_session_timeout_ms">group_max_session_timeout_ms</code>+++ tooltip description contains a `prop:` macro call and a legacy `config_ref:` call. Both must render as plain code in the tooltip, never as raw macro text.

== Deprecated Properties

The property `group_max_session_timeout_ms` is deprecated but still shown with appropriate styling.
The property +++<code class="property-ref" data-property-name="group_max_session_timeout_ms">group_max_session_timeout_ms</code>+++ is deprecated but still shown with appropriate styling.

== Display text override

The macro's text attribute changes the display while keeping the lookup name: +++<code class="property-ref" data-property-name="cloud_storage_enabled">object storage flag</code>+++ should show the Tiered Storage property tooltip.

== Unmarked property names get NO tooltip (the old false-positive case)

These are real property names in plain backticks, exactly what used to be auto-matched. With opt-in marking they must NOT have tooltips:

* `log_segment_size` - No tooltip (not marked)
* `enable_sasl` - No tooltip (not marked)
* The `admin` listener in a Helm values file - No tooltip (ambiguous word)

== Marked but unknown property

A marked element whose name is not in the published data renders untouched: +++<code class="property-ref" data-property-name="this_is_not_a_property">this_is_not_a_property</code>+++ - No tooltip.

== Properties in Code Blocks

Expand All @@ -57,7 +77,3 @@ Regular code elements should NOT have tooltips:
* `this_is_not_a_property` - No tooltip
* `some_random_function()` - No tooltip
* `myVariable` - No tooltip

== Mixed Content

In a paragraph with both properties and regular text: Set `enable_sasl` to configure authentication, then use `kubectl apply` to deploy. Only `enable_sasl` should have a tooltip.
8 changes: 6 additions & 2 deletions src/helpers/resolve-resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,16 @@ function logUnresolved (resource, reason, page, context, logger) {
module.exports = (resource, { data, hash: context }) => {
const { page, logger } = data.root || {}
const fallbackUrl = context?.fallback
// An explicitly provided fallback (even an empty one) marks the resolution
// as optional: the caller handles the miss, so an unresolved target is
// expected on components that don't publish the resource and must not warn.
const optional = context ? 'fallback' in context : false

// Log and return undefined if resource is not provided
if (!resource || typeof resource !== 'string') {
// Only log if we have page context (not during initial template compilation)
if (page && resource === undefined) {
logUnresolved('undefined', 'attribute not defined (check page attributes)', page, context, logger)
if (!optional) logUnresolved('undefined', 'attribute not defined (check page attributes)', page, context, logger)
}
return fallbackUrl || undefined
}
Expand Down Expand Up @@ -141,7 +145,7 @@ module.exports = (resource, { data, hash: context }) => {
result = fallbackUrl
} else {
// Log warning for unresolved resource (only if no fallback provided)
logUnresolved(resolvedResource, 'target not found in content catalog', page, context, logger)
if (!optional) logUnresolved(resolvedResource, 'target not found in content catalog', page, context, logger)
result = resource
}

Expand Down
66 changes: 52 additions & 14 deletions src/js/19-property-tooltips.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
/**
* Redpanda Property Tooltips
*
* Adds hover documentation tooltips to configuration property names.
* Enabled by default on all pages. Disable on specific pages with:
* Adds hover documentation tooltips to configuration property references.
* Marking is opt-in: only code elements emitted by the prop: AsciiDoc macro
* (class property-ref plus a data-property-name attribute) are decorated.
* Plain backticked words are never matched, so ambiguous terms such as
* admin or rack in Helm or feature contexts don't pick up wrong tooltips.
* Disable on specific pages with:
* :page-disable-property-tooltips: true
*/

Expand Down Expand Up @@ -34,6 +38,21 @@
return null
}

/**
* Get the component-local property pages base URL from meta tag.
* head-meta resolves reference:properties/cluster-properties.adoc in the
* current page's own component, so cloud pages link to cloud's property
* pages, streaming pages to streaming's, and so on.
*/
function getPropertiesPagesUrl () {
var meta = document.querySelector('meta[name="properties-pages-url"]')
// Ignore unresolved placeholders (the UI preview resolver emits '#').
if (meta && meta.content && meta.content.indexOf('cluster-properties') !== -1) {
return meta.content
}
return null
}

/**
* Get the latest Redpanda tag from meta tag (for cache versioning)
*/
Expand Down Expand Up @@ -276,14 +295,21 @@
parts.push('<div class="prop-tooltip-range"><strong>Range:</strong> ' + range.join(', ') + '</div>')
}

// Link to full documentation (use current page version)
// Link to full documentation, relative to the current page's component
var scope = prop.configScope || 'cluster'
var version = getDocVersion()
// AsciiDoc auto-ID generation: dots are removed, underscores become hyphens
// e.g., "redpanda.storage.mode" -> "redpandastoragemode"
// e.g., "log_retention_ms" -> "log-retention-ms"
var anchor = prop.name.replace(/\./g, '').replace(/_/g, '-')
var docUrl = '/' + version + '/reference/properties/' + scope + '-properties/#' + anchor
var pagesUrl = getPropertiesPagesUrl()
var docUrl
if (pagesUrl) {
// Swap the scope into the component-resolved cluster-properties URL.
docUrl = pagesUrl.replace('cluster-properties', scope + '-properties') + '#' + anchor
} else {
// Fallback for pages without the meta tag: streaming URL space.
docUrl = '/' + getDocVersion() + '/reference/properties/' + scope + '-properties/#' + anchor
}
parts.push('<a href="' + escapeHtml(docUrl) + '" class="prop-tooltip-link">View full documentation &rarr;</a>')

return '<div class="property-doc-tooltip">' + parts.join('') + '</div>'
Expand Down Expand Up @@ -329,8 +355,21 @@
// Convert backticks to code tags
var withCode = escaped.replace(/`([^`]+)`/g, '<code>$1</code>')

// Render prop macro calls from generated descriptions as code (the
// text= attribute wins as the display, matching the macro's rendering)
var withProps = withCode.replace(/prop:([^[\s]+)\[([^\]]*)\]/g, function (match, name, attrs) {
var textMatch = attrs.match(/text=([^,\]]+)/)
return '<code>' + (textMatch ? textMatch[1] : name) + '</code>'
})

// Legacy config_ref macro calls survive in older published JSONs
withProps = withProps.replace(/config_ref:([^[,]+)(?:,[^[]*)?\[([^\]]*)\]/g, function (match, name, payload) {
var display = payload.replace(/^`|`$/g, '') || name
return '<code>' + display + '</code>'
})

// Fallback: resolve any remaining xrefs that weren't pre-resolved
var withXrefs = withCode.replace(
var withXrefs = withProps.replace(
/xref:\.?\/?([^[]+)\.adoc(?:#([^[]*))?\[([^\]]+)\]/g,
function (match, path, anchor, display) {
var href = path.replace(/^\.\//, '') + '/'
Expand Down Expand Up @@ -363,14 +402,13 @@
return
}

// Create a Set for fast lookup
var propertyNames = new Set(Object.keys(properties))

// Scope: opt-in pages look at all <code> elements in the article
// Scope: only elements marked by the prop: macro are decorated
var article = document.querySelector('article.doc')
if (!article) return

var codeElements = article.querySelectorAll('code:not(.has-property-tooltip)')
var codeElements = article.querySelectorAll(
'code[data-property-name]:not(.has-property-tooltip), code.property-ref:not(.has-property-tooltip)'
)
var isTouch = isTouchDevice()

var getTippyConfig = function (content) {
Expand All @@ -388,10 +426,10 @@
}

codeElements.forEach(function (codeEl) {
var text = codeEl.textContent.trim()
var text = codeEl.getAttribute('data-property-name') || codeEl.textContent.trim()

// Check if this code element matches a property name
if (propertyNames.has(text)) {
// Look up the marked property in the published data
if (Object.prototype.hasOwnProperty.call(properties, text)) {
var prop = properties[text]
var tooltipContent = createPropertyTooltip(prop)

Expand Down
10 changes: 10 additions & 0 deletions src/partials/head-meta.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@
{{/if}}
{{/with}}
{{/if}}
{{!--
Component-local property pages base for tooltip doc links. Resolved in the
current page's own component (resolve-resource defaults to the page's
component and version), so cloud pages link to cloud's property pages and
streaming pages to streaming's. Omitted when the component has no property
pages; the JS then falls back to streaming's URL space.
--}}
{{#with (resolve-resource 'reference:properties/cluster-properties.adoc' fallback='')}}
<meta name="properties-pages-url" content="{{this}}">
{{/with}}
{{!--
Connect JSON URL for Bloblang tooltips

Expand Down
4 changes: 2 additions & 2 deletions src/static/redpanda-properties.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
"name": "group_max_session_timeout_ms",
"type": "integer",
"default": 300000,
"description": "The maximum session timeout for consumer groups. If a consumer does not send a heartbeat within this time, it is considered dead and removed from the group.",
"description": "The maximum session timeout for consumer groups. If a consumer does not send a heartbeat within this time, it is considered dead and removed from the group. Works together with prop:log_segment_size[link=true] and the legacy config_ref:enable_sasl,true,cluster-properties[`enable_sasl`] form renders too.",
"config_scope": "cluster",
"needs_restart": false,
"cloud_supported": true,
Expand Down Expand Up @@ -102,4 +102,4 @@
"visibility": "user"
}
}
}
}