Skip to content
Merged
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
2 changes: 2 additions & 0 deletions .github/workflows/validate-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ jobs:
run: npm run test:hero-scroll
- name: Test docs page weight (lazy images, trimmed Prism, on-demand highlighting, head)
run: npm run test:docs-page-weight
- name: Test the introduced-in version badge
run: npm run test:introduced-in-badge
- name: Test the Ask AI latest-version tool
run: npm run test:latest-version-tool
- name: Cache generated files
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,12 @@
"test:hero-scroll": "node --test tests/hero-scroll/*.test.js",
"test:docs-page-weight": "node --test tests/docs-page-weight/*.test.js",
"test:callout-preservation": "npx gulp generate:prism && node tests/callout-preservation/test-runner.js",
"test:all": "npm run test:playground && npm run test:interactive && npm run test:negative-cache && npm run test:head-meta && npm run test:latest-version-tool && npm run test:property-tooltips && npm run test:signin-nudge && npm run test:chat-panel-navigation && npm run test:toc-collapsible && npm run test:toc-active-anchor && npm run test:nav-scroll && npm run test:kapa-source-groups && npm run test:tooltip-touch && npm run test:tooltip-single-open && npm run test:lazy-askai && npm run test:nav-lazy-buckets && npm run test:has-code-blocks && npm run test:docs-page-weight && npm run test:anon-quota && npm run test:callout-preservation",
"test:all": "npm run test:playground && npm run test:interactive && npm run test:negative-cache && npm run test:head-meta && npm run test:latest-version-tool && npm run test:introduced-in-badge && npm run test:property-tooltips && npm run test:signin-nudge && npm run test:chat-panel-navigation && npm run test:toc-collapsible && npm run test:toc-active-anchor && npm run test:nav-scroll && npm run test:kapa-source-groups && npm run test:tooltip-touch && npm run test:tooltip-single-open && npm run test:lazy-askai && npm run test:nav-lazy-buckets && npm run test:has-code-blocks && npm run test:docs-page-weight && npm run test:anon-quota && npm run test:callout-preservation",
"build:wasm": "cd blobl-editor/wasm && GOOS=js GOARCH=wasm go build -o ../../src/static/blobl.wasm .",
"copy:wasm-exec": "cp \"$(go env GOROOT)/lib/wasm/wasm_exec.js\" src/js/vendor/",
"serve:playground": "npx serve .",
"test:toc-active-anchor": "node --test tests/toc-active-anchor/*.test.js",
"test:introduced-in-badge": "node --test tests/introduced-in-badge/*.test.js",
"test:latest-version-tool": "node --test tests/latest-version-tool/*.test.js"
},
"dependencies": {
Expand Down
10 changes: 10 additions & 0 deletions preview-src/introduced-in-cloud-test.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
= Introduced-In Badge on Cloud
:page-cloud: true
:page-introduced-in: 4.43.0
:description: This page checks that the version badge never shows on Cloud pages.

This page sets `:page-cloud: true` and `:page-introduced-in: 4.43.0`.

== What to check

* The sticky header shows no "Since" badge. A self-managed version doesn't apply to Cloud, so the badge is hidden on Cloud pages even when a single-sourced page carries the attribute.
14 changes: 14 additions & 0 deletions preview-src/introduced-in-combo-test.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
= Introduced-In Badge With Other Badges
:page-beta: true
:page-limited-availability: true
:page-introduced-in: v4.43.0
:page-introduced-in-product: Redpanda Connect
:description: This page tests the version badge next to the Beta and Limited Availability badges.

This page sets `:page-beta:`, `:page-limited-availability:`, `:page-introduced-in: v4.43.0` and `:page-introduced-in-product: Redpanda Connect`.

== What to check

* The sticky header shows the Beta, Limited Availability and "Since 4.43.0" badges in that order, and wraps cleanly at narrow widths.
* The leading `v` is stripped: the badge reads "Since 4.43.0", not "Since v4.43.0".
* Hovering the version badge shows the tooltip "Introduced in Redpanda Connect 4.43.0."
30 changes: 30 additions & 0 deletions preview-src/introduced-in-test.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
= Introduced-In Badge Test Page
:page-introduced-in: 4.43.0
:description: This page tests the version badge that shows the release a feature was introduced in.

This page sets `:page-introduced-in: 4.43.0`, so the sticky header shows a gray "Since 4.43.0" badge.

== What to check

* The "Since 4.43.0" badge appears in the sticky header.
* Hovering the badge shows the tooltip "Introduced in version 4.43.0."
* The badge is readable in both light and dark themes.
* Pages that don't set `:page-introduced-in:`, such as the Limited Availability Test page, show no version badge.
* The Introduced-In Badge With Other Badges page shows the badge next to Beta and Limited Availability, with the product named in the tooltip.
* The Introduced-In Badge on Cloud page sets `:page-introduced-in:` but shows no version badge.

== Page content

Some content so you can scroll and check that the badge stays in the sticky header.

=== Subsection 1

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

=== Subsection 2

Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

=== Subsection 3

Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
9 changes: 9 additions & 0 deletions preview-src/ui-model.yml
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,15 @@ page:
- content: Limited Availability Test
url: /limited-availability-test.html
urlType: internal
- content: Introduced-In Badge Test
url: /introduced-in-test.html
urlType: internal
- content: Introduced-In Badge With Other Badges
url: /introduced-in-combo-test.html
urlType: internal
- content: Introduced-In Badge on Cloud
url: /introduced-in-cloud-test.html
urlType: internal
- content: Preview Status Test
url: /preview-status-test.html
urlType: internal
Expand Down
26 changes: 24 additions & 2 deletions src/css/doc.css
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ html[data-theme="dark"] .component-indicator-sticky .sm-ver-pill-caret {
}
}

/* Status badges (Beta, Limited Availability) in sticky header */
/* Status badges (Beta, Preview, Limited Availability, Since) in sticky header */
.status-badge {
display: inline-flex;
align-items: center;
Expand Down Expand Up @@ -485,7 +485,9 @@ html[data-theme="dark"] .status-badge--beta {
border-color: rgba(251, 191, 36, 0.3);
}

html[data-theme="dark"] .status-badge--preview {
/* Since shares the neutral dark palette with Preview */
html[data-theme="dark"] .status-badge--preview,
html[data-theme="dark"] .status-badge--since {
background: rgba(148, 163, 184, 0.15);
color: #cbd5e1;
border-color: rgba(148, 163, 184, 0.3);
Expand All @@ -511,6 +513,26 @@ html[data-theme="dark"] .status-badge--cloud {
border-color: rgba(14, 165, 233, 0.3);
}

.status-badge--since {
background: #f3f4f6;
color: #374151;
border: 1px solid #d1d5db;
font-weight: 500;
}

/* Full badge text for screen readers; the visible "Since X" is aria-hidden */
.status-badge__sr-text {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}

/* Availability badges: links to the other platform's version of this page */
a.status-badge--cloud-link,
a.status-badge--self-managed-link {
Expand Down
9 changes: 9 additions & 0 deletions src/helpers/normalize-version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use strict'

// Normalizes a hand-set version string for display: trims whitespace, drops a
// leading "v" (v4.43.0 -> 4.43.0) and any trailing periods, so a value of
// "4.43.0." doesn't print "4.43.0.." in a sentence. Returns '' for no value.
module.exports = (val) => {
if (val == null || typeof val === 'object') return ''
return String(val).trim().replace(/^v(?=\d)/i, '').replace(/\.+$/, '')
}
15 changes: 14 additions & 1 deletion src/partials/article.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
</div>
{{/with}}
{{> version-selector}}
{{!-- Status badges: Beta, Preview, Limited Availability --}}
{{!-- Status badges: Beta, Preview, Limited Availability, Since (version) --}}
{{#if page.attributes.beta}}
<span class="status-badge status-badge--beta" data-tippy-content="{{#if page.attributes.beta-text}}{{page.attributes.beta-text}}{{else}}This feature is in beta and may change.{{/if}}">beta</span>
{{/if}}
Expand All @@ -39,6 +39,19 @@
Limited Availability
</span>
{{/if}}
{{!-- Version badge: the release that introduced the page's feature.
:page-introduced-in: takes a bare X.Y.Z (a leading v or trailing period
is stripped). :page-introduced-in-product: names the product in the
tooltip, for example "Redpanda Connect"; without it the tooltip says
"version". Never shown on Cloud pages: a self-managed version doesn't
apply there, even if a single-sourced page carries the attribute. --}}
{{#if (and page.attributes.introduced-in (not page.attributes.cloud))}}
{{#with (normalize-version page.attributes.introduced-in)}}
{{!-- One source for the tooltip and the screen reader text, so they can't drift --}}
{{#*inline "introduced-in-text"}}Introduced in {{#if @root.page.attributes.introduced-in-product}}{{@root.page.attributes.introduced-in-product}}{{else}}version{{/if}} {{this}}.{{/inline}}
<span class="status-badge status-badge--since" data-tippy-content="{{> introduced-in-text}}"><span aria-hidden="true">Since {{this}}</span><span class="status-badge__sr-text">{{> introduced-in-text}}</span></span>
{{/with}}
{{/if}}
{{!-- ADP platform badges: only show when limited to one platform --}}
{{#if page.attributes.adp}}
{{#if page.attributes.byoc-only}}
Expand Down
82 changes: 82 additions & 0 deletions tests/introduced-in-badge/introduced-in-badge.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
'use strict'

// Verifies the "Since X" version badge in src/partials/article.hbs, rendered
// with the REAL helpers this repo ships (and.js, not.js, normalize-version.js).
// The badge block is sliced out of the partial, so a change to the template is
// what gets tested, not a copy of it.

const test = require('node:test')
const assert = require('node:assert/strict')
const path = require('node:path')
const fs = require('node:fs')
const Handlebars = require('handlebars')

const ROOT = path.join(__dirname, '..', '..')
const hbs = Handlebars.create()
hbs.registerHelper('and', require(path.join(ROOT, 'src/helpers/and.js')))
hbs.registerHelper('not', require(path.join(ROOT, 'src/helpers/not.js')))
hbs.registerHelper('normalize-version', require(path.join(ROOT, 'src/helpers/normalize-version.js')))

const ARTICLE_SRC = fs.readFileSync(path.join(ROOT, 'src/partials/article.hbs'), 'utf8')
const FROM = '{{!-- Status badges:'
const TO = '{{!-- ADP platform badges'
const start = ARTICLE_SRC.indexOf(FROM)
const end = ARTICLE_SRC.indexOf(TO)
assert.ok(start >= 0 && end > start, `article.hbs changed under this test: could not find ${FROM} .. ${TO}`)
const BADGES = hbs.compile(ARTICLE_SRC.slice(start, end))

const render = (attributes) => BADGES({ page: { attributes } })
const sinceBadges = (html) => html.match(/status-badge--since/g) || []
const tooltip = (html) => (html.match(/status-badge--since" data-tippy-content="([^"]*)"/) || [])[1]
const visible = (html) => (html.match(/<span aria-hidden="true">([^<]*)<\/span>/) || [])[1]
const srText = (html) => (html.match(/<span class="status-badge__sr-text">([^<]*)<\/span>/) || [])[1]

test('renders the badge when the page sets introduced-in', () => {
const html = render({ 'introduced-in': '4.43.0' })
assert.equal(sinceBadges(html).length, 1)
assert.equal(visible(html), 'Since 4.43.0')
assert.equal(tooltip(html), 'Introduced in version 4.43.0.')
assert.equal(srText(html), tooltip(html), 'screen reader text matches the tooltip')
})

test('renders nothing when introduced-in is absent or empty', () => {
assert.equal(sinceBadges(render({})).length, 0)
assert.equal(sinceBadges(render({ 'introduced-in': '' })).length, 0)
const blank = render({ 'introduced-in': ' ' })
assert.equal(sinceBadges(blank).length, 0, 'a value that normalizes to nothing renders nothing')
})

test('never renders on Cloud pages', () => {
const html = render({ 'introduced-in': '4.43.0', cloud: 'true' })
assert.equal(sinceBadges(html).length, 0)
})

test('names the product in the tooltip and screen reader text when introduced-in-product is set', () => {
const html = render({ 'introduced-in': '4.43.0', 'introduced-in-product': 'Redpanda Connect' })
assert.equal(visible(html), 'Since 4.43.0')
assert.equal(tooltip(html), 'Introduced in Redpanda Connect 4.43.0.')
assert.equal(srText(html), tooltip(html), 'screen reader text matches the tooltip')
})

test('normalizes a leading v and trailing periods', () => {
for (const raw of ['v4.43.0', 'V4.43.0', '4.43.0.', ' v4.43.0.. ']) {
const html = render({ 'introduced-in': raw })
assert.equal(visible(html), 'Since 4.43.0', raw)
assert.equal(tooltip(html), 'Introduced in version 4.43.0.', raw)
assert.equal(srText(html), tooltip(html), raw)
}
})

test('escapes attribute values', () => {
const html = render({ 'introduced-in': '1.0"><script>', 'introduced-in-product': 'A"B' })
assert.ok(!html.includes('<script>'))
assert.ok(!html.includes('A"B'))
})

test('sits after Beta and Limited Availability when several badges show', () => {
const html = render({ beta: 'true', 'limited-availability': 'true', 'introduced-in': '4.43.0' })
const beta = html.indexOf('status-badge--beta')
const la = html.indexOf('status-badge--la')
const since = html.indexOf('status-badge--since')
assert.ok(beta >= 0 && la > beta && since > la, 'order is Beta, Limited Availability, Since')
})
Loading