Merge pull request #4 from sofico-global/fix/queryparamnavigation

Fix query param navigation issues + add external update trigger
This commit is contained in:
Depickere Sven 2023-04-25 15:55:47 +01:00 committed by GitHub
commit 4be8be3284
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 56 additions and 13 deletions

View File

@ -1,6 +1,6 @@
{
"name": "redoc",
"version": "2.0.0-0.1.1",
"version": "2.0.0-0.2.0",
"description": "ReDoc",
"repository": {
"type": "git",

View File

@ -72,11 +72,14 @@ export class AppStore {
// update position statically based on hash (in case of SSR)
MenuStore.updateOnHistory(this.history.currentId, this.scroll);
// Listen for external event to update
window.addEventListener('redocUpdatePosition', this.updateOnEvent);
// override the openApi standard to version 3.1.0
// TODO remove when fully supporting open API 3.1.0
spec.openapi = "3.1.0";
spec.openapi = '3.1.0';
this.spec = new SpecStore(spec, specUrl, this.options);
this.menu = new MenuStore(this.spec, this.scroll, this.history);
this.menu = new MenuStore(this.spec, this.scroll, this.history, this.options);
if (!this.options.disableSearch) {
this.search = new SearchStore();
@ -98,6 +101,7 @@ export class AppStore {
dispose() {
this.scroll.dispose();
this.menu.dispose();
window.removeEventListener('redocUpdatePosition', this.updateOnEvent);
if (this.search) {
this.search.dispose();
}
@ -125,6 +129,10 @@ export class AppStore {
};
}
private updateOnEvent(): void {
MenuStore.updateOnHistory(this.history.currentId, this.scroll);
}
private updateMarkOnMenu(idx: number) {
const start = Math.max(0, idx);
const end = Math.min(this.menu.flatItems.length, start + 5);

View File

@ -18,7 +18,13 @@ export class HistoryService {
get currentId(): string {
if (IS_BROWSER) {
if (this.shouldQueryParamNavigationBeUsed()) {
return this.getQueryParams(window.location.search);
// When the window.location.hash is not empty this means that we have clicked on
// router that's for example stored in the description via markdown
if (window.location.hash == '') {
return this.getQueryParams(window.location.search);
} else {
return decodeURIComponent(window.location.hash.substring(1));
}
} else {
return decodeURIComponent(window.location.hash.substring(1));
}
@ -65,7 +71,13 @@ export class HistoryService {
return;
}
if (id == null || id === this.currentId) {
// If there currentId and the ID are equal but there is still
// a hash left when using query param navigation
// that means that the URL hasn't been overridden
if (
id == null ||
(id === this.currentId && this.checkIfThereIsHashLeftWhenQueryParamNavigationShouldBeUsed())
) {
return;
}
if (rewriteHistory) {
@ -103,6 +115,8 @@ export class HistoryService {
private getFullUrl(id: string): string {
const url = this.getUrl();
// Override the hash so it's removed when using query param navigation
url.hash = '';
url.searchParams.set('redoc', id);
return url.toString();
}
@ -111,9 +125,7 @@ export class HistoryService {
return new URL(window.location.href);
}
// private getQueryParamKey(): void {
// let searchParams = new URLSearchParams(window.location.search);
// searchParams.get('redoc')
//
// }
private checkIfThereIsHashLeftWhenQueryParamNavigationShouldBeUsed(): boolean {
return !(this.shouldQueryParamNavigationBeUsed() && window.location.hash != '');
}
}

View File

@ -8,6 +8,7 @@ import { GROUP_DEPTH } from './MenuBuilder';
import type { SpecStore } from './models';
import type { ScrollService } from './ScrollService';
import type { IMenuItem } from './types';
import { RedocNormalizedOptions } from './RedocNormalizedOptions';
/** Generic interface for MenuItems */
@ -42,6 +43,7 @@ export class MenuStore {
items: IMenuItem[];
flatItems: IMenuItem[];
private options: RedocNormalizedOptions;
/**
* cached flattened menu items to support absolute indexing
@ -53,11 +55,19 @@ export class MenuStore {
*
* @param spec [SpecStore](#SpecStore) which contains page content structure
* @param scroll scroll service instance used by this menu
* @param history the history service
* @param options the RedocNormalizedOptions that can be used to retrieve the config options
*/
constructor(spec: SpecStore, public scroll: ScrollService, public history: HistoryService) {
constructor(
spec: SpecStore,
public scroll: ScrollService,
public history: HistoryService,
options: RedocNormalizedOptions,
) {
makeObservable(this);
this.items = spec.contentItems;
this.options = options;
this.flatItems = flattenByProp(this.items || [], 'items');
this.flatItems.forEach((item, idx) => (item.absoluteIdx = idx));
@ -126,16 +136,28 @@ export class MenuStore {
item = this.flatItems.find(i => i.id === id);
if (item) {
this.activateAndScroll(item, false);
this.activateAndScrollWithNavigationStrategy(item);
} else {
if (id.startsWith(SECURITY_SCHEMES_SECTION_PREFIX)) {
item = this.flatItems.find(i => SECURITY_SCHEMES_SECTION_PREFIX.startsWith(i.id));
this.activateAndScroll(item, false);
this.activateAndScrollWithNavigationStrategy(item);
}
this.scroll.scrollIntoViewBySelector(`[${SECTION_ATTR}="${escapeHTMLAttrChars(id)}"]`);
}
};
private activateAndScrollWithNavigationStrategy(item: IMenuItem | undefined): void {
if (this.shouldQueryParamNavigationBeUsed()) {
this.activateAndScroll(item, true, true);
} else {
this.activateAndScroll(item, false);
}
}
private shouldQueryParamNavigationBeUsed(): boolean {
return this.options?.userQueryParamToNavigate;
}
/**
* get section/operation DOM Node related to the item or null if it doesn't exist
* @param idx item absolute index
@ -237,6 +259,7 @@ export class MenuStore {
) {
// item here can be a copy from search results so find corresponding item from menu
const menuItem = (item && this.getItemById(item.id)) || item;
console.log('activateAndScroll', menuItem?.id, updateLocation, rewriteHistory);
this.activate(menuItem, updateLocation, rewriteHistory);
this.scrollToActive();
if (!menuItem || !menuItem.items.length) {