mirror of
https://github.com/Redocly/redoc.git
synced 2024-11-23 00:56:33 +03:00
Use operationId in links; fixes #14
This commit is contained in:
parent
7f57558225
commit
fa81187e10
|
@ -1,7 +1,7 @@
|
||||||
<div class="method">
|
<div class="method">
|
||||||
<div class="method-content">
|
<div class="method-content">
|
||||||
<h2 class="method-header sharable-header">
|
<h2 class="method-header sharable-header">
|
||||||
<a class="share-link" href="#{{tag}}{{pointer}}"></a>{{data.methodInfo.summary}}
|
<a class="share-link" href="#{{data.methodAnchor}}"></a>{{data.methodInfo.summary}}
|
||||||
</h2>
|
</h2>
|
||||||
<h3 class="method-endpoint">
|
<h3 class="method-endpoint">
|
||||||
<span class="http-method" [ngClass]="data.httpMethod">{{data.httpMethod}}</span>
|
<span class="http-method" [ngClass]="data.httpMethod">{{data.httpMethod}}</span>
|
||||||
|
|
|
@ -28,6 +28,11 @@ export default class Method extends BaseComponent {
|
||||||
this.data.methodInfo = this.componentSchema;
|
this.data.methodInfo = this.componentSchema;
|
||||||
this.data.methodInfo.tags = this.filterMainTags(this.data.methodInfo.tags);
|
this.data.methodInfo.tags = this.filterMainTags(this.data.methodInfo.tags);
|
||||||
this.data.bodyParam = this.findBodyParam();
|
this.data.bodyParam = this.findBodyParam();
|
||||||
|
if (this.componentSchema.operationId) {
|
||||||
|
this.data.methodAnchor = 'operation/' + this.componentSchema.operationId;
|
||||||
|
} else {
|
||||||
|
this.data.methodAnchor = 'tag/' + this.tag + this.pointer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
filterMainTags(tags) {
|
filterMainTags(tags) {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<div class="methods">
|
<div class="methods">
|
||||||
<div class="tag" *ngFor="#tag of data.tags">
|
<div class="tag" *ngFor="#tag of data.tags">
|
||||||
<div class="tag-info" [attr.tag]="tag.name">
|
<div class="tag-info" [attr.tag]="tag.name">
|
||||||
<h1 class="sharable-header"> <a class="share-link" href="#{{tag.name}}"></a>{{tag.name}} </h1>
|
<h1 class="sharable-header"> <a class="share-link" href="#tag/{{tag.name}}"></a>{{tag.name}} </h1>
|
||||||
<p *ngIf="tag.description" innerHtml="{{ tag.description | marked }}"> </p>
|
<p *ngIf="tag.description" innerHtml="{{ tag.description | marked }}"> </p>
|
||||||
</div>
|
</div>
|
||||||
<method *ngFor="#method of tag.methods" [pointer]="method.pointer" [attr.pointer]="method.pointer"
|
<method *ngFor="#method of tag.methods" [pointer]="method.pointer" [attr.pointer]="method.pointer"
|
||||||
[attr.tag]="method.tag" [tag]="method.tag"></method>
|
[attr.tag]="method.tag" [tag]="method.tag" [attr.operation-id]="method.operationId"></method>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
import {RedocComponent, BaseComponent} from '../base';
|
import {RedocComponent, BaseComponent} from '../base';
|
||||||
import {redocEvents} from '../../events';
|
import {redocEvents} from '../../events';
|
||||||
|
|
||||||
import {NgZone, ChangeDetectionStrategy, ElementRef} from 'angular2/core';
|
import {NgZone, ChangeDetectionStrategy, ElementRef, forwardRef} from 'angular2/core';
|
||||||
|
import Redoc from '../Redoc/redoc';
|
||||||
import {document} from 'angular2/src/facade/browser';
|
import {document} from 'angular2/src/facade/browser';
|
||||||
import {BrowserDomAdapter} from 'angular2/platform/browser';
|
import {BrowserDomAdapter} from 'angular2/platform/browser';
|
||||||
import {global} from 'angular2/src/facade/lang';
|
import {global} from 'angular2/src/facade/lang';
|
||||||
|
@ -60,10 +61,18 @@ export default class SideMenu extends BaseComponent {
|
||||||
let hash = this.adapter.getLocation().hash;
|
let hash = this.adapter.getLocation().hash;
|
||||||
if (!hash) return;
|
if (!hash) return;
|
||||||
|
|
||||||
|
let el;
|
||||||
hash = hash.substr(1);
|
hash = hash.substr(1);
|
||||||
let tag = hash.split('/')[0];
|
let namespace = hash.split('/')[0];
|
||||||
let ptr = hash.substr(tag.length);
|
let ptr = hash.substr(namespace.length + 1);
|
||||||
let el = this.getMethodEl(ptr, tag);
|
if (namespace === 'operation') {
|
||||||
|
el = this.getMethodElByOperId(ptr);
|
||||||
|
} else if (namespace === 'tag') {
|
||||||
|
let tag = ptr.split('/')[0];
|
||||||
|
ptr = ptr.substr(tag.length);
|
||||||
|
el = this.getMethodElByPtr(ptr, tag);
|
||||||
|
}
|
||||||
|
|
||||||
if (el) this.scrollTo(el);
|
if (el) this.scrollTo(el);
|
||||||
if (evt) evt.preventDefault();
|
if (evt) evt.preventDefault();
|
||||||
}
|
}
|
||||||
|
@ -170,13 +179,18 @@ export default class SideMenu extends BaseComponent {
|
||||||
return (methodIdx === 0 && catIdx === 0);
|
return (methodIdx === 0 && catIdx === 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMethodEl(ptr, tag) {
|
getMethodElByPtr(ptr, tag) {
|
||||||
let selector = ptr ? `[pointer="${ptr}"][tag="${tag}"]` : `[tag="${tag}"]`;
|
let selector = ptr ? `[pointer="${ptr}"][tag="${tag}"]` : `[tag="${tag}"]`;
|
||||||
return document.querySelector(selector);
|
return document.querySelector(selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMethodElByOperId(operationId) {
|
||||||
|
let selector =`[operation-id="${operationId}"]`;
|
||||||
|
return document.querySelector(selector);
|
||||||
|
}
|
||||||
|
|
||||||
getCurrentMethodEl() {
|
getCurrentMethodEl() {
|
||||||
return this.getMethodEl(this.activeMethodPtr, this.data.menu[this.activeCatIdx].name);
|
return this.getMethodElByPtr(this.activeMethodPtr, this.data.menu[this.activeCatIdx].name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* returns 1 if element if above the view, 0 if in view and -1 below the view */
|
/* returns 1 if element if above the view, 0 if in view and -1 below the view */
|
||||||
|
@ -240,4 +254,5 @@ export default class SideMenu extends BaseComponent {
|
||||||
this.changeActive(CHANGE.INITIAL);
|
this.changeActive(CHANGE.INITIAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SideMenu.parameters = SideMenu.parameters.concat([[ElementRef], [BrowserDomAdapter], [NgZone]]);
|
SideMenu.parameters = SideMenu.parameters.concat([[ElementRef],
|
||||||
|
[BrowserDomAdapter], [NgZone], [forwardRef(() => Redoc)] ]);
|
||||||
|
|
|
@ -75,8 +75,22 @@ describe('Redoc components', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should scroll to method when location hash is present', (done) => {
|
it('should scroll to method when location hash is present [jp]', (done) => {
|
||||||
let hash = '#pet/paths/~1pet~1findByStatus/get';
|
let hash = '#tag/pet/paths/~1pet~1findByStatus/get';
|
||||||
|
spyOn(component.adapter, 'getLocation').and.returnValue({hash: hash});
|
||||||
|
spyOn(component, 'hashScroll').and.callThrough();
|
||||||
|
spyOn(window, 'scrollTo').and.stub();
|
||||||
|
redocEvents.bootstrapped.next();
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(component.hashScroll).toHaveBeenCalled();
|
||||||
|
let scrollY = window.scrollTo.calls.argsFor(0)[1];
|
||||||
|
expect(scrollY).toBeGreaterThan(0);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should scroll to method when location hash is present [operation]', (done) => {
|
||||||
|
let hash = '#operation/getPetById';
|
||||||
spyOn(component.adapter, 'getLocation').and.returnValue({hash: hash});
|
spyOn(component.adapter, 'getLocation').and.returnValue({hash: hash});
|
||||||
spyOn(component, 'hashScroll').and.callThrough();
|
spyOn(component, 'hashScroll').and.callThrough();
|
||||||
spyOn(window, 'scrollTo').and.stub();
|
spyOn(window, 'scrollTo').and.stub();
|
||||||
|
@ -121,8 +135,20 @@ describe('Redoc components', () => {
|
||||||
expect(component.data).not.toBeNull();
|
expect(component.data).not.toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should scroll to method when location hash is present', (done) => {
|
it('should scroll to method when location hash is present [jp]', (done) => {
|
||||||
let hash = '#pet/paths/~1pet~1findByStatus/get';
|
let hash = '#tag/pet/paths/~1pet~1findByStatus/get';
|
||||||
|
spyOn(component.adapter, 'getLocation').and.returnValue({hash: hash});
|
||||||
|
spyOn(component, 'hashScroll').and.callThrough();
|
||||||
|
redocEvents.bootstrapped.next();
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(component.hashScroll).toHaveBeenCalled();
|
||||||
|
expect(component.scrollParent.scrollTop).toBeGreaterThan(0);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should scroll to method when location hash is present [operation]', (done) => {
|
||||||
|
let hash = '#operation/getPetById';
|
||||||
spyOn(component.adapter, 'getLocation').and.returnValue({hash: hash});
|
spyOn(component.adapter, 'getLocation').and.returnValue({hash: hash});
|
||||||
spyOn(component, 'hashScroll').and.callThrough();
|
spyOn(component, 'hashScroll').and.callThrough();
|
||||||
redocEvents.bootstrapped.next();
|
redocEvents.bootstrapped.next();
|
||||||
|
|
|
@ -150,7 +150,11 @@ export default class SchemaManager {
|
||||||
}
|
}
|
||||||
if (tagDetails['x-traitTag']) continue;
|
if (tagDetails['x-traitTag']) continue;
|
||||||
if (!tagDetails.methods) tagDetails.methods = [];
|
if (!tagDetails.methods) tagDetails.methods = [];
|
||||||
tagDetails.methods.push({pointer: methodPointer, summary: methodSummary});
|
tagDetails.methods.push({
|
||||||
|
pointer: methodPointer,
|
||||||
|
summary: methodSummary,
|
||||||
|
operationId: methodInfo.operationId
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -165,7 +165,7 @@ describe('Utils', () => {
|
||||||
info.should.be.an.Object();
|
info.should.be.an.Object();
|
||||||
info.methods.should.be.an.Array();
|
info.methods.should.be.an.Array();
|
||||||
for (let methodInfo of info.methods) {
|
for (let methodInfo of info.methods) {
|
||||||
methodInfo.should.have.keys('pointer', 'summary');
|
methodInfo.should.have.properties(['pointer', 'summary']);
|
||||||
let methSchema = schemaMgr.byPointer(methodInfo.pointer);
|
let methSchema = schemaMgr.byPointer(methodInfo.pointer);
|
||||||
expect(methSchema).not.toBeNull();
|
expect(methSchema).not.toBeNull();
|
||||||
if (methSchema.summary) {
|
if (methSchema.summary) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user