From 95dd9c3f6d9e082e52eece0c673cddb5687a0bb9 Mon Sep 17 00:00:00 2001 From: Audrey Hamelers Date: Mon, 20 Jul 2026 18:19:20 +0200 Subject: [PATCH 1/6] tab lists where possible and group where not --- src/components/api-request.js | 63 ++++++++++++++------- src/components/api-response.js | 77 ++++++++++++++++++++------ src/styles/tab-styles.js | 3 +- src/templates/code-samples-template.js | 29 +++++++++- 4 files changed, 133 insertions(+), 39 deletions(-) diff --git a/src/components/api-request.js b/src/components/api-request.js index c08cc984..a6cfa09c 100644 --- a/src/components/api-request.js +++ b/src/components/api-request.js @@ -302,7 +302,7 @@ export default class ApiRequest extends LitElement { } return html` -
${title}${paramLocation === 'path' ? html`*` : ''}
+
${title}${paramLocation === 'path' ? html`*` : ''}
${tableRows} @@ -575,7 +575,7 @@ export default class ApiRequest extends LitElement { return html`
-
+
${getI18nText('operations.request-body')} ${this.request_body.required ? html`*` : ''} ${this.selectedRequestBodyType} @@ -586,12 +586,34 @@ export default class ApiRequest extends LitElement { ${reqBodySchemaHtml || reqBodyDefaultHtml ? html`
-
- - +
+ +
- ${html`
${reqBodySchemaHtml}
`} - ${html`
${reqBodyDefaultHtml}
`} + ${html`
${reqBodySchemaHtml}
`} + ${html`
${reqBodyDefaultHtml}
`}
` : html`${reqBodyFileInputHtml}` } @@ -627,18 +649,21 @@ export default class ApiRequest extends LitElement { ${!hasResponse ? '' : html``}
-
-
-
- - ${!hasResponse ? '' : html` - - ` - } -
+ ${hasResponse + ? html` +
+ + + +
` + : html` +
+
${getI18nText('operations.request')}
+
` + }
${this.responseIsBlob ? html` diff --git a/src/components/api-response.js b/src/components/api-response.js index f5e3ecf9..82cdc00e 100644 --- a/src/components/api-response.js +++ b/src/components/api-response.js @@ -103,7 +103,7 @@ export default class ApiResponse extends LitElement { render() { return html`
-
+
${this.callback === 'true' ? getI18nText('operations.callback-response') : getI18nText('operations.response')}
@@ -159,8 +159,8 @@ export default class ApiResponse extends LitElement { this.headersForEachRespStatus[statusCode] = tempHeaders; this.mimeResponsesForEachStatus[statusCode] = allMimeResp; } - return html`
- ${Object.keys(this.responses).map((respStatus) => html` + return html`
+ ${Object.keys(this.responses).map((respStatus, i) => html` ${respStatus === '$$ref' // Swagger-Client parser creates '$$ref' object if JSON references are used to create responses - this should be ignored ? '' : html` @@ -173,7 +173,32 @@ export default class ApiResponse extends LitElement { this.selectedMimeType = undefined; } }}" - aria-current="${this.selectedStatus === respStatus}" + @keydown="${(e) => { + const keys = Object.keys(this.responses); + let newIndex = 0; + switch (e.key) { + case 'ArrowRight': + newIndex = (i + 1) % keys.length; + break; + case 'ArrowLeft': + newIndex = (i - 1 + keys.length) % keys.length; + break; + case 'Home': + newIndex = 0; + break; + case 'End': + newIndex = keys.length - 1; + break; + default: + return; + } + this.shadowRoot.getElementById(`button${keys[newIndex]}`).focus(); + }}" + role="tab" + aria-selected="${this.selectedStatus === respStatus}" + aria-controls="status${respStatus}" + tabindex="${this.selectedStatus === respStatus ? '0' : '-1'}" + id="button${respStatus}" class='m-btn small ${this.selectedStatus === respStatus ? 'primary' : ''}' part="btn--resp ${this.selectedStatus === respStatus ? 'btn-fill--resp' : 'btn-outline--resp'} btn-response-status" style='margin: 8px 4px 0 0; text-transform: capitalize'> @@ -184,7 +209,7 @@ export default class ApiResponse extends LitElement {
${Object.keys(this.responses).map((status) => html` -
+
${unsafeHTML(toMarkdown(this.responses[status] && this.responses[status].description || ''))} ${(this.headersForEachRespStatus[status] && this.headersForEachRespStatus[status].length > 0) @@ -196,23 +221,43 @@ export default class ApiResponse extends LitElement { ? '' : html`
-
- - +
+ +
${Object.keys(this.mimeResponsesForEachStatus[status]).length === 1 ? html` ${Object.keys(this.mimeResponsesForEachStatus[status])[0]} ` : html`${this.mimeTypeDropdownTemplate(Object.keys(this.mimeResponsesForEachStatus[status]))}` }
- ${this.activeSchemaTab === 'body' - ? html`
- ${this.mimeExampleTemplate(this.mimeResponsesForEachStatus[status][this.selectedMimeType])} -
` - : html`
- ${this.mimeSchemaTemplate(this.mimeResponsesForEachStatus[status][this.selectedMimeType])} -
` - } + ${html`
+ ${this.mimeExampleTemplate(this.mimeResponsesForEachStatus[status][this.selectedMimeType])} +
`} + ${html`
+ ${this.mimeSchemaTemplate(this.mimeResponsesForEachStatus[status][this.selectedMimeType])} +
`}
` }`) diff --git a/src/styles/tab-styles.js b/src/styles/tab-styles.js index 2efe1c04..a67d1d42 100644 --- a/src/styles/tab-styles.js +++ b/src/styles/tab-styles.js @@ -42,7 +42,8 @@ export default css` color:var(--primary-color); } -.tab-btn:focus-visible { +.tab-btn:focus-visible, +.tab-btn.active:focus-visible { color:var(--secondary-color); } diff --git a/src/templates/code-samples-template.js b/src/templates/code-samples-template.js index 02d353d8..ea6e835e 100644 --- a/src/templates/code-samples-template.js +++ b/src/templates/code-samples-template.js @@ -17,8 +17,31 @@ export default function codeSamplesTemplate(xCodeSamples) { tabContents.forEach((tabBodyEl) => { tabBodyEl.style.display = (tabBodyEl.dataset.tab === clickedTab ? 'block' : 'none'); }); } }"> -
- ${xCodeSamples.map((v, i) => html``)} +
+ ${xCodeSamples.map((v, i) => html``)}
${xCodeSamples.map((v, i) => { // We skip the first line because it could be there is no padding there, but padding on the next lines which needs to be removed @@ -26,7 +49,7 @@ export default function codeSamplesTemplate(xCodeSamples) { const sanitizedSource = v.source.split('\n').map(s => s.substring(0, paddingToRemove).match(/^\s+$/) ? s.substring(paddingToRemove) : s); const fullSource = sanitizedSource.join('\n'); return html` -
+
`; }) From 4542287f22a09506f4c4b31cf0b8ad38381fb372 Mon Sep 17 00:00:00 2001 From: Audrey Hamelers Date: Mon, 20 Jul 2026 18:56:00 +0200 Subject: [PATCH 2/6] tabs for request response --- src/components/api-request.js | 43 +++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/src/components/api-request.js b/src/components/api-request.js index a6cfa09c..096eff1f 100644 --- a/src/components/api-request.js +++ b/src/components/api-request.js @@ -651,23 +651,48 @@ export default class ApiRequest extends LitElement {
${hasResponse ? html` -
- - - + + +
` : html`
-
${getI18nText('operations.request')}
+ /* nonfunctional button should not be a button element*/ +
${getI18nText('operations.request')}
` }
${this.responseIsBlob ? html` -
+
${this.responseBlobType === 'image' ? html`` : '' @@ -683,14 +708,14 @@ export default class ApiRequest extends LitElement {
` : html` -
+
` } -
+
-
+
`; From 1604f9172da1d02332edca209076f3c97b35e028 Mon Sep 17 00:00:00 2001 From: Audrey Hamelers Date: Mon, 20 Jul 2026 18:59:51 +0200 Subject: [PATCH 3/6] missing tablist label --- src/components/api-request.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/api-request.js b/src/components/api-request.js index 096eff1f..050080b2 100644 --- a/src/components/api-request.js +++ b/src/components/api-request.js @@ -651,7 +651,7 @@ export default class ApiRequest extends LitElement {
${hasResponse ? html` -
Date: Tue, 21 Jul 2026 15:05:54 +0200 Subject: [PATCH 4/6] create common util handleTabs --- src/components/api-request.js | 52 ++------------------------ src/components/api-response.js | 51 ++----------------------- src/templates/code-samples-template.js | 28 ++------------ src/utils/common-utils.js | 26 +++++++++++++ 4 files changed, 36 insertions(+), 121 deletions(-) diff --git a/src/components/api-request.js b/src/components/api-request.js index 050080b2..bccf6d9e 100644 --- a/src/components/api-request.js +++ b/src/components/api-request.js @@ -6,7 +6,7 @@ import formatXml from 'xml-but-prettier'; import { getI18nText } from '../languages/index.js'; import { schemaInObjectNotation, getTypeInfo, generateExample, isPatternProperty } from '../utils/schema-utils.js'; -import { toMarkdown } from '../utils/common-utils.js'; +import { toMarkdown, handleTabs } from '../utils/common-utils.js'; import './schema-tree.js'; import getRequestFormTable from './request-form-table.js'; import './tag-input.js'; @@ -586,29 +586,7 @@ export default class ApiRequest extends LitElement { ${reqBodySchemaHtml || reqBodyDefaultHtml ? html`
-
+
@@ -651,33 +629,9 @@ export default class ApiRequest extends LitElement {
${hasResponse ? html` -
diff --git a/src/components/api-response.js b/src/components/api-response.js index 82cdc00e..80b0c16e 100644 --- a/src/components/api-response.js +++ b/src/components/api-response.js @@ -1,7 +1,7 @@ import { LitElement, html, css } from 'lit'; import { unsafeHTML } from 'lit/directives/unsafe-html.js'; import { schemaInObjectNotation, generateExample, getTypeInfo } from '../utils/schema-utils.js'; -import { toMarkdown } from '../utils/common-utils.js'; +import { toMarkdown, handleTabs } from '../utils/common-utils.js'; import { getI18nText } from '../languages/index.js'; import FontStyles from '../styles/font-styles.js'; import FlexStyles from '../styles/flex-styles.js'; @@ -159,8 +159,8 @@ export default class ApiResponse extends LitElement { this.headersForEachRespStatus[statusCode] = tempHeaders; this.mimeResponsesForEachStatus[statusCode] = allMimeResp; } - return html`
- ${Object.keys(this.responses).map((respStatus, i) => html` + return html`
+ ${Object.keys(this.responses).map((respStatus) => html` ${respStatus === '$$ref' // Swagger-Client parser creates '$$ref' object if JSON references are used to create responses - this should be ignored ? '' : html` @@ -173,27 +173,6 @@ export default class ApiResponse extends LitElement { this.selectedMimeType = undefined; } }}" - @keydown="${(e) => { - const keys = Object.keys(this.responses); - let newIndex = 0; - switch (e.key) { - case 'ArrowRight': - newIndex = (i + 1) % keys.length; - break; - case 'ArrowLeft': - newIndex = (i - 1 + keys.length) % keys.length; - break; - case 'Home': - newIndex = 0; - break; - case 'End': - newIndex = keys.length - 1; - break; - default: - return; - } - this.shadowRoot.getElementById(`button${keys[newIndex]}`).focus(); - }}" role="tab" aria-selected="${this.selectedStatus === respStatus}" aria-controls="status${respStatus}" @@ -221,29 +200,7 @@ export default class ApiResponse extends LitElement { ? '' : html`
-
+
diff --git a/src/templates/code-samples-template.js b/src/templates/code-samples-template.js index ea6e835e..5689e95a 100644 --- a/src/templates/code-samples-template.js +++ b/src/templates/code-samples-template.js @@ -1,5 +1,6 @@ import { html } from 'lit'; import { getI18nText } from '../languages/index.js'; +import { handleTabs } from '../utils/common-utils.js'; /* eslint-disable indent */ export default function codeSamplesTemplate(xCodeSamples) { @@ -17,31 +18,8 @@ export default function codeSamplesTemplate(xCodeSamples) { tabContents.forEach((tabBodyEl) => { tabBodyEl.style.display = (tabBodyEl.dataset.tab === clickedTab ? 'block' : 'none'); }); } }"> -
- ${xCodeSamples.map((v, i) => html``)} +
+ ${xCodeSamples.map((v, i) => html``)}
${xCodeSamples.map((v, i) => { // We skip the first line because it could be there is no padding there, but padding on the next lines which needs to be removed diff --git a/src/utils/common-utils.js b/src/utils/common-utils.js index a55e96c5..4af28617 100644 --- a/src/utils/common-utils.js +++ b/src/utils/common-utils.js @@ -188,3 +188,29 @@ export function getSanitizedEmail(emailRaw) { return ''; } + +export function handleTabs(e) { + const b = e.target; + if (b.tagName.toLowerCase() !== 'button') {return;} + const buttons = Array.from(b.parentNode.children); + const i = buttons.indexOf(b); + let newIndex = 0; + switch (e.key) { + case 'ArrowRight': + newIndex = (i + 1) % buttons.length; + break; + case 'ArrowLeft': + newIndex = (i - 1 + buttons.length) % buttons.length; + break; + case 'Home': + newIndex = 0; + break; + case 'End': + newIndex = buttons.length - 1; + break; + default: + return; + } + e.target.parentElement.children[newIndex].focus(); +} + From 95040ea4aab5cac42f7587632751745f91ccd227 Mon Sep 17 00:00:00 2001 From: Audrey Hamelers Date: Tue, 21 Jul 2026 15:12:23 +0200 Subject: [PATCH 5/6] missing tabindex --- src/components/api-request.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/api-request.js b/src/components/api-request.js index bccf6d9e..52eca23b 100644 --- a/src/components/api-request.js +++ b/src/components/api-request.js @@ -633,9 +633,9 @@ export default class ApiRequest extends LitElement { if (e.target.classList.contains('tab-btn') === false) { return; } this.activeResponseTab = e.target.dataset.tab; }}"> - - - + + +
` : html`
@@ -646,7 +646,7 @@ export default class ApiRequest extends LitElement {
${this.responseIsBlob ? html` -
+
${this.responseBlobType === 'image' ? html`` : '' @@ -662,14 +662,14 @@ export default class ApiRequest extends LitElement {
` : html` -
+
` } -
+
-
+
`; From 185c9c65275588054d930cdfce147d9bf19828ed Mon Sep 17 00:00:00 2001 From: Audrey Hamelers Date: Tue, 21 Jul 2026 15:14:45 +0200 Subject: [PATCH 6/6] handleTabs cleanup --- src/utils/common-utils.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/common-utils.js b/src/utils/common-utils.js index 4af28617..7adbb13b 100644 --- a/src/utils/common-utils.js +++ b/src/utils/common-utils.js @@ -191,8 +191,8 @@ export function getSanitizedEmail(emailRaw) { export function handleTabs(e) { const b = e.target; - if (b.tagName.toLowerCase() !== 'button') {return;} - const buttons = Array.from(b.parentNode.children); + if (b.tagName.toLowerCase() !== 'button') { return; } + const buttons = Array.from(b.parentElement.children); const i = buttons.indexOf(b); let newIndex = 0; switch (e.key) { @@ -211,6 +211,6 @@ export function handleTabs(e) { default: return; } - e.target.parentElement.children[newIndex].focus(); + b.parentElement.children[newIndex].focus(); }