From 8ec4a7cc93bdcd7f27b3392e1a4c98691f8081a9 Mon Sep 17 00:00:00 2001 From: Tim Lai Date: Tue, 12 Apr 2022 13:28:39 -0700 Subject: [PATCH] feat: EditorLocalStoragePlugin (#640) --- src/App.jsx | 4 +++ src/plugins/editor-local-storage/index.js | 22 ++++++++++++ .../editor-local-storage/wrap-actions.js | 31 ++++++++++++++++ .../integration/plugin.local-storage.spec.js | 36 +++++++++++++++++++ 4 files changed, 93 insertions(+) create mode 100644 src/plugins/editor-local-storage/index.js create mode 100644 src/plugins/editor-local-storage/wrap-actions.js create mode 100644 test/cypress/integration/plugin.local-storage.spec.js diff --git a/src/App.jsx b/src/App.jsx index 79d83a6d8b0..92cc4205fc1 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -15,6 +15,7 @@ import EditorPreviewSwaggerUIPlugin from './plugins/editor-preview-swagger-ui/in import EditorPreviewAsyncAPIPlugin from './plugins/editor-preview-asyncapi/index.js'; import EditorReadOnlyPlugin from './plugins/editor-read-only/index.js'; import EditorSpecOriginPlugin from './plugins/editor-spec-origin/index.js'; +import EditorLocalStoragePlugin from './plugins/editor-local-storage/index.js'; const SafeRenderPlugin = (system) => SwaggerUI.plugins.SafeRender({ @@ -52,6 +53,7 @@ SwaggerIDE.plugins = { EditorMonaco: EditorMonacoPlugin, EditorReadOnly: EditorReadOnlyPlugin, EditorSpecOrigin: EditorSpecOriginPlugin, + EditorLocalStorage: EditorLocalStoragePlugin, EditorPreviewSwaggerUI: EditorPreviewSwaggerUIPlugin, EditorPreviewAsyncAPI: EditorPreviewAsyncAPIPlugin, Topbar: TopbarPlugin, @@ -65,6 +67,7 @@ SwaggerIDE.presets = { EditorTextareaPlugin, EditorReadOnlyPlugin, EditorSpecOriginPlugin, + EditorLocalStoragePlugin, EditorPreviewSwaggerUIPlugin, EditorPreviewAsyncAPIPlugin, TopbarPlugin, @@ -79,6 +82,7 @@ SwaggerIDE.presets = { EditorMonacoPlugin, EditorReadOnlyPlugin, EditorSpecOriginPlugin, + EditorLocalStoragePlugin, EditorPreviewSwaggerUIPlugin, EditorPreviewAsyncAPIPlugin, TopbarPlugin, diff --git a/src/plugins/editor-local-storage/index.js b/src/plugins/editor-local-storage/index.js new file mode 100644 index 00000000000..14f92cb8be8 --- /dev/null +++ b/src/plugins/editor-local-storage/index.js @@ -0,0 +1,22 @@ +import { updateSpec, loadFromLocalStorage, download } from './wrap-actions.js'; + +/** + * wraps updateSpec to also save specStr to localStorage + * wraps download to check if it should be ignored + */ + +const EditorLocalStoragePlugin = (system) => { + loadFromLocalStorage(system); // will check if exists + return { + statePlugins: { + spec: { + wrapActions: { + updateSpec, + download, + }, + }, + }, + }; +}; + +export default EditorLocalStoragePlugin; diff --git a/src/plugins/editor-local-storage/wrap-actions.js b/src/plugins/editor-local-storage/wrap-actions.js new file mode 100644 index 00000000000..368ed7ed315 --- /dev/null +++ b/src/plugins/editor-local-storage/wrap-actions.js @@ -0,0 +1,31 @@ +const CONTENT_KEY = 'swagger-ide-content'; +const { localStorage } = window; + +const saveContentToStorage = (str) => { + return localStorage.setItem(CONTENT_KEY, str); +}; + +export const updateSpec = (oriAction) => (specStr) => { + oriAction(specStr); + saveContentToStorage(specStr); +}; + +export const loadFromLocalStorage = (system) => { + // setTimeout runs on the next tick + setTimeout(() => { + if (localStorage.getItem(CONTENT_KEY)) { + system?.specActions?.updateSpec(localStorage.getItem(CONTENT_KEY), 'local-storage'); + } + }, 0); +}; + +/** + * when given a SwaggerUI config prop `url`, + * before `download` of new specStr as specified by `url`, + * check if active specStr already exists in localStorage + */ +export const download = (oriAction) => (specStr) => { + if (!localStorage.getItem(CONTENT_KEY)) { + oriAction(specStr); + } +}; diff --git a/test/cypress/integration/plugin.local-storage.spec.js b/test/cypress/integration/plugin.local-storage.spec.js new file mode 100644 index 00000000000..52755a45d20 --- /dev/null +++ b/test/cypress/integration/plugin.local-storage.spec.js @@ -0,0 +1,36 @@ +describe('EditorLocalStoragePlugin', () => { + beforeEach(() => { + cy.intercept( + 'GET', + 'https://raw.githubusercontent.com/asyncapi/spec/v2.2.0/examples/streetlights-kafka.yml', + { + fixture: 'streetlights-kafka.yml', + } + ).as('streetlightsKafka'); + + cy.visit('/', {}); + // tests when initial URL is set to AsyncAPI streetlights-kafka.yml + cy.wait('@streetlightsKafka').then(() => { + // console.log('ok'); + }); + }); + + it('should load definition with provided url prop', () => { + cy.get('.monaco-editor .view-lines') + .should('contains.text', 'asyncapi') + .should('contains.text', '2.2.0'); + }); + + it('should reload while keeping text change from 2.2.0 to 2.1.0', () => { + const moveToPosition = `{rightArrow}{rightArrow}{rightArrow}{rightArrow}{rightArrow}{rightArrow}{rightArrow}{rightArrow}{rightArrow}{rightArrow}{rightArrow}{rightArrow}{rightArrow}`; + cy.get('.monaco-editor textarea:first') + .click() + .focused() + .type(`${moveToPosition}{shift+rightArrow}1`); + cy.get('.monaco-editor .view-lines').should('contains.text', '2.1.0'); + cy.reload(); + cy.get('.monaco-editor .view-lines') + .should('contains.text', '2.1.0') + .should('not.contains.text', '2.2.0'); + }); +});