diff --git a/CHANGELOG.md b/CHANGELOG.md index edd50368..ce2b7e1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ This package follows standard semvar, `..`. No breaking cha * Add `path` and `query` parameters into request interceptor. * Fix display text for the basic auth type. * Hide security options display for endpoint when none is required. +* Correctly display the schema title in the schema-tree. +* Add links to component schema when clicked in bodies of objects. ## 2.0 * `show-server-selection` has now be inverted to be `hide-server-selection` this brings it in line with how html boolean attributes are supposed to work. If you set this value to false previously, now you can hide the section using the new property. diff --git a/src/components/schema-table.js b/src/components/schema-table.js index 8315c100..c70285a4 100644 --- a/src/components/schema-table.js +++ b/src/components/schema-table.js @@ -118,9 +118,10 @@ export default class SchemaTable extends LitElement { /* eslint-disable indent */ render() { + const displayLine = [this.data?.['::title'], this.data?.['::description']].filter(d => d).join(' - '); return html` - ${this.data && this.data['::description'] - ? html` ${unsafeHTML(marked(this.data['::description'] || ''))}` + ${displayLine + ? html` ${unsafeHTML(marked(displayLine))}` : '' }
@@ -136,6 +137,10 @@ export default class SchemaTable extends LitElement { `; } + scrollToSchemaComponentByName(componentName) { + this.dispatchEvent(new CustomEvent('scrollToSchemaComponentByName', { bubbles: true, composed: true, detail: componentName })); + } + generateTree(data, dataType = 'object', key = '', title = '', description = '', schemaLevel = 0, indentLevel = 0) { const newSchemaLevel = data['::type'] && data['::type'].startsWith('xxx-of') ? schemaLevel : (schemaLevel + 1); const newIndentLevel = dataType === 'xxx-of-option' || data['::type'] === 'xxx-of-option' || key.startsWith('::OPTION') ? indentLevel : (indentLevel + 1); @@ -163,20 +168,22 @@ export default class SchemaTable extends LitElement { } let detailObjType = ''; + let displaySchemaLink = false; if ((data['::type'] || '').includes('xxx-of')) { detailObjType = ''; - } else if (data['::type'] === 'object') { - if (dataType === 'array') { - detailObjType = `[${keyLabel.replace(/(s|Collection|List)[*]?$/i, '')}]`; // Array of Object - } else { - detailObjType = 'object'; // Object - } } else if (data['::type'] === 'array') { if (dataType === 'array') { detailObjType = 'array of array'; // Array of array } else { detailObjType = 'array'; } + } else if (data['::type']) { + displaySchemaLink = data['::link']; + if (dataType === 'array') { + detailObjType = `[${data['::link']}]`; // Array of Object + } else { + detailObjType = data['::link'] || data['::type']; + } } if (typeof data === 'object') { @@ -205,7 +212,10 @@ export default class SchemaTable extends LitElement {
-
${(data['::type'] || '').includes('xxx-of') ? '' : detailObjType}
+ ${displaySchemaLink + ? html`` + : html`
${(data['::type'] || '').includes('xxx-of') ? '' : detailObjType}
` + }
${flags['🆁'] || flags['🆆'] || ''}
diff --git a/src/openapi-explorer.js b/src/openapi-explorer.js index 18eb27af..099a1a2f 100644 --- a/src/openapi-explorer.js +++ b/src/openapi-explorer.js @@ -306,11 +306,7 @@ export default class OpenApiExplorer extends LitElement { position: relative; padding: 6px 0px; } - .expanded-endpoint-component > h2:hover { - cursor: pointer; - text-decoration: underline; - } .divider { border-top: 2px solid var(--border-color); margin: 24px 0; @@ -764,6 +760,14 @@ export default class OpenApiExplorer extends LitElement { }, 300); } + async scrollToSchemaComponentByName(schemaComponentNameEvent) { + const schemaComponentName = schemaComponentNameEvent.detail; + const schemaComponent = this.resolvedSpec?.components?.find(c => c.componentKeyId === 'schemas')?.subComponents?.find(s => s.name === schemaComponentName); + if (schemaComponent) { + await this.scrollTo(`cmp--${schemaComponent.id}`, true); + } + } + // Public Method (scrolls to a given path and highlights the left-nav selection) async scrollTo(elementId, scrollNavItemToView = true, repeatedElementIndex) { try { diff --git a/src/styles/schema-styles.js b/src/styles/schema-styles.js index ec2c7d9a..9de55e93 100644 --- a/src/styles/schema-styles.js +++ b/src/styles/schema-styles.js @@ -106,4 +106,9 @@ export default css` display: flex; } } + +.expanded-endpoint-component > h2:hover, .schema-link:hover { + cursor: pointer; + text-decoration: underline; +} `; diff --git a/src/templates/callback-template.js b/src/templates/callback-template.js index 56b84b1c..ce5fb231 100644 --- a/src/templates/callback-template.js +++ b/src/templates/callback-template.js @@ -36,6 +36,7 @@ export default function callbackTemplate(callbacks) { schema-expand-level = "${this.schemaExpandLevel}" schema-hide-read-only = "${this.schemaHideReadOnly}" fetch-credentials = "${this.fetchCredentials}" + @scrollToSchemaComponentByName=${v => this.scrollToSchemaComponentByName(v)} exportparts="btn btn-fill btn-outline btn-try"> @@ -47,6 +48,7 @@ export default function callbackTemplate(callbacks) { schema-style="${this.displaySchemaAsTable ? 'table' : 'tree'}" active-schema-tab = "${this.defaultSchemaTab}" schema-expand-level = "${this.schemaExpandLevel}" + @scrollToSchemaComponentByName=${v => this.scrollToSchemaComponentByName(v)} exportparts = "btn--resp btn-fill--resp btn-outline--resp" >
diff --git a/src/templates/components-template.js b/src/templates/components-template.js index 49d49445..959cd0f9 100644 --- a/src/templates/components-template.js +++ b/src/templates/components-template.js @@ -10,12 +10,13 @@ function componentBodyTemplate(sComponent) { const formdataPartSchema = schemaInObjectNotation(sComponent.component, { includeNulls: this.includeNulls }); return html` -
-

${sComponent.name}

+
+

${sComponent.name}

${this.displaySchemaAsTable ? html` this.scrollToSchemaComponentByName(v)} schema-expand-level = "${this.schemaExpandLevel}" schema-hide-read-only=false schema-hide-write-only=false> ` diff --git a/src/templates/endpoint-template.js b/src/templates/endpoint-template.js index 7f52c5f4..f6e664d4 100644 --- a/src/templates/endpoint-template.js +++ b/src/templates/endpoint-template.js @@ -108,6 +108,7 @@ function endpointBodyTemplate(path) { schema-expand-level = "${this.schemaExpandLevel}" schema-hide-read-only = "${this.schemaHideReadOnly}" fetch-credentials = "${this.fetchCredentials}" + @scrollToSchemaComponentByName=${v => this.scrollToSchemaComponentByName(v)} exportparts="btn btn-fill btn-outline btn-try">
@@ -122,6 +123,7 @@ function endpointBodyTemplate(path) { schema-expand-level = "${this.schemaExpandLevel}" schema-hide-write-only = "${this.schemaHideWriteOnly}" selected-status = "${Object.keys(path.responses || {})[0] || ''}" + @scrollToSchemaComponentByName=${v => this.scrollToSchemaComponentByName(v)} exportparts = "btn--resp btn-fill--resp btn-outline--resp" >
diff --git a/src/templates/expanded-endpoint-template.js b/src/templates/expanded-endpoint-template.js index dbe2aaa2..5eef40cd 100644 --- a/src/templates/expanded-endpoint-template.js +++ b/src/templates/expanded-endpoint-template.js @@ -67,6 +67,7 @@ export function expandedEndpointBodyTemplate(path, tagName = '') { schema-expand-level = "${this.schemaExpandLevel}" schema-hide-read-only = "${this.schemaHideReadOnly}" fetch-credentials = "${this.fetchCredentials}" + @scrollToSchemaComponentByName=${v => this.scrollToSchemaComponentByName(v)} exportparts = "btn btn-fill btn-outline btn-try" > @@ -82,6 +83,7 @@ export function expandedEndpointBodyTemplate(path, tagName = '') { schema-expand-level = "${this.schemaExpandLevel}" schema-hide-write-only = "${this.schemaHideWriteOnly}" selected-status = "${Object.keys(path.responses || {})[0] || ''}" + @scrollToSchemaComponentByName=${v => this.scrollToSchemaComponentByName(v)} exportparts = "btn--resp btn-fill--resp btn-outline--resp" >
diff --git a/src/utils/schema-utils.js b/src/utils/schema-utils.js index ba58d599..90633dc0 100644 --- a/src/utils/schema-utils.js +++ b/src/utils/schema-utils.js @@ -374,7 +374,9 @@ export function schemaInObjectNotation(rawSchema, options, level = 0, suffix = ' if (Object.keys(objWithAnyOfProps).length) { resultObj[(anyOf ? `::ANY~OF ${suffix}` : `::ONE~OF ${suffix}`)] = objWithAnyOfProps; } - resultObj['::type'] = 'object'; + + resultObj['::link'] = schema.title; + resultObj['::type'] = schema.title || 'object'; resultObj['::flags'] = { '🆁': readOnly && '🆁', '🆆': writeOnly && '🆆' }; resultObj['::title'] = schema.title || ''; resultObj['::description'] = schema.description || ''; @@ -410,6 +412,7 @@ export function schemaInObjectNotation(rawSchema, options, level = 0, suffix = ' } } if (complexTypes.length > 0) { + obj['::link'] = schema.title; obj['::type'] = 'object'; const multiTypeOptions = { '::type': 'xxx-of-option', @@ -429,7 +432,8 @@ export function schemaInObjectNotation(rawSchema, options, level = 0, suffix = ' '::title': schema.title || '', '::description': schema.description || '', '::flags': { '🆁': schema.readOnly && '🆁', '🆆': schema.writeOnly && '🆆' }, - '::type': 'object', + '::link': schema.title, + '::type': schema.title || 'object', '::deprecated': schema.deprecated || false, '::metadata': metadata }; @@ -446,6 +450,7 @@ export function schemaInObjectNotation(rawSchema, options, level = 0, suffix = ' '::title': schema.title || '', '::description': schema.description || '', '::flags': { '🆁': schema.readOnly && '🆁', '🆆': schema.writeOnly && '🆆' }, + '::link': arrayItemsSchema.title || schema.title, '::type': 'array', // Array properties are read from the ::props object instead of reading from the keys of this object // '::props': schemaInObjectNotation(Object.assign({ deprecated: schema.deprecated, readOnly: schema.readOnly, writeOnly: schema.writeOnly }, arrayItemsSchema), options, (level + 1)), @@ -464,7 +469,8 @@ export function schemaInObjectNotation(rawSchema, options, level = 0, suffix = ' obj['::title'] = schema.title || ''; obj['::description'] = schema.description || ''; obj['::flags'] = { '🆁': schema.readOnly && '🆁', '🆆': schema.writeOnly && '🆆' }; - obj['::type'] = 'object'; + obj['::link'] = schema.title; + obj['::type'] = schema.title || 'object'; obj['::deprecated'] = schema.deprecated || false; obj['::metadata'] = metadata; for (const key in schemaProperties) { @@ -486,6 +492,7 @@ export function schemaInObjectNotation(rawSchema, options, level = 0, suffix = ' obj['::title'] = schema.title || ''; obj['::description'] = schema.description || (arrayItemsSchema?.description ? `array<${arrayItemsSchema.description}>` : ''); obj['::flags'] = { '🆁': schema.readOnly && '🆁', '🆆': schema.writeOnly && '🆆' }; + obj['::link'] = arrayItemsSchema.title || schema.title; obj['::type'] = 'array'; obj['::deprecated'] = schema.deprecated || false; obj['::metadata'] = metadata;