You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by ju...@apache.org on 2021/06/15 01:33:13 UTC
[apisix-dashboard] branch master updated: feat: Use monaco editor
(#1907)
This is an automated email from the ASF dual-hosted git repository.
juzhiyuan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-dashboard.git
The following commit(s) were added to refs/heads/master by this push:
new 421261f feat: Use monaco editor (#1907)
421261f is described below
commit 421261fbf897c5cf4d670bf5c3fbd4730c3336bd
Author: qian0817 <94...@qq.com>
AuthorDate: Tue Jun 15 09:33:01 2021 +0800
feat: Use monaco editor (#1907)
---
.gitignore | 3 +-
web/cypress/fixtures/selector.json | 6 +-
...e-consumer-with-api-breaker-plugin-form.spec.js | 20 +-
...e-consumer-with-limit-count-plugin-form.spec.js | 20 +-
.../create-consumer-with-proxy-mirror-form.spec.js | 24 +--
.../create-upstream-with-cors-form.spec.js | 23 +--
.../create-upstream-with-limit-req-form.spec.js | 22 +-
.../consumer/create-with-limit-conn-form.spec.js | 22 +-
.../create-with-referer-restriction-form.spec.js | 20 +-
.../consumer/create_and_delete_consumer.spec.js | 44 ++--
.../plugin/create-delete-in-drawer-plugin.spec.js | 6 +-
.../plugin/create-edit-delete-plugin.spec.js | 9 +-
.../create-route-with-plugin-orchestration.spec.js | 4 +-
.../create-edit-delete-plugin-template.spec.js | 4 +-
.../rawDataEditor/test-rawDataEditor.spec.js | 23 ++-
.../create-edit-duplicate-delete-route.spec.js | 12 +-
.../service/create-edit-delete-service.spec.js | 10 +-
.../upstream/create_and_delete_upstream.spec.js | 6 +-
web/cypress/support/commands.js | 26 +--
web/package.json | 21 +-
web/src/components/Plugin/PluginDetail.tsx | 225 +++++++++++----------
web/src/components/Plugin/PluginPage.tsx | 4 +-
web/src/components/Plugin/typing.d.ts | 8 +-
web/src/components/PluginFlow/PluginFlow.tsx | 4 +-
web/src/components/RawDataEditor/RawDataEditor.tsx | 178 ++++++++--------
web/src/global.tsx | 9 +
web/src/locales/en-US/component.ts | 3 +
web/src/locales/zh-CN/component.ts | 3 +
web/src/pages/Plugin/List.tsx | 4 +-
.../Route/components/DebugViews/DebugDrawView.tsx | 135 ++++++++-----
web/src/pages/Route/constants.ts | 13 +-
web/tsconfig.json | 4 +
web/yarn.lock | 46 +++--
33 files changed, 500 insertions(+), 461 deletions(-)
diff --git a/.gitignore b/.gitignore
index 10e2061..be85770 100644
--- a/.gitignore
+++ b/.gitignore
@@ -60,8 +60,7 @@ web/coverage
web/cypress/screenshots/
web/cypress/videos/
web/cypress/downloads/
-web/cypress/screenshots/
-web/cypress/videos/
+web/public/monaco-editor/
# vim
*.swp
diff --git a/web/cypress/fixtures/selector.json b/web/cypress/fixtures/selector.json
index d6691b9..83b1924 100644
--- a/web/cypress/fixtures/selector.json
+++ b/web/cypress/fixtures/selector.json
@@ -1,5 +1,4 @@
{
- "codeMirror": ".CodeMirror",
"username": "#username",
"languageSwitcher": ".ant-space-align-center",
"dropdown": ".rc-virtual-list",
@@ -15,7 +14,6 @@
"tableCell": ".ant-table-cell",
"empty": ".ant-empty-normal",
"refresh": ".anticon-reload",
- "codemirror": ".CodeMirror",
"disabledSwitcher": "#disable",
"checkedSwitcher": ".ant-switch-checked",
"deleteButton": ".ant-btn-dangerous",
@@ -60,7 +58,7 @@
"passwordInput": "#control-ref_password",
"drawer": ".ant-drawer-content",
"drawerFooter": ".ant-drawer-footer",
- "codemirrorScroll": ".CodeMirror-scroll",
+ "monacoScroll": ".monaco-scrollable-element",
"drawerClose": ".ant-drawer-close",
"descriptionSelector": "[title=Description]",
"customSelector": "[title=Custom]",
@@ -74,7 +72,7 @@
"pageTwo": ".ant-pagination-item-2",
"pageTwoActived": ".ant-pagination-item-2.ant-pagination-item-active",
"selectDropdown": ".ant-select-dropdown",
- "codeMirrorMode": "[data-cy='code-mirror-mode']",
+ "monacoMode": "[data-cy='monaco-mode']",
"selectJSON": ".ant-select-dropdown [label=JSON]",
"deleteAlert": ".ant-modal-body"
}
diff --git a/web/cypress/integration/consumer/create-consumer-with-api-breaker-plugin-form.spec.js b/web/cypress/integration/consumer/create-consumer-with-api-breaker-plugin-form.spec.js
index 7891a11..a4df5d2 100644
--- a/web/cypress/integration/consumer/create-consumer-with-api-breaker-plugin-form.spec.js
+++ b/web/cypress/integration/consumer/create-consumer-with-api-breaker-plugin-form.spec.js
@@ -26,8 +26,8 @@ context('Create and delete consumer with api-breaker plugin form', () => {
pluginCard: '.ant-card',
drawer: '.ant-drawer-content',
disabledSwitcher: '#disable',
- codeMirror: '.CodeMirror',
notification: '.ant-notification-notice-message',
+ monacoViewZones: '.view-zones'
}
const data = {
@@ -59,18 +59,14 @@ context('Create and delete consumer with api-breaker plugin form', () => {
});
});
cy.focused(selector.drawer).should('exist');
+ cy.get(selector.monacoViewZones).should('exist');
cy.get(selector.disabledSwitcher).click();
- // edit codemirror
- cy.get(selector.codeMirror)
- .first()
- .then((editor) => {
- editor[0].CodeMirror.setValue(
- JSON.stringify({
- key: 'test',
- }),
- );
- cy.contains('button', 'Submit').click();
- });
+
+ // edit monaco
+ cy.window().then((window) => {
+ window.monacoEditor.setValue(JSON.stringify({ key: 'test' }));
+ cy.contains('button', 'Submit').click();
+ });
cy.contains(selector.pluginCard, 'api-breaker').within(() => {
cy.contains('Enable').click({
diff --git a/web/cypress/integration/consumer/create-consumer-with-limit-count-plugin-form.spec.js b/web/cypress/integration/consumer/create-consumer-with-limit-count-plugin-form.spec.js
index 6e45667..249d4a5 100644
--- a/web/cypress/integration/consumer/create-consumer-with-limit-count-plugin-form.spec.js
+++ b/web/cypress/integration/consumer/create-consumer-with-limit-count-plugin-form.spec.js
@@ -26,7 +26,6 @@ context('Create and delete consumer with limit-count plugin form', () => {
drawer: '.ant-drawer-content',
dropdown: '.rc-virtual-list',
disabledSwitcher: '#disable',
- codeMirror: '.CodeMirror',
notification: '.ant-notification-notice-message',
count: '#count',
time_window: '#time_window',
@@ -40,6 +39,7 @@ context('Create and delete consumer with limit-count plugin form', () => {
redis_cluster_name: '#redis_cluster_name',
redis_cluster_nodes_0: '#redis_cluster_nodes_0',
redis_cluster_nodes_1: '#redis_cluster_nodes_1',
+ monacoViewZones: '.view-zones'
}
const data = {
@@ -70,18 +70,14 @@ context('Create and delete consumer with limit-count plugin form', () => {
});
});
cy.focused(selector.drawer).should('exist');
+ cy.get(selector.monacoViewZones).should('exist');
cy.get(selector.disabledSwitcher).click();
- // edit codemirror
- cy.get(selector.codeMirror)
- .first()
- .then((editor) => {
- editor[0].CodeMirror.setValue(
- JSON.stringify({
- key: 'test',
- }),
- );
- cy.contains('button', 'Submit').click();
- });
+
+ // edit monaco
+ cy.window().then((window) => {
+ window.monacoEditor.setValue(JSON.stringify({ key: 'test' }));
+ cy.contains('button', 'Submit').click();
+ });
cy.contains(selector.pluginCard, 'limit-count').within(() => {
cy.contains('Enable').click({
diff --git a/web/cypress/integration/consumer/create-consumer-with-proxy-mirror-form.spec.js b/web/cypress/integration/consumer/create-consumer-with-proxy-mirror-form.spec.js
index e6f7f6b..f49f35c 100644
--- a/web/cypress/integration/consumer/create-consumer-with-proxy-mirror-form.spec.js
+++ b/web/cypress/integration/consumer/create-consumer-with-proxy-mirror-form.spec.js
@@ -26,13 +26,13 @@ context('Create and delete consumer with proxy-mirror plugin form', () => {
drawer: '.ant-drawer-content',
dropdown: '.rc-virtual-list',
disabledSwitcher: '#disable',
- codeMirror: '.CodeMirror',
notification: '.ant-notification-notice-message',
notificationCloseIcon: '.ant-notification-close-icon',
max_age: '#max_age',
allow_origins_by_regex: '#allow_origins_by_regex_0',
host: '#host',
- alert: '[role=alert]'
+ alert: '[role=alert]',
+ monacoViewZones: '.view-zones'
}
const data = {
@@ -64,18 +64,14 @@ context('Create and delete consumer with proxy-mirror plugin form', () => {
});
});
cy.focused(selector.drawer).should('exist');
+ cy.get(selector.monacoViewZones).should('exist');
cy.get(selector.disabledSwitcher).click();
- // edit codemirror
- cy.get(selector.codeMirror)
- .first()
- .then((editor) => {
- editor[0].CodeMirror.setValue(
- JSON.stringify({
- key: 'test',
- }),
- );
- cy.contains('button', 'Submit').click();
- });
+
+ // edit monaco
+ cy.window().then((window) => {
+ window.monacoEditor.setValue(JSON.stringify({ key: 'test' }));
+ cy.contains('button', 'Submit').click();
+ });
cy.contains(selector.pluginCard, 'proxy-mirror').within(() => {
cy.contains('Enable').click({
@@ -98,7 +94,7 @@ context('Create and delete consumer with proxy-mirror plugin form', () => {
// config proxy-mirror form with correct host
cy.get(selector.host).clear().type('http://127.0.0.1:1999');
- cy.get(selector.alert).should('not.exist');
+ cy.get(selector.alert).should('not.be.visible');
cy.get(selector.disabledSwitcher).click();
cy.get(selector.drawer).within(() => {
cy.contains('Submit').click({
diff --git a/web/cypress/integration/consumer/create-upstream-with-cors-form.spec.js b/web/cypress/integration/consumer/create-upstream-with-cors-form.spec.js
index f6450a0..03cb35c 100644
--- a/web/cypress/integration/consumer/create-upstream-with-cors-form.spec.js
+++ b/web/cypress/integration/consumer/create-upstream-with-cors-form.spec.js
@@ -26,7 +26,6 @@ context('Create and Delete Consumer', () => {
drawer: '.ant-drawer-content',
dropdown: '.rc-virtual-list',
disabledSwitcher: '#disable',
- codeMirror: '.CodeMirror',
notification: '.ant-notification-notice-message',
notificationCloseIcon: '.ant-notification-close-icon',
rate: '#rate',
@@ -34,7 +33,8 @@ context('Create and Delete Consumer', () => {
key: '#key',
remote_addr: '[title=remote_addr]',
max_age: '#max_age',
- allow_origins_by_regex: '#allow_origins_by_regex_0'
+ allow_origins_by_regex: '#allow_origins_by_regex_0',
+ monacoViewZones: '.view-zones'
}
const data = {
@@ -66,18 +66,15 @@ context('Create and Delete Consumer', () => {
});
});
cy.focused(selector.drawer).should('exist');
+ cy.get(selector.monacoViewZones).should('exist');
cy.get(selector.disabledSwitcher).click().should('have.class', 'ant-switch-checked');
- // edit codemirror
- cy.get(selector.codeMirror)
- .first()
- .then((editor) => {
- editor[0].CodeMirror.setValue(
- JSON.stringify({
- key: 'test',
- }),
- );
- cy.contains('button', 'Submit').click();
- });
+
+ // edit monaco
+ cy.window().then((window) => {
+ window.monacoEditor.setValue(JSON.stringify({ key: 'test' }));
+
+ cy.contains('button', 'Submit').click();
+ });
cy.contains(selector.pluginCard, 'cors').within(() => {
cy.contains('Enable').click({
diff --git a/web/cypress/integration/consumer/create-upstream-with-limit-req-form.spec.js b/web/cypress/integration/consumer/create-upstream-with-limit-req-form.spec.js
index 1e375ef..1c4fa7f 100644
--- a/web/cypress/integration/consumer/create-upstream-with-limit-req-form.spec.js
+++ b/web/cypress/integration/consumer/create-upstream-with-limit-req-form.spec.js
@@ -26,12 +26,12 @@ context('Create and Delete Consumer', () => {
drawer: '.ant-drawer-content',
dropdown: '.rc-virtual-list',
disabledSwitcher: '#disable',
- codeMirror: '.CodeMirror',
notification: '.ant-notification-notice-message',
rate: '#rate',
burst: '#burst',
key: '#key',
- remote_addr: '[title=remote_addr]'
+ remote_addr: '[title=remote_addr]',
+ monacoViewZones: '.view-zones'
}
const data = {
@@ -61,18 +61,14 @@ context('Create and Delete Consumer', () => {
cy.contains('Enable').click({ force: true });
});
cy.focused(selector.drawer).should('exist');
+ cy.get(selector.monacoViewZones).should('exist');
+
+ // edit monaco
cy.get(selector.disabledSwitcher).click().should('have.class', 'ant-switch-checked');
- // edit codemirror
- cy.get(selector.codeMirror)
- .first()
- .then((editor) => {
- editor[0].CodeMirror.setValue(
- JSON.stringify({
- key: 'test',
- }),
- );
- cy.contains('button', 'Submit').click();
- });
+ cy.window().then((window) => {
+ window.monacoEditor.setValue(JSON.stringify({ key: 'test' }));
+ cy.contains('button', 'Submit').click();
+ });
cy.contains(selector.pluginCard, 'limit-req').within(() => {
cy.contains('Enable').click({
diff --git a/web/cypress/integration/consumer/create-with-limit-conn-form.spec.js b/web/cypress/integration/consumer/create-with-limit-conn-form.spec.js
index 2dbfc4c..4d096f3 100644
--- a/web/cypress/integration/consumer/create-with-limit-conn-form.spec.js
+++ b/web/cypress/integration/consumer/create-with-limit-conn-form.spec.js
@@ -26,7 +26,6 @@ context('Create and delete consumer with limit-conn plugin form', () => {
drawer: '.ant-drawer-content',
dropdown: '.rc-virtual-list',
disabledSwitcher: '#disable',
- codeMirror: '.CodeMirror',
notification: '.ant-notification-notice-message',
selectDropdown: '.ant-select-dropdown',
conn: '#conn',
@@ -34,7 +33,8 @@ context('Create and delete consumer with limit-conn plugin form', () => {
default_conn_delay: '#default_conn_delay',
key: '#key',
rejected_code: '#rejected_code',
- title: '[title="remote_addr"]'
+ title: '[title="remote_addr"]',
+ monacoViewZones: '.view-zones'
}
const data = {
@@ -69,18 +69,14 @@ context('Create and delete consumer with limit-conn plugin form', () => {
});
});
cy.focused(selector.drawer).should('exist');
+ cy.get(selector.monacoViewZones).should('exist');
cy.get(selector.disabledSwitcher).click();
- // edit codemirror
- cy.get(selector.codeMirror)
- .first()
- .then((editor) => {
- editor[0].CodeMirror.setValue(
- JSON.stringify({
- key: 'test',
- }),
- );
- cy.contains('button', 'Submit').click();
- });
+
+ // edit monaco
+ cy.window().then((window) => {
+ window.monacoEditor.setValue(JSON.stringify({ key: 'test' }));
+ cy.contains('button', 'Submit').click();
+ });
cy.contains(selector.pluginCard, 'limit-conn').within(() => {
cy.contains('Enable').click({
diff --git a/web/cypress/integration/consumer/create-with-referer-restriction-form.spec.js b/web/cypress/integration/consumer/create-with-referer-restriction-form.spec.js
index 304b928..eb9d7c2 100644
--- a/web/cypress/integration/consumer/create-with-referer-restriction-form.spec.js
+++ b/web/cypress/integration/consumer/create-with-referer-restriction-form.spec.js
@@ -26,10 +26,10 @@ context('Create and delete Consumer with referer-restriction form ', () => {
drawer: '.ant-drawer-content',
dropdown: '.rc-virtual-list',
disabledSwitcher: '#disable',
- codeMirror: '.CodeMirror',
notification: '.ant-notification-notice-message',
whitelist: "#whitelist_0",
bypass_missing: "#bypass_missing",
+ monacoViewZones: '.view-zones'
}
const data = {
@@ -61,18 +61,14 @@ context('Create and delete Consumer with referer-restriction form ', () => {
});
});
cy.focused(selector.drawer).should('exist');
+ cy.get(selector.monacoViewZones).should('exist');
cy.get(selector.disabledSwitcher).click();
- // edit codemirror
- cy.get(selector.codeMirror)
- .first()
- .then((editor) => {
- editor[0].CodeMirror.setValue(
- JSON.stringify({
- key: 'test',
- }),
- );
- cy.contains('button', 'Submit').click();
- });
+
+ // edit monaco
+ cy.window().then((window) => {
+ window.monacoEditor.setValue(JSON.stringify({ key: 'test' }));
+ cy.contains('button', 'Submit').click();
+ });
cy.contains(selector.pluginCard, 'referer-restriction').within(() => {
cy.contains('Enable').click({
diff --git a/web/cypress/integration/consumer/create_and_delete_consumer.spec.js b/web/cypress/integration/consumer/create_and_delete_consumer.spec.js
index 1e00b66..ffd669d 100644
--- a/web/cypress/integration/consumer/create_and_delete_consumer.spec.js
+++ b/web/cypress/integration/consumer/create_and_delete_consumer.spec.js
@@ -25,11 +25,11 @@ context('Create and Delete Consumer', () => {
pluginCard: '.ant-card',
drawer: '.ant-drawer-content',
disabledSwitcher: '#disable',
- codeMirror: '.CodeMirror',
notification: '.ant-notification-notice-message',
nameSelector: '[title=Name]',
serviceSelector: '[title=test_service]',
- codemirrorScroll: '.CodeMirror-scroll',
+ monacoScroll: ".monaco-scrollable-element",
+ monacoViewZones: '.view-zones'
}
const data = {
@@ -37,7 +37,7 @@ context('Create and Delete Consumer', () => {
description: 'desc_by_autotest',
createConsumerSuccess: 'Create Consumer Successfully',
deleteConsumerSuccess: 'Delete Consumer Successfully',
- pluginErrorAlert: 'Invalid plugin data',
+ pluginErrorAlert: 'Invalid plugin data'
}
beforeEach(() => {
@@ -65,17 +65,13 @@ context('Create and Delete Consumer', () => {
});
cy.focused(selector.drawer).should('exist');
cy.get(selector.disabledSwitcher).click();
- // edit codemirror
- cy.get(selector.codeMirror)
- .first()
- .then((editor) => {
- editor[0].CodeMirror.setValue(
- JSON.stringify({
- key: 'test',
- }),
- );
- cy.contains('button', 'Submit').click();
- });
+
+ // edit monaco
+ cy.get(selector.monacoViewZones).should('exist').click({ force: true });
+ cy.window().then((window) => {
+ window.monacoEditor.setValue(JSON.stringify({ key: 'test' }));
+ cy.contains('button', 'Submit').click();
+ });
cy.contains('button', 'Next').click();
cy.contains('button', 'Submit').click();
cy.get(selector.notification).should('contain', data.createConsumerSuccess);
@@ -89,7 +85,7 @@ context('Create and Delete Consumer', () => {
cy.contains(data.consumerName).siblings().contains('View').click();
cy.get(selector.drawer).should('be.visible');
- cy.get(selector.codemirrorScroll).within(() => {
+ cy.get(selector.monacoScroll).within(() => {
cy.contains('plugins').should('exist');
cy.contains(data.consumerName).should('exist');
});
@@ -116,17 +112,13 @@ context('Create and Delete Consumer', () => {
force: true,
});
});
- // edit codeMirror
- cy.get(selector.codeMirror)
- .first()
- .then((editor) => {
- editor[0].CodeMirror.setValue(
- JSON.stringify({
- key_not_exst: 'test',
- }),
- );
- cy.contains('button', 'Submit').click();
- });
+
+ // edit monaco
+ cy.get(selector.monacoViewZones).should('exist').click({ force: true });
+ cy.window().then((window) => {
+ window.monacoEditor.setValue(JSON.stringify({ key_not_exist: 'test' }));
+ cy.contains('button', 'Submit').click();
+ });
cy.get(selector.notification).should('contain', data.pluginErrorAlert);
});
});
diff --git a/web/cypress/integration/plugin/create-delete-in-drawer-plugin.spec.js b/web/cypress/integration/plugin/create-delete-in-drawer-plugin.spec.js
index bcde1e3..ca633af 100644
--- a/web/cypress/integration/plugin/create-delete-in-drawer-plugin.spec.js
+++ b/web/cypress/integration/plugin/create-delete-in-drawer-plugin.spec.js
@@ -23,7 +23,7 @@ context('Delete Plugin List with the Drawer', () => {
pluginCardBordered: '.ant-card-bordered',
drawer: '.ant-drawer-content',
selectDropdown: '.ant-select-dropdown',
- codeMirrorMode: "[data-cy='code-mirror-mode']",
+ monacoMode: "[data-cy='monaco-mode']",
selectJSON: '.ant-select-dropdown [label=JSON]',
drawerFooter: '.ant-drawer-footer',
disabledSwitcher: '#disable',
@@ -51,9 +51,9 @@ context('Delete Plugin List with the Drawer', () => {
});
});
- cy.get(selector.codeMirrorMode).invoke('text').then(text => {
+ cy.get(selector.monacoMode).invoke('text').then(text => {
if (text === 'Form') {
- cy.get(selector.codeMirrorMode).click();
+ cy.get(selector.monacoMode).click();
cy.get(selector.selectDropdown).should('be.visible');
cy.get(selector.selectJSON).click();
}
diff --git a/web/cypress/integration/plugin/create-edit-delete-plugin.spec.js b/web/cypress/integration/plugin/create-edit-delete-plugin.spec.js
index 3f37301..c924d29 100644
--- a/web/cypress/integration/plugin/create-edit-delete-plugin.spec.js
+++ b/web/cypress/integration/plugin/create-edit-delete-plugin.spec.js
@@ -42,12 +42,9 @@ context('Enable and Delete Plugin List', () => {
cy.get(this.domSelector.refresh).click();
cy.contains('Configure').click();
- cy.get(this.domSelector.codemirror)
- .first()
- .then(() => {
- cy.get(this.domSelector.disabledSwitcher).click();
- cy.contains('button', 'Submit').click();
- });
+ cy.get(this.domSelector.monacoScroll).should('exist');
+ cy.get(this.domSelector.disabledSwitcher).click();
+ cy.contains('button', 'Submit').click();
});
it('should delete plugin list', function () {
diff --git a/web/cypress/integration/plugin/create-route-with-plugin-orchestration.spec.js b/web/cypress/integration/plugin/create-route-with-plugin-orchestration.spec.js
index 867baad..34ff152 100644
--- a/web/cypress/integration/plugin/create-route-with-plugin-orchestration.spec.js
+++ b/web/cypress/integration/plugin/create-route-with-plugin-orchestration.spec.js
@@ -36,7 +36,7 @@ context('Create and delete route with plugin orchestration', () => {
canvasContainer: '#container',
drawer: '.ant-drawer-content',
deleteAlert: '.ant-modal-body',
- codemirrorScroll: '.CodeMirror-scroll',
+ monacoScroll: '.monaco-scrollable-element',
};
beforeEach(() => {
@@ -114,7 +114,7 @@ context('Create and delete route with plugin orchestration', () => {
cy.visit('/routes/list');
cy.contains('routeName').siblings().contains('More').click();
cy.contains('View').click();
- cy.get(selector.codemirrorScroll).within(() => {
+ cy.get(selector.monacoScroll).within(() => {
cy.contains('script').should('exist');
});
cy.contains('Cancel').click();
diff --git a/web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js b/web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js
index 03e5f62..2567931 100644
--- a/web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js
+++ b/web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js
@@ -23,7 +23,7 @@ context('Create Configure and Delete PluginTemplate', () => {
empty: '.ant-empty-normal',
description: '#desc',
drawer: '.ant-drawer-content',
- codeMirrorMode: "[data-cy='code-mirror-mode']",
+ monacoMode: "[data-cy='monaco-mode']",
selectDropdown: '.ant-select-dropdown',
selectJSON: '.ant-select-dropdown [label=JSON]',
disabledSwitcher: '#disable',
@@ -66,7 +66,7 @@ context('Create Configure and Delete PluginTemplate', () => {
});
cy.focused(selector.drawer).should('exist');
- cy.get(selector.codeMirrorMode).click();
+ cy.get(selector.monacoMode).click();
cy.get(selector.selectDropdown).should('be.visible');
cy.get(selector.selectJSON).click();
cy.get(selector.disabledSwitcher).click({
diff --git a/web/cypress/integration/rawDataEditor/test-rawDataEditor.spec.js b/web/cypress/integration/rawDataEditor/test-rawDataEditor.spec.js
index 9f9233b..f89b341 100644
--- a/web/cypress/integration/rawDataEditor/test-rawDataEditor.spec.js
+++ b/web/cypress/integration/rawDataEditor/test-rawDataEditor.spec.js
@@ -24,6 +24,7 @@ context('Test RawDataEditor', () => {
tableBody: '.ant-table-tbody',
drawer: '.ant-drawer-content',
notification: '.ant-notification-notice-message',
+ monacoViewZones: '.view-zones'
};
const data = {
@@ -55,10 +56,10 @@ context('Test RawDataEditor', () => {
const dataSetItem = dataset[item];
- cy.window().then(({ codemirror }) => {
- if (codemirror) {
- codemirror.setValue(JSON.stringify(dataSetItem));
- }
+ cy.get(selector.monacoViewZones).should('exist').click({ force: true });
+ // edit monaco
+ cy.window().then((window) => {
+ window.monacoEditor.setValue(JSON.stringify(dataSetItem));
cy.get(selector.drawer).should('exist');
cy.get(selector.drawer, { timeout }).within(() => {
cy.contains('Submit').click({
@@ -85,13 +86,13 @@ context('Test RawDataEditor', () => {
.click();
}
- cy.window().then(({ codemirror }) => {
- if (codemirror) {
- if (item === 'Consumer') {
- codemirror.setValue(JSON.stringify({ ...dataSetItem, desc: 'newDesc' }));
- } else {
- codemirror.setValue(JSON.stringify({ ...dataSetItem, name: 'newName' }));
- }
+ cy.get(selector.monacoViewZones).should('exist').click({ force: true });
+ // edit monaco
+ cy.window().then((window) => {
+ if (item === 'Consumer') {
+ window.monacoEditor.setValue(JSON.stringify({ ...dataSetItem, desc: 'newDesc' }));
+ } else {
+ window.monacoEditor.setValue(JSON.stringify({ ...dataSetItem, name: 'newName' }));
}
cy.get(selector.drawer).should('exist');
cy.get(selector.drawer, { timeout }).within(() => {
diff --git a/web/cypress/integration/route/create-edit-duplicate-delete-route.spec.js b/web/cypress/integration/route/create-edit-duplicate-delete-route.spec.js
index a579471..87e3cbf 100644
--- a/web/cypress/integration/route/create-edit-duplicate-delete-route.spec.js
+++ b/web/cypress/integration/route/create-edit-duplicate-delete-route.spec.js
@@ -43,11 +43,11 @@ context('Create and Delete Route', () => {
checkedSwitcher: '.ant-switch-checked',
drawer: '.ant-drawer-content',
selectDropdown: '.ant-select-dropdown',
- codeMirrorMode: "[data-cy='code-mirror-mode']",
+ monacoMode: "[data-cy='monaco-mode']",
selectJSON: '.ant-select-dropdown [label=JSON]',
drawerFooter: '.ant-drawer-footer',
nameSelector: '[title=Name]',
- codemirrorScroll: '.CodeMirror-scroll',
+ monacoScroll: ".monaco-scrollable-element",
deleteAlert: '.ant-modal-body',
notificationCloseIcon: '.ant-notification-close-icon',
notification: '.ant-notification-notice-message',
@@ -137,7 +137,7 @@ context('Create and Delete Route', () => {
cy.get(selector.checkedSwitcher).should('exist');
});
- cy.get(selector.codeMirrorMode).click();
+ cy.get(selector.monacoMode).click();
cy.get(selector.selectDropdown).should('be.visible');
cy.get(selector.selectJSON).click();
@@ -181,7 +181,7 @@ context('Create and Delete Route', () => {
cy.contains('View').click();
cy.get(selector.drawer).should('be.visible');
- cy.get(selector.codemirrorScroll).within(() => {
+ cy.get(selector.monacoScroll).within(() => {
cy.contains('upstream').should('exist');
cy.contains('vars').should('exist')
cy.contains(name).should('exist');
@@ -219,7 +219,7 @@ context('Create and Delete Route', () => {
cy.contains('View').click();
cy.get(selector.drawer).should('be.visible');
- cy.get(selector.codemirrorScroll).within(() => {
+ cy.get(selector.monacoScroll).within(() => {
cy.contains('upstream').should('exist');
cy.contains(newName).should('exist');
cy.contains('vars').should('not.exist');
@@ -254,7 +254,7 @@ context('Create and Delete Route', () => {
cy.contains('View').click();
cy.get(selector.drawer).should('be.visible');
- cy.get(selector.codemirrorScroll).within(() => {
+ cy.get(selector.monacoScroll).within(() => {
cy.contains('upstream').should('exist');
cy.contains(duplicateNewName).should('exist');
});
diff --git a/web/cypress/integration/service/create-edit-delete-service.spec.js b/web/cypress/integration/service/create-edit-delete-service.spec.js
index 96ee893..74ae8f8 100644
--- a/web/cypress/integration/service/create-edit-delete-service.spec.js
+++ b/web/cypress/integration/service/create-edit-delete-service.spec.js
@@ -29,11 +29,11 @@ context('Create and Delete Service ', () => {
disabledSwitcher: '#disable',
drawer: '.ant-drawer-content',
checkedSwitcher: '.ant-switch-checked',
- codeMirrorMode: "[data-cy='code-mirror-mode']",
+ monacoScroll: ".monaco-scrollable-element",
+ monacoMode: "[data-cy='monaco-mode']",
selectDropdown: '.ant-select-dropdown',
selectJSON: '.ant-select-dropdown [label=JSON]',
drawerFooter: '.ant-drawer-footer',
- codemirrorScroll: '.CodeMirror-scroll',
notification: '.ant-notification-notice-message',
nameSelector: '[title=Name]',
};
@@ -84,7 +84,7 @@ context('Create and Delete Service ', () => {
cy.get(selector.checkedSwitcher).should('exist');
});
- cy.get(selector.codeMirrorMode).click();
+ cy.get(selector.monacoMode).click();
cy.get(selector.selectDropdown).should('be.visible');
cy.get(selector.selectJSON).click();
cy.contains('button', 'Submit').click();
@@ -122,7 +122,7 @@ context('Create and Delete Service ', () => {
cy.contains(data.serviceName).siblings().contains('View').click();
cy.get(selector.drawer).should('be.visible');
- cy.get(selector.codemirrorScroll).within(() => {
+ cy.get(selector.monacoScroll).within(() => {
cy.contains('upstream').should('exist');
cy.contains(data.serviceName).should('exist');
});
@@ -152,7 +152,7 @@ context('Create and Delete Service ', () => {
cy.contains(data.serviceName2).siblings().contains('View').click();
cy.get(selector.drawer).should('be.visible');
- cy.get(selector.codemirrorScroll).within(() => {
+ cy.get(selector.monacoScroll).within(() => {
cy.contains('upstream').should('exist');
cy.contains(data.serviceName2).should('exist');
});
diff --git a/web/cypress/integration/upstream/create_and_delete_upstream.spec.js b/web/cypress/integration/upstream/create_and_delete_upstream.spec.js
index b74467c..7d8a48d 100644
--- a/web/cypress/integration/upstream/create_and_delete_upstream.spec.js
+++ b/web/cypress/integration/upstream/create_and_delete_upstream.spec.js
@@ -28,7 +28,7 @@ context('Create and Delete Upstream', () => {
"nameSelector": "[title=Name]",
"upstreamType": ".ant-select-item-option-content",
"drawer": ".ant-drawer-content",
- "codemirrorScroll": ".CodeMirror-scroll",
+ "monacoScroll": ".monaco-scrollable-element",
"description": "#desc",
}
@@ -76,7 +76,7 @@ context('Create and Delete Upstream', () => {
cy.contains(data.upstreamName).siblings().contains('View').click();
cy.get('.ant-drawer-content').should('be.visible');
- cy.get('.CodeMirror-scroll').within(() => {
+ cy.get(selector.monacoScroll).within(() => {
cy.contains('nodes').should("exist");
cy.contains('roundrobin').should('exist');
cy.contains(data.upstreamName).should('exist');
@@ -140,7 +140,7 @@ context('Create and Delete Upstream', () => {
cy.contains(data.upstreamName).siblings().contains('View').click();
cy.get(selector.drawer).should('be.visible');
- cy.get(selector.codemirrorScroll).within(() => {
+ cy.get(selector.monacoScroll).within(() => {
cy.contains('nodes').should("exist");
cy.contains('chash').should('exist');
cy.contains(data.upstreamName).should('exist');
diff --git a/web/cypress/support/commands.js b/web/cypress/support/commands.js
index 9a8a1f9..e46ca78 100644
--- a/web/cypress/support/commands.js
+++ b/web/cypress/support/commands.js
@@ -43,8 +43,9 @@ Cypress.Commands.add('configurePlugins', (cases) => {
switch: '#disable',
close: '.anticon-close',
selectDropdown: '.ant-select-dropdown',
- codeMirrorMode: '[data-cy="code-mirror-mode"]',
- selectJSON: '.ant-select-dropdown [label=JSON]'
+ monacoMode: '[data-cy="monaco-mode"]',
+ selectJSON: '.ant-select-dropdown [label=JSON]',
+ monacoViewZones: '.view-zones'
};
cy.get(domSelector.name, { timeout }).then(function (cards) {
@@ -68,11 +69,11 @@ Cypress.Commands.add('configurePlugins', (cases) => {
// NOTE: wait for the Drawer to appear on the DOM
cy.focused(domSelector.drawer).should('exist');
- cy.get(domSelector.codeMirrorMode).invoke('text').then(text => {
+ cy.get(domSelector.monacoMode).invoke('text').then(text => {
if (text === 'Form') {
cy.wait(5000);
- cy.get(domSelector.codeMirrorMode).should('be.visible');
- cy.get(domSelector.codeMirrorMode).click();
+ cy.get(domSelector.monacoMode).should('be.visible');
+ cy.get(domSelector.monacoMode).click();
cy.get(domSelector.selectDropdown).should('be.visible');
cy.get(domSelector.selectJSON).click();
}
@@ -84,21 +85,20 @@ Cypress.Commands.add('configurePlugins', (cases) => {
});
});
- cy.get(domSelector.codeMirrorMode).invoke('text').then(text => {
+ cy.get(domSelector.monacoMode).invoke('text').then(text => {
if (text === 'Form') {
// FIXME: https://github.com/cypress-io/cypress/issues/7306
cy.wait(5000);
- cy.get(domSelector.codeMirrorMode).should('be.visible');
- cy.get(domSelector.codeMirrorMode).click();
+ cy.get(domSelector.monacoMode).should('be.visible');
+ cy.get(domSelector.monacoMode).click();
cy.get(domSelector.selectDropdown).should('be.visible');
cy.get(domSelector.selectJSON).click();
}
});
-
- cy.window().then(({ codemirror }) => {
- if (codemirror) {
- codemirror.setValue(JSON.stringify(data));
- }
+ // edit monaco
+ cy.get(domSelector.monacoViewZones).should('exist').click({ force: true });
+ cy.window().then((window) => {
+ window.monacoEditor.setValue(JSON.stringify(data));
cy.get(domSelector.drawer, { timeout }).within(() => {
cy.contains('Submit').click({
diff --git a/web/package.json b/web/package.json
index 8f425a4..b840201 100644
--- a/web/package.json
+++ b/web/package.json
@@ -4,8 +4,8 @@
"private": true,
"description": "Dashboard for Apache APISIX",
"scripts": {
- "analyze": "cross-env ANALYZE=1 umi build",
- "build": "umi build",
+ "analyze": "cross-env ANALYZE=1 yarn run build",
+ "build": "cp -R ./node_modules/monaco-editor ./public/ && umi build",
"dev": "yarn run start:dev",
"fetch:blocks": "pro fetch-blocks --branch antd@4 && yarn run prettier",
"i18n-remove": "pro i18n-remove --locale=zh-CN --write",
@@ -19,15 +19,15 @@
"lint:style": "stylelint --fix \"src/**/*.less\" --syntax less",
"prettier": "prettier -c --write \"**/*\"",
"site": "yarn run fetch:blocks && yarn run build",
- "start": "umi dev",
- "start:e2e": "cross-env SERVE_ENV=test umi dev",
+ "start": "cp -R ./node_modules/monaco-editor ./public/ && umi dev",
+ "start:e2e": "cross-env SERVE_ENV=test yarn run start",
"test:e2e": "start-server-and-test 'cross-env SERVE_ENV=test yarn start' http-get://localhost:8000 cypress:run-ci",
"test-plugin:e2e": "start-server-and-test 'cross-env SERVE_ENV=test yarn start' http-get://localhost:8000 cypress:run-plugin-ci",
- "start:dev": "cross-env REACT_APP_ENV=dev MOCK=none umi dev",
- "start:no-mock": "cross-env MOCK=none umi dev",
- "start:no-ui": "cross-env UMI_UI=none umi dev",
- "start:pre": "cross-env REACT_APP_ENV=pre umi dev",
- "start:test": "cross-env REACT_APP_ENV=test MOCK=none umi dev",
+ "start:dev": "cross-env REACT_APP_ENV=dev MOCK=none yarn run start",
+ "start:no-mock": "cross-env MOCK=none yarn run start",
+ "start:no-ui": "cross-env UMI_UI=none yarn run start",
+ "start:pre": "cross-env REACT_APP_ENV=pre yarn run start",
+ "start:test": "cross-env REACT_APP_ENV=test MOCK=none yarn run start",
"test:component": "umi test ./src/components",
"tsc": "tsc",
"cypress:open": "cross-env CYPRESS_SERVE_ENV=test cypress open",
@@ -54,10 +54,10 @@
"@ant-design/pro-table": "2.30.1",
"@antv/x6": "^1.18.5",
"@antv/x6-react-components": "^1.1.7",
+ "@monaco-editor/react": "^4.1.3",
"@rjsf/antd": "2.2.0",
"@rjsf/core": "2.2.0",
"@types/js-yaml": "^4.0.0",
- "@uiw/react-codemirror": "^3.0.5",
"ajv": "^7.0.3",
"ajv-formats": "^1.5.1",
"antd": "^4.4.0",
@@ -129,6 +129,7 @@
"express": "^4.17.1",
"lint-staged": "^10.0.0",
"mockjs": "^1.0.1-beta3",
+ "monaco-editor": "^0.24.0",
"pinst": "^2.1.1",
"prettier": "^2.0.1",
"pro-download": "1.0.1",
diff --git a/web/src/components/Plugin/PluginDetail.tsx b/web/src/components/Plugin/PluginDetail.tsx
index 0028116..c4daac6 100644
--- a/web/src/components/Plugin/PluginDetail.tsx
+++ b/web/src/components/Plugin/PluginDetail.tsx
@@ -14,30 +14,31 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import React, { useEffect, useRef, useState } from 'react';
+import React, { useEffect, useState } from 'react';
import {
+ Alert,
Button,
+ Divider,
+ Drawer,
+ Form,
+ Input,
notification,
PageHeader,
- Switch,
- Form,
+ Popconfirm,
Select,
- Divider,
- Drawer,
- Alert,
Space,
- Popconfirm,
- Tooltip,
- Input,
+ Switch,
} from 'antd';
import { useIntl } from 'umi';
-import CodeMirror from '@uiw/react-codemirror';
import { js_beautify } from 'js-beautify';
import { LinkOutlined } from '@ant-design/icons';
import Ajv from 'ajv';
import type { DefinedError } from 'ajv';
import addFormats from 'ajv-formats';
import { compact, omit } from 'lodash';
+import type { Monaco } from "@monaco-editor/react";
+import Editor from "@monaco-editor/react";
+import type {languages} from "monaco-editor";
import { fetchSchema } from './service';
import { json2yaml, yaml2json } from '../../helpers';
@@ -55,7 +56,7 @@ type Props = {
maskClosable?: boolean;
isEnabled?: boolean;
onClose?: () => void;
- onChange?: (data: any) => void;
+ onChange?: (data: PluginComponent.PluginDetailValues) => void;
};
const ajv = new Ajv();
@@ -98,26 +99,24 @@ const PluginDetail: React.FC<Props> = ({
onChange = () => { },
}) => {
const { formatMessage } = useIntl();
- enum codeMirrorModeList {
+ enum monacoModeList {
JSON = 'JSON',
YAML = 'YAML',
UIForm = 'Form'
}
const [form] = Form.useForm();
const [UIForm] = Form.useForm();
- const ref = useRef<any>(null);
const data = initialData[name] || {};
const pluginType = pluginList.find((item) => item.name === name)?.type;
- const [codeMirrorMode, setCodeMirrorMode] = useState<PluginComponent.CodeMirrorMode>(
- codeMirrorModeList.JSON,
- );
+ const [content, setContent] = useState<string>(JSON.stringify(data, null, 2));
+ const [monacoMode, setMonacoMode] = useState<PluginComponent.MonacoLanguage>(monacoModeList.JSON);
const modeOptions: { label: string; value: string }[] = [
- { label: codeMirrorModeList.JSON, value: codeMirrorModeList.JSON },
- { label: codeMirrorModeList.YAML, value: codeMirrorModeList.YAML },
+ { label: monacoModeList.JSON, value: monacoModeList.JSON },
+ { label: monacoModeList.YAML, value: monacoModeList.YAML },
];
if (PLUGIN_UI_LIST.includes(name)) {
- modeOptions.push({ label: formatMessage({ id: 'component.plugin.form' }), value: codeMirrorModeList.UIForm });
+ modeOptions.push({ label: formatMessage({ id: 'component.plugin.form' }), value: monacoModeList.UIForm });
}
const getUIFormData = () => {
@@ -150,11 +149,48 @@ const PluginDetail: React.FC<Props> = ({
scope: 'global',
});
if (PLUGIN_UI_LIST.includes(name)) {
- setCodeMirrorMode(codeMirrorModeList.UIForm);
+ setMonacoMode(monacoModeList.UIForm);
setUIFormData(initialData[name]);
- };
+ }
}, []);
+ const formatYaml = (yaml: string): string => {
+ const json=yaml2json(yaml,true)
+ if (json.error){
+ return yaml
+ }
+ return json2yaml(json.data).data;
+ }
+
+ const editorWillMount = (monaco: Monaco) => {
+ fetchSchema(name, schemaType).then((schema)=> {
+ const schemaConfig: languages.json.DiagnosticsOptions = {
+ validate: true,
+ schemas: [
+ {
+ // useless placeholder
+ uri: `https://apisix.apache.org/`,
+ fileMatch: ['*'],
+ schema
+ }
+ ],
+ trailingCommas: "error",
+ enableSchemaRequest: false
+ };
+ const yamlFormatProvider: languages.DocumentFormattingEditProvider = {
+ provideDocumentFormattingEdits(model) {
+ return [{
+ text: formatYaml(model.getValue()),
+ range: model.getFullModelRange()
+ }];
+ }
+ };
+ monaco.languages.registerDocumentFormattingEditProvider("yaml",yamlFormatProvider);
+ monaco.editor.getModels().forEach(model => model.updateOptions({tabSize: 2}));
+ monaco.languages.json.jsonDefaults.setDiagnosticsOptions(schemaConfig);
+ })
+ }
+
const validateData = (pluginName: string, value: PluginComponent.Data) => {
return fetchSchema(pluginName, schemaType).then((schema) => {
return new Promise((resolve) => {
@@ -199,53 +235,40 @@ const PluginDetail: React.FC<Props> = ({
});
});
};
- const handleModeChange = (value: PluginComponent.CodeMirrorMode) => {
+
+ const handleModeChange = (value: PluginComponent.MonacoLanguage) => {
switch (value) {
- case codeMirrorModeList.JSON: {
- if (codeMirrorMode === codeMirrorModeList.YAML) {
- const { data: yamlData, error } = yaml2json(ref.current.editor.getValue(), true);
+ case monacoModeList.JSON: {
+ if (monacoMode === monacoModeList.YAML) {
+ const { data: yamlData, error } = yaml2json(content, true);
if (error) {
- notification.error({
- message: 'Invalid Yaml data',
- });
+ notification.error({message: formatMessage({id: 'component.global.invalidYaml'})});
return;
}
- ref.current.editor.setValue(
- js_beautify(yamlData, {
- indent_size: 2,
- }),
- );
+ setContent(js_beautify(yamlData, { indent_size: 2 }))
} else {
- ref.current.editor.setValue(
- js_beautify(JSON.stringify(getUIFormData()), {
- indent_size: 2,
- }),
- );
+ setContent(js_beautify(JSON.stringify(getUIFormData()), { indent_size: 2 }))
}
break;
}
- case codeMirrorModeList.YAML: {
- const { data: jsonData, error } = json2yaml(codeMirrorMode === codeMirrorModeList.JSON ? ref.current.editor.getValue() : JSON.stringify(getUIFormData()));
-
- if (error) {
- notification.error({
- message: 'Invalid Json data',
- });
+ case monacoModeList.YAML: {
+ const jsonData = monacoMode === monacoModeList.JSON ? content : JSON.stringify(getUIFormData());
+ const { data: yamlData, error } = json2yaml(jsonData);
+ if (error){
+ notification.error({ message: formatMessage({ id:'component.global.invalidJson' }) });
return;
}
- ref.current.editor.setValue(jsonData);
+ setContent(yamlData);
break;
}
- case codeMirrorModeList.UIForm: {
- if (codeMirrorMode === codeMirrorModeList.JSON) {
- setUIFormData(JSON.parse(ref.current.editor.getValue()));
+ case monacoModeList.UIForm: {
+ if (monacoMode === monacoModeList.JSON) {
+ setUIFormData(JSON.parse(content));
} else {
- const { data: yamlData, error } = yaml2json(ref.current.editor.getValue(), true);
+ const { data: yamlData, error } = yaml2json(content, true);
if (error) {
- notification.error({
- message: 'Invalid Yaml data',
- });
+ notification.error({ message: formatMessage({ id:'component.global.invalidYaml' }) });
return;
}
setUIFormData(JSON.parse(yamlData));
@@ -255,26 +278,10 @@ const PluginDetail: React.FC<Props> = ({
default:
break;
}
- setCodeMirrorMode(value);
+ setMonacoMode(value);
};
- const formatCodes = () => {
- try {
- if (ref.current) {
- ref.current.editor.setValue(
- js_beautify(ref.current.editor.getValue(), {
- indent_size: 2,
- }),
- );
- }
- } catch (error) {
- notification.error({
- message: 'Format failed',
- });
- }
- };
-
- const isNoConfigurationRequired = pluginType === PluginType.authentication && schemaType !== 'consumer' && (codeMirrorMode !== codeMirrorModeList.UIForm)
+ const isNoConfigurationRequired = pluginType === PluginType.authentication && schemaType !== 'consumer' && (monacoMode !== monacoModeList.UIForm);
return (
<Drawer
@@ -301,7 +308,7 @@ const PluginDetail: React.FC<Props> = ({
onConfirm={() => {
onChange({
formData: form.getFieldsValue(),
- codemirrorData: {},
+ monacoData: {},
shouldDelete: true,
});
}}
@@ -319,21 +326,19 @@ const PluginDetail: React.FC<Props> = ({
onClick={() => {
try {
let editorData;
- if (codeMirrorMode === codeMirrorModeList.JSON) {
- editorData = JSON.parse(ref.current?.editor.getValue());
- } else if (codeMirrorMode === codeMirrorModeList.YAML) {
- editorData = yaml2json(ref.current?.editor.getValue(), false).data;
+ if (monacoMode === monacoModeList.JSON) {
+ editorData = JSON.parse(content);
+ } else if (monacoMode === monacoModeList.YAML) {
+ editorData = yaml2json(content, false).data;
} else {
editorData = getUIFormData();
}
validateData(name, editorData).then((value) => {
- onChange({ formData: form.getFieldsValue(), codemirrorData: value });
+ onChange({ formData: form.getFieldsValue(), monacoData: value });
});
} catch (error) {
- notification.error({
- message: 'Invalid JSON data',
- });
+ notification.error({ message: formatMessage({ id:'component.global.invalidJson' }) });
}
}}
>
@@ -384,20 +389,13 @@ const PluginDetail: React.FC<Props> = ({
ghost={false}
extra={[
<Select
- defaultValue={codeMirrorModeList.JSON}
- value={codeMirrorMode}
+ defaultValue={monacoModeList.JSON}
+ value={monacoMode}
options={modeOptions}
- onChange={(value: PluginComponent.CodeMirrorMode) => {
- handleModeChange(value);
- }}
- data-cy='code-mirror-mode'
+ onChange={handleModeChange}
+ data-cy='monaco-mode'
key={1}
- ></Select>,
- <Tooltip title={formatMessage({ id: "component.plugin.format-codes.disable" })} key={2}>
- <Button type="primary" onClick={formatCodes} disabled={codeMirrorMode === codeMirrorModeList.UIForm}>
- {formatMessage({ id: 'component.global.format' })}
- </Button>
- </Tooltip>,
+ />,
<Button
type="default"
icon={<LinkOutlined />}
@@ -414,25 +412,34 @@ const PluginDetail: React.FC<Props> = ({
</Button>
]}
/>
- {Boolean(codeMirrorMode === codeMirrorModeList.UIForm) && <PluginForm name={name} form={UIForm} renderForm={!(pluginType === PluginType.authentication && schemaType !== 'consumer')} />}
- <div style={{ display: codeMirrorMode === codeMirrorModeList.UIForm ? 'none' : 'unset' }}><CodeMirror
- ref={(codemirror) => {
- ref.current = codemirror;
- if (codemirror) {
+ {Boolean(monacoMode === monacoModeList.UIForm) && <PluginForm name={name} form={UIForm} renderForm={!(pluginType === PluginType.authentication && schemaType !== 'consumer')} />}
+ <div style={{ display: monacoMode === monacoModeList.UIForm ? 'none' : 'unset' }}>
+ <Editor
+ value={content}
+ onChange={text=>{
+ if (text) {
+ setContent(text);
+ } else {
+ setContent('');
+ }
+ }}
+ language={monacoMode.toLocaleLowerCase()}
+ onMount={(editor)=>{
// NOTE: for debug & test
// @ts-ignore
- window.codemirror = codemirror.editor;
- }
- }}
- value={JSON.stringify(data, null, 2)}
- options={{
- mode: codeMirrorMode,
- readOnly: (readonly || isNoConfigurationRequired) ? 'nocursor' : '',
- lineWrapping: true,
- lineNumbers: true,
- showCursorWhenSelecting: true,
- autofocus: true,
- }} />
+ window.monacoEditor = editor;
+ }}
+ beforeMount={editorWillMount}
+ options={{
+ scrollbar: {
+ vertical: 'hidden',
+ horizontal: 'hidden',
+ },
+ wordWrap: "on",
+ minimap: {enabled: false},
+ readOnly: readonly,
+ }}
+ />
</div>
</Drawer>
);
diff --git a/web/src/components/Plugin/PluginPage.tsx b/web/src/components/Plugin/PluginPage.tsx
index 5b50167..6ae0224 100644
--- a/web/src/components/Plugin/PluginPage.tsx
+++ b/web/src/components/Plugin/PluginPage.tsx
@@ -250,10 +250,10 @@ const PluginPage: React.FC<Props> = ({
onClose={() => {
setName(NEVER_EXIST_PLUGIN_FLAG);
}}
- onChange={({ codemirrorData, formData, shouldDelete }) => {
+ onChange={({ monacoData, formData, shouldDelete }) => {
let newPlugins = {
...initialData,
- [name]: { ...codemirrorData, disable: !formData.disable },
+ [name]: { ...monacoData, disable: !formData.disable },
};
if (shouldDelete === true) {
newPlugins = omit(newPlugins, name);
diff --git a/web/src/components/Plugin/typing.d.ts b/web/src/components/Plugin/typing.d.ts
index 0fe2052..126b560 100644
--- a/web/src/components/Plugin/typing.d.ts
+++ b/web/src/components/Plugin/typing.d.ts
@@ -31,5 +31,11 @@ declare namespace PluginComponent {
type ReferPage = '' | 'route' | 'consumer' | 'service' | 'plugin';
- type CodeMirrorMode = 'JSON' | 'YAML' | 'Form';
+ type PluginDetailValues = {
+ formData: any;
+ monacoData: any;
+ shouldDelete?: boolean;
+ }
+
+ type MonacoLanguage = 'JSON' | 'YAML' | 'Form';
}
diff --git a/web/src/components/PluginFlow/PluginFlow.tsx b/web/src/components/PluginFlow/PluginFlow.tsx
index f49130c..21afb12 100644
--- a/web/src/components/PluginFlow/PluginFlow.tsx
+++ b/web/src/components/PluginFlow/PluginFlow.tsx
@@ -155,12 +155,12 @@ const PluginFlow: React.FC<Props> = ({ chart, readonly = false }) => {
onClose={() => {
setPluginProps(DEFAULT_PLUGIN_PROPS)
}}
- onChange={({ formData, codemirrorData, shouldDelete }) => {
+ onChange={({ formData, monacoData, shouldDelete }) => {
if (shouldDelete) {
FlowGraph.graph.removeCell(pluginProps.id)
} else {
const disable = !formData.disable
- FlowGraph.setData(pluginProps.id, { ...codemirrorData, disable })
+ FlowGraph.setData(pluginProps.id, { ...monacoData, disable })
}
setPluginProps(DEFAULT_PLUGIN_PROPS)
}}
diff --git a/web/src/components/RawDataEditor/RawDataEditor.tsx b/web/src/components/RawDataEditor/RawDataEditor.tsx
index 8f9ba0d..bf8a24d 100644
--- a/web/src/components/RawDataEditor/RawDataEditor.tsx
+++ b/web/src/components/RawDataEditor/RawDataEditor.tsx
@@ -14,13 +14,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import React, { useEffect, useRef, useState } from 'react';
-import { Button, Drawer, PageHeader, notification, Space, Select } from 'antd';
+import React, { useEffect, useState } from 'react';
+import { Button, Drawer, notification, PageHeader, Select, Space } from 'antd';
import { LinkOutlined } from '@ant-design/icons';
-import CodeMirror from '@uiw/react-codemirror';
import { CopyToClipboard } from 'react-copy-to-clipboard';
import { useIntl } from 'umi';
import { js_beautify } from 'js-beautify';
+import type {Monaco} from "@monaco-editor/react";
+import Editor from "@monaco-editor/react";
+import type {languages} from "monaco-editor";
import { json2yaml, yaml2json } from '../../helpers';
@@ -33,78 +35,92 @@ type Props = {
onSubmit?: (data: Record<string, any>) => void;
};
-enum codeMirrorModeList {
+enum monacoLanguageList {
JSON = 'JSON',
YAML = 'YAML',
}
const RawDataEditor: React.FC<Props> = ({ visible, readonly = true, type, data = {}, onClose = () => { }, onSubmit = () => { } }) => {
- const ref = useRef<any>(null);
const { formatMessage } = useIntl();
- const [codeMirrorMode, setCodeMirrorMode] = useState<PluginComponent.CodeMirrorMode>(
- codeMirrorModeList.JSON,
+ const [monacoLanguage, setMonacoLanguage] = useState<PluginComponent.MonacoLanguage>(
+ monacoLanguageList.JSON,
);
+ const [content, setContent] = useState('')
useEffect(() => {
- setCodeMirrorMode(codeMirrorModeList.JSON);
+ switch (monacoLanguage) {
+ case monacoLanguageList.JSON:
+ setContent(JSON.stringify(data, null, 2));
+ break;
+ case monacoLanguageList.YAML: {
+ const {data: yamlData} = json2yaml(JSON.stringify(data, null, 2));
+ setContent(yamlData)
+ break;
+ }
+ default:
+ }
+ }, [data])
+
+ useEffect(() => {
+ setMonacoLanguage(monacoLanguageList.JSON);
}, [visible])
const modeOptions = [
- { label: codeMirrorModeList.JSON, value: codeMirrorModeList.JSON },
- { label: codeMirrorModeList.YAML, value: codeMirrorModeList.YAML },
+ { label: monacoLanguageList.JSON, value: monacoLanguageList.JSON },
+ { label: monacoLanguageList.YAML, value: monacoLanguageList.YAML },
];
- const handleModeChange = (value: PluginComponent.CodeMirrorMode) => {
+ const handleModeChange = (value: PluginComponent.MonacoLanguage) => {
switch (value) {
- case codeMirrorModeList.JSON: {
- const { data: yamlData, error } = yaml2json(ref.current.editor.getValue(), true);
-
- if (error) {
- notification.error({
- message: 'Invalid Yaml data',
- });
- return;
- }
- ref.current.editor.setValue(
- js_beautify(yamlData, {
- indent_size: 2,
- }),
- );
+ case monacoLanguageList.JSON:
+ setContent(c => {
+ const {data:jsonData,error} = yaml2json(c, true);
+ if (error){
+ notification.error({message: formatMessage({ id: 'component.global.invalidYaml' })});
+ return c;
+ }
+ return js_beautify(jsonData, {indent_size: 2});
+ });
break;
- }
- case codeMirrorModeList.YAML: {
- const { data: jsonData, error } = json2yaml(ref.current.editor.getValue());
-
- if (error) {
- notification.error({
- message: 'Invalid JSON data',
- });
- return;
- }
- ref.current.editor.setValue(jsonData);
+ case monacoLanguageList.YAML:
+ setContent(c => {
+ const {data:yamlData,error} = json2yaml(c);
+ if (error){
+ notification.error({message: formatMessage({ id: 'component.global.invalidJson' })});
+ return c;
+ }
+ return yamlData;
+ });
break;
- }
default:
break;
}
- setCodeMirrorMode(value);
+ setMonacoLanguage(value)
};
- const formatCodes = () => {
- try {
- if (ref.current) {
- ref.current.editor.setValue(
- js_beautify(ref.current.editor.getValue(), {
- indent_size: 2,
- }),
- );
- }
- } catch (error) {
- notification.error({
- message: 'Format failed',
- });
+ const formatYaml = (yaml: string): string=> {
+ const json=yaml2json(yaml,true)
+ if (json.error){
+ return yaml;
}
- };
+ return json2yaml(json.data).data;
+ }
+
+ const editorWillMount = (monaco: Monaco) => {
+ const yamlFormatProvider: languages.DocumentFormattingEditProvider = {
+ provideDocumentFormattingEdits(model) {
+ return [{
+ text: formatYaml(model.getValue()),
+ range: model.getFullModelRange()
+ }];
+ }
+ };
+ monaco.languages.registerDocumentFormattingEditProvider("yaml",yamlFormatProvider);
+ monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
+ validate: true,
+ trailingCommas: "error",
+ });
+ }
return (
<>
@@ -126,14 +142,12 @@ const RawDataEditor: React.FC<Props> = ({ visible, readonly = true, type, data =
onClick={() => {
try {
const editorData =
- codeMirrorMode === codeMirrorModeList.JSON
- ? JSON.parse(ref.current?.editor.getValue())
- : yaml2json(ref.current?.editor.getValue(), false).data;
+ monacoLanguage === monacoLanguageList.JSON
+ ? JSON.parse(content)
+ : yaml2json(content, false).data;
onSubmit(editorData);
} catch (error) {
- notification.error({
- message: 'Invalid JSON data',
- });
+ notification.error({message: formatMessage({ id: 'component.global.invalidJson' })});
}
}}
>
@@ -147,18 +161,15 @@ const RawDataEditor: React.FC<Props> = ({ visible, readonly = true, type, data =
title=""
extra={[
<Select
- defaultValue={codeMirrorModeList.JSON}
- value={codeMirrorMode}
+ defaultValue={monacoLanguageList.JSON}
+ value={monacoLanguage}
options={modeOptions}
- onChange={(value: PluginComponent.CodeMirrorMode) => {
+ onChange={(value: PluginComponent.MonacoLanguage) => {
handleModeChange(value);
}}
- data-cy='code-mirror-mode'
+ data-cy='monaco-language'
/>,
- <Button type="primary" onClick={formatCodes} key={2}>
- {formatMessage({ id: 'component.global.format' })}
- </Button>,
- <CopyToClipboard text={JSON.stringify(data)} onCopy={(_: string, result: boolean) => {
+ <CopyToClipboard text={content} onCopy={(_: string, result: boolean) => {
if (!result) {
notification.error({
message: formatMessage({ id: 'component.global.copyFail' }),
@@ -187,23 +198,30 @@ const RawDataEditor: React.FC<Props> = ({ visible, readonly = true, type, data =
</Button>,
]}
/>
- <CodeMirror
- ref={(codemirror) => {
- ref.current = codemirror;
- if (codemirror) {
- // NOTE: for debug & test
- // @ts-ignore
- window.codemirror = codemirror.editor;
+ <Editor
+ value={content}
+ onChange={text=>{
+ if (text){
+ setContent(text);
+ } else {
+ setContent('');
}
}}
- value={JSON.stringify(data, null, 2)}
+ onMount={(editor)=>{
+ // NOTE: for debug & test
+ // @ts-ignore
+ window.monacoEditor = editor;
+ }}
+ beforeMount={editorWillMount}
+ language={monacoLanguage.toLocaleLowerCase()}
options={{
- mode: 'json-ld',
- readOnly: readonly ? 'nocursor' : '',
- lineWrapping: true,
- lineNumbers: true,
- showCursorWhenSelecting: true,
- autofocus: true,
+ scrollbar:{
+ vertical: 'hidden',
+ horizontal: 'hidden',
+ },
+ wordWrap: "on",
+ minimap: {enabled: false},
+ readOnly: readonly,
}}
/>
</Drawer>
diff --git a/web/src/global.tsx b/web/src/global.tsx
index 346ea4d..e921c31 100644
--- a/web/src/global.tsx
+++ b/web/src/global.tsx
@@ -18,8 +18,17 @@ import { Button, message, notification } from 'antd';
import React from 'react';
import { formatMessage } from 'umi';
+import { loader } from '@monaco-editor/react';
+import { getLocale } from 'umi';
+
import defaultSettings from '../config/defaultSettings';
+// see https://github.com/suren-atoyan/monaco-react/issues/168
+loader.config({ paths: { vs: '/monaco-editor/min/vs' } });
+if (getLocale() === 'zh-CN'){
+ loader.config({ "vs/nls": { availableLanguages: { "*": 'zh-cn' } } });
+}
+
const { pwa } = defaultSettings;
// if pwa is true
if (pwa) {
diff --git a/web/src/locales/en-US/component.ts b/web/src/locales/en-US/component.ts
index 4b9eba9..71e959b 100644
--- a/web/src/locales/en-US/component.ts
+++ b/web/src/locales/en-US/component.ts
@@ -81,4 +81,7 @@ export default {
'component.global.copy': 'Copy',
'component.global.copySuccess': 'Copy Successfully ',
'component.global.copyFail': 'Copy Failed',
+
+ 'component.global.invalidYaml': 'Invalid Yaml data',
+ 'component.global.invalidJson': 'Invalid Json data',
};
diff --git a/web/src/locales/zh-CN/component.ts b/web/src/locales/zh-CN/component.ts
index 2acc640..112211e 100644
--- a/web/src/locales/zh-CN/component.ts
+++ b/web/src/locales/zh-CN/component.ts
@@ -77,4 +77,7 @@ export default {
'component.global.copy': '复制',
'component.global.copySuccess': '复制成功',
'component.global.copyFail': '复制失败',
+
+ 'component.global.invalidYaml': '无效的 Yaml 数据',
+ 'component.global.invalidJson': '无效的 Json 数据',
};
diff --git a/web/src/pages/Plugin/List.tsx b/web/src/pages/Plugin/List.tsx
index d0cbe3c..1df3f1e 100644
--- a/web/src/pages/Plugin/List.tsx
+++ b/web/src/pages/Plugin/List.tsx
@@ -125,11 +125,11 @@ const Page: React.FC = () => {
onClose={() => {
setVisible(false);
}}
- onChange={({ formData, codemirrorData, shouldDelete }) => {
+ onChange={({ formData, monacoData, shouldDelete }) => {
const disable = !formData.disable;
let plugins = {
...initialData,
- [name]: { ...codemirrorData, disable },
+ [name]: { ...monacoData, disable },
};
if (shouldDelete === true) {
plugins = omit(plugins, name);
diff --git a/web/src/pages/Route/components/DebugViews/DebugDrawView.tsx b/web/src/pages/Route/components/DebugViews/DebugDrawView.tsx
index fa4b840..602a1fe 100644
--- a/web/src/pages/Route/components/DebugViews/DebugDrawView.tsx
+++ b/web/src/pages/Route/components/DebugViews/DebugDrawView.tsx
@@ -14,29 +14,31 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import React, { useEffect, useState, useRef } from 'react';
+import React, { useEffect, useState } from 'react';
import { Button, Card, Drawer, Form, Input, notification, Radio, Select, Spin, Tabs } from 'antd';
import { useIntl } from 'umi';
-import CodeMirror from '@uiw/react-codemirror';
import queryString from 'query-string';
import Base64 from 'base-64';
import urlRegexSafe from 'url-regex-safe';
import CopyToClipboard from "react-copy-to-clipboard";
import { CopyOutlined } from "@ant-design/icons";
+import type * as monacoEditor from "monaco-editor";
+import type { Monaco } from "@monaco-editor/react";
+import Editor from "@monaco-editor/react";
import PanelSection from '@/components/PanelSection';
import {
- HTTP_METHOD_OPTION_LIST,
- DEFAULT_DEBUG_PARAM_FORM_DATA,
- DEFAULT_DEBUG_AUTH_FORM_DATA,
- PROTOCOL_SUPPORTED,
+ DEBUG_BODY_MODE_SUPPORTED,
DEBUG_BODY_TYPE_SUPPORTED,
- DEBUG_BODY_CODEMIRROR_MODE_SUPPORTED,
- DEBUG_RESPONSE_BODY_CODEMIRROR_MODE_SUPPORTED,
+ DEBUG_RESPONSE_BODY_MODE_SUPPORTED,
DebugBodyFormDataValueType,
+ DEFAULT_DEBUG_AUTH_FORM_DATA,
+ DEFAULT_DEBUG_PARAM_FORM_DATA,
+ HTTP_METHOD_OPTION_LIST,
+ PROTOCOL_SUPPORTED,
} from '../../constants';
-import { DebugParamsView, AuthenticationView, DebugFormDataView } from '.';
+import { AuthenticationView, DebugFormDataView, DebugParamsView } from '.';
import { debugRoute } from '../../service';
import styles from './index.less';
@@ -56,15 +58,15 @@ const DebugDrawView: React.FC<RouteModule.DebugDrawProps> = (props) => {
const [headerForm] = Form.useForm();
const [response, setResponse] = useState<RouteModule.debugResponse | null>()
const [loading, setLoading] = useState(false);
- const [codeMirrorHeight, setCodeMirrorHeight] = useState<number | string>(50);
- const bodyCodeMirrorRef = useRef<any>(null);
+ const [body, setBody] = useState('');
+ const [height,setHeight] = useState(50);
const [bodyType, setBodyType] = useState('none');
const methodWithoutBody = ['GET', 'HEAD'];
- const [responseBodyCodeMirrorMode, setResponseBodyCodeMirrorMode] = useState(
- DEBUG_RESPONSE_BODY_CODEMIRROR_MODE_SUPPORTED[0].mode,
+ const [responseBodyMode, setResponseBodyMode] = useState(
+ DEBUG_RESPONSE_BODY_MODE_SUPPORTED[0].mode,
);
- const [bodyCodeMirrorMode, setBodyCodeMirrorMode] = useState(
- DEBUG_BODY_CODEMIRROR_MODE_SUPPORTED[0].mode,
+ const [bodyMode, setBodyCodeMode] = useState(
+ DEBUG_BODY_MODE_SUPPORTED[0].mode,
);
enum DebugBodyType {
@@ -114,21 +116,21 @@ const DebugDrawView: React.FC<RouteModule.DebugDrawProps> = (props) => {
}
case DEBUG_BODY_TYPE_SUPPORTED[DebugBodyType.RawInput]: {
let contentType = [''];
- switch (bodyCodeMirrorMode) {
- case DEBUG_BODY_CODEMIRROR_MODE_SUPPORTED[0].mode:
+ switch (bodyMode) {
+ case DEBUG_BODY_MODE_SUPPORTED[0].mode:
contentType = ['application/json;charset=UTF-8'];
break;
- case DEBUG_BODY_CODEMIRROR_MODE_SUPPORTED[1].mode:
+ case DEBUG_BODY_MODE_SUPPORTED[1].mode:
contentType = ['text/plain;charset=UTF-8'];
break;
- case DEBUG_BODY_CODEMIRROR_MODE_SUPPORTED[2].mode:
+ case DEBUG_BODY_MODE_SUPPORTED[2].mode:
contentType = ['application/xml;charset=UTF-8'];
break;
default: break;
}
return {
- bodyFormData: bodyCodeMirrorRef.current.editor.getValue(),
+ bodyFormData: body,
header: {
'Content-type': contentType
}
@@ -241,22 +243,34 @@ const DebugDrawView: React.FC<RouteModule.DebugDrawProps> = (props) => {
setResponse(resp);
const contentType = resp.header["Content-Type"];
if (contentType == null || contentType.length !== 1) {
- setResponseBodyCodeMirrorMode("TEXT");
+ setResponseBodyMode("TEXT");
} else if (contentType[0].toLowerCase().indexOf("json") !== -1) {
- setResponseBodyCodeMirrorMode("JSON");
+ setResponseBodyMode("JSON");
} else if (contentType[0].toLowerCase().indexOf("xml") !== -1) {
- setResponseBodyCodeMirrorMode("XML");
+ setResponseBodyMode("XML");
} else if (contentType[0].toLowerCase().indexOf("html") !== -1) {
- setResponseBodyCodeMirrorMode("HTML");
+ setResponseBodyMode("HTML");
} else {
- setResponseBodyCodeMirrorMode("TEXT");
+ setResponseBodyMode("TEXT");
}
- setCodeMirrorHeight('auto');
})
.catch(() => {
setLoading(false);
});
};
+
+ const handleEditorMount = (editor: monacoEditor.editor.IStandaloneCodeEditor, monaco: Monaco) => {
+ editor.onDidChangeModelDecorations(() => {
+ if (!editor.getDomNode()) {
+ return;
+ }
+ const padding = 40;
+ const lineHeight = editor.getOption(monaco.editor.EditorOption.lineHeight);
+ const lineCount = editor.getModel()?.getLineCount() || 1;
+ setHeight(editor.getTopForLineNumber(lineCount + 1) + lineHeight + padding);
+ })
+ };
+
return (
<Drawer
title={formatMessage({ id: 'page.route.onlineDebug' })}
@@ -321,7 +335,6 @@ const DebugDrawView: React.FC<RouteModule.DebugDrawProps> = (props) => {
onChange={(e) => {
if (e.currentTarget.value === '') {
resetForms();
- setCodeMirrorHeight(50);
}
}}
/>
@@ -357,12 +370,12 @@ const DebugDrawView: React.FC<RouteModule.DebugDrawProps> = (props) => {
<Select
size="small"
onChange={(value) => {
- setBodyCodeMirrorMode(value);
+ setBodyCodeMode(value);
}}
style={{ width: 100 }}
- defaultValue={bodyCodeMirrorMode}
+ defaultValue={bodyMode}
>
- {DEBUG_BODY_CODEMIRROR_MODE_SUPPORTED.map((modeObj) => (
+ {DEBUG_BODY_MODE_SUPPORTED.map((modeObj) => (
<Option key={modeObj.name} value={modeObj.mode}>
{modeObj.name}
</Option>
@@ -381,23 +394,27 @@ const DebugDrawView: React.FC<RouteModule.DebugDrawProps> = (props) => {
{bodyType === DEBUG_BODY_TYPE_SUPPORTED[DebugBodyType.RawInput] && (
<Form>
<Form.Item>
- <CodeMirror
- ref={(codemirror) => {
- bodyCodeMirrorRef.current = codemirror;
- if (codemirror) {
- // NOTE: for debug & test
- window.codeMirrorBody = codemirror.editor;
+ <Editor
+ value={body}
+ language={bodyMode.toLowerCase()}
+ onChange={(text)=>{
+ if (text){
+ setBody(text);
+ } else {
+ setBody('');
}
}}
height={250}
+ beforeMount={(monaco) => {
+ monaco?.languages.json.jsonDefaults.setDiagnosticsOptions({validate: false});
+ }}
options={{
- mode: bodyCodeMirrorMode,
- readOnly: '',
- lineWrapping: true,
- lineNumbers: true,
- showCursorWhenSelecting: true,
- autofocus: true,
- scrollbarStyle: null,
+ scrollbar:{
+ vertical: 'hidden',
+ horizontal: 'hidden',
+ },
+ wordWrap: "on",
+ minimap: {enabled: false},
}}
/>
</Form.Item>
@@ -416,10 +433,10 @@ const DebugDrawView: React.FC<RouteModule.DebugDrawProps> = (props) => {
<TabPane tab={formatMessage({ id: 'page.route.TabPane.response' })} key="response">
<Select
disabled={response == null}
- value={responseBodyCodeMirrorMode}
- onSelect={(mode) => setResponseBodyCodeMirrorMode(mode as string)}>
+ value={responseBodyMode}
+ onSelect={(mode) => setResponseBodyMode(mode as string)}>
{
- DEBUG_RESPONSE_BODY_CODEMIRROR_MODE_SUPPORTED.map(mode => {
+ DEBUG_RESPONSE_BODY_MODE_SUPPORTED.map(mode => {
return <Option value={mode.mode} key={mode.mode}>{mode.name}</Option>
})
}
@@ -441,18 +458,24 @@ const DebugDrawView: React.FC<RouteModule.DebugDrawProps> = (props) => {
<CopyOutlined />
</Button>
</CopyToClipboard>
- <div id='codeMirror-response' style={{ marginTop: 16 }}>
- <CodeMirror
+ <div id='monaco-response' style={{ marginTop: 16 }}>
+ <Editor
value={response ? response.data : ""}
- height={codeMirrorHeight}
+ height={height}
+ language={responseBodyMode.toLowerCase()}
+ onMount={handleEditorMount}
+ beforeMount={(monaco) => {
+ monaco?.languages.json.jsonDefaults.setDiagnosticsOptions({validate: false});
+ }}
options={{
- mode: responseBodyCodeMirrorMode,
- readOnly: 'nocursor',
- lineWrapping: true,
- lineNumbers: true,
- showCursorWhenSelecting: true,
- autofocus: true,
- scrollbarStyle: null,
+ automaticLayout:true,
+ scrollbar:{
+ vertical: 'hidden',
+ horizontal: 'hidden',
+ },
+ wordWrap: "on",
+ minimap: {enabled: false},
+ readOnly: true,
}}
/>
</div>
diff --git a/web/src/pages/Route/constants.ts b/web/src/pages/Route/constants.ts
index f0aeea9..6688287 100644
--- a/web/src/pages/Route/constants.ts
+++ b/web/src/pages/Route/constants.ts
@@ -164,18 +164,17 @@ export const DEBUG_BODY_TYPE_SUPPORTED: RouteModule.DebugBodyType[] = [
'raw input',
];
-// Note: codemirror mode: apl for text; javascript for json(need to format); xml for xml;
-export const DEBUG_BODY_CODEMIRROR_MODE_SUPPORTED = [
- { name: 'JSON', mode: 'javascript' },
- { name: 'TEXT', mode: 'apl' },
+export const DEBUG_BODY_MODE_SUPPORTED = [
+ { name: 'JSON', mode: 'json' },
+ { name: 'TEXT', mode: 'text' },
{ name: 'XML', mode: 'xml' },
];
-export const DEBUG_RESPONSE_BODY_CODEMIRROR_MODE_SUPPORTED = [
- { name: 'JSON', mode: 'javascript' },
+export const DEBUG_RESPONSE_BODY_MODE_SUPPORTED = [
+ { name: 'JSON', mode: 'json' },
{ name: 'XML', mode: 'xml' },
{ name: 'HTML', mode: 'html' },
- { name: 'TEXT', mode: 'apl' },
+ { name: 'TEXT', mode: 'text' },
];
export const EXPORT_FILE_MIME_TYPE_SUPPORTED = ['application/json', 'application/x-yaml'];
diff --git a/web/tsconfig.json b/web/tsconfig.json
index d50aa7d..b0f8453 100644
--- a/web/tsconfig.json
+++ b/web/tsconfig.json
@@ -22,5 +22,9 @@
"@@/*": ["./src/.umi/*"]
}
},
+ "include": [
+ "src",
+ "node_modules/monaco-editor/monaco.d.ts"
+ ],
"exclude": ["node_modules", "build", "dist", "scripts", "src/.umi/*", "webpack", "jest"]
}
diff --git a/web/yarn.lock b/web/yarn.lock
index 446e9c2..fcedfed 100644
--- a/web/yarn.lock
+++ b/web/yarn.lock
@@ -1546,13 +1546,6 @@
core-js-pure "^3.0.0"
regenerator-runtime "^0.13.4"
-"@babel/runtime@7.11.2":
- version "7.11.2"
- resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736"
- integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==
- dependencies:
- regenerator-runtime "^0.13.4"
-
"@babel/runtime@7.12.5":
version "7.12.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e"
@@ -2193,6 +2186,22 @@
dependencies:
extend "3.0.2"
+"@monaco-editor/loader@^1.0.1":
+ version "1.0.1"
+ resolved "https://registry.npm.taobao.org/@monaco-editor/loader/download/@monaco-editor/loader-1.0.1.tgz#7068c9b07bbc65387c0e7a4df6dac0a326155905"
+ integrity sha1-cGjJsHu8ZTh8DnpN9trAoyYVWQU=
+ dependencies:
+ state-local "^1.0.6"
+
+"@monaco-editor/react@^4.1.3":
+ version "4.1.3"
+ resolved "https://registry.nlark.com/@monaco-editor/react/download/@monaco-editor/react-4.1.3.tgz#7dcaa584f2a4e8bd8f5298604f0b5368f8ebca55"
+ integrity sha1-fcqlhPKk6L2PUphgTwtTaPjrylU=
+ dependencies:
+ "@monaco-editor/loader" "^1.0.1"
+ prop-types "^15.7.2"
+ state-local "^1.0.7"
+
"@mrmlnc/readdir-enhanced@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
@@ -3202,14 +3211,6 @@
"@typescript-eslint/types" "4.18.0"
eslint-visitor-keys "^2.0.0"
-"@uiw/react-codemirror@^3.0.5":
- version "3.0.5"
- resolved "https://registry.yarnpkg.com/@uiw/react-codemirror/-/react-codemirror-3.0.5.tgz#cda1ad64cb5a82b085575e316ed462db2bf320b4"
- integrity sha512-Fwn55BYknCScDuN6MIzlFxSU3BsHHQDADcr6q46O6NBGRRFyD1UEIdN0cgitHqBCX2v1+b4CajadPChat6WVrQ==
- dependencies:
- "@babel/runtime" "7.11.2"
- codemirror "5.59.0"
-
"@umijs/ast@3.3.11":
version "3.3.11"
resolved "https://registry.yarnpkg.com/@umijs/ast/-/ast-3.3.11.tgz#33a32156eca99de59ad7c11cce1c91cd62d83325"
@@ -5738,11 +5739,6 @@ code-point-at@^1.0.0:
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
-codemirror@5.59.0:
- version "5.59.0"
- resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.59.0.tgz#6d8132055459aabf21d04cae5cf5c430e5c57bb9"
- integrity sha512-UGzSkCacY9z0rSpQ3wnTWRN2nvRE6foDXnJltWW8pazInR/R+3gXHrao4IFQMv/bSBvFBxt8/HPpkpKAS54x5Q==
-
coffeeify@3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/coffeeify/-/coffeeify-3.0.1.tgz#5e2753000c50bd24c693115f33864248dd11136c"
@@ -11628,6 +11624,11 @@ moment@^2.24.0, moment@^2.25.3, moment@^2.27.0, moment@^2.29.1:
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
+monaco-editor@^0.24.0:
+ version "0.24.0"
+ resolved "https://registry.nlark.com/monaco-editor/download/monaco-editor-0.24.0.tgz?cache=0&sync_timestamp=1620845835330&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fmonaco-editor%2Fdownload%2Fmonaco-editor-0.24.0.tgz#990b55096bcc95d08d8d28e55264c6eb17707269"
+ integrity sha1-mQtVCWvMldCNjSjlUmTG6xdwcmk=
+
moo@^0.5.0:
version "0.5.1"
resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4"
@@ -15802,6 +15803,11 @@ start-server-and-test@^1.11.5:
ps-tree "1.2.0"
wait-on "5.3.0"
+state-local@^1.0.6, state-local@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.npm.taobao.org/state-local/download/state-local-1.0.7.tgz#da50211d07f05748d53009bee46307a37db386d5"
+ integrity sha1-2lAhHQfwV0jVMAm+5GMHo32zhtU=
+
state-toggle@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.3.tgz#e123b16a88e143139b09c6852221bc9815917dfe"