From 08d2e440a9f2fe8f56f6020ac0f6042ff3a996f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20=C3=85hl=C3=A9n?= Date: Tue, 19 Mar 2024 16:26:53 +0100 Subject: [PATCH] CDPD-68772: [editor] Move the SQL Syntax Dropdown from mako to js to get rid of inline script (#3653) No code changes other than linting and some small clean-up. (cherry picked from commit b6b2eccc9070556764e85b8a3b5f36d49ec20418) (cherry picked from commit 273e39e0c106e74828691887209dce1c1d6b983b) Change-Id: Ie0c07a005df31ab5ee015deb3e7a734c753435c7 (cherry picked from commit 6372faf1d50990464f42cf0c47fc78edb3064ea6) --- .../ko.syntaxDropdown.test.js.snap | 51 ++++++ .../editor/components/ko.syntaxDropdown.js | 147 ++++++++++++++++++ .../components/ko.syntaxDropdown.test.js | 54 +++++++ .../core/src/desktop/js/apps/notebook/app.js | 1 + .../templates/sql_syntax_dropdown.mako | 132 ---------------- .../notebook/templates/editor_components.mako | 3 - 6 files changed, 253 insertions(+), 135 deletions(-) create mode 100644 desktop/core/src/desktop/js/apps/editor/components/__snapshots__/ko.syntaxDropdown.test.js.snap create mode 100644 desktop/core/src/desktop/js/apps/editor/components/ko.syntaxDropdown.js create mode 100644 desktop/core/src/desktop/js/apps/editor/components/ko.syntaxDropdown.test.js delete mode 100644 desktop/core/src/desktop/templates/sql_syntax_dropdown.mako diff --git a/desktop/core/src/desktop/js/apps/editor/components/__snapshots__/ko.syntaxDropdown.test.js.snap b/desktop/core/src/desktop/js/apps/editor/components/__snapshots__/ko.syntaxDropdown.test.js.snap new file mode 100644 index 00000000000..dd7db50bfcc --- /dev/null +++ b/desktop/core/src/desktop/js/apps/editor/components/__snapshots__/ko.syntaxDropdown.test.js.snap @@ -0,0 +1,51 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ko.syntaxDropdown.js should match snapshot 1`] = ` +"
+
+ + +
+
0\\"> + + + + +
+
+
+
" +`; + +exports[`ko.syntaxDropdown.js should render component on show event 1`] = `"
"`; diff --git a/desktop/core/src/desktop/js/apps/editor/components/ko.syntaxDropdown.js b/desktop/core/src/desktop/js/apps/editor/components/ko.syntaxDropdown.js new file mode 100644 index 00000000000..46e5add83af --- /dev/null +++ b/desktop/core/src/desktop/js/apps/editor/components/ko.syntaxDropdown.js @@ -0,0 +1,147 @@ +// Licensed to Cloudera, Inc. under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. Cloudera, Inc. licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import * as ko from 'knockout'; +import $ from 'jquery'; + +import 'ko/components/ko.dropDown'; +import componentUtils from 'ko/components/componentUtils'; +import DisposableComponent from 'ko/components/DisposableComponent'; +import huePubSub from 'utils/huePubSub'; +import I18n from 'utils/i18n'; +import { hueLocalStorage } from 'utils/storageUtils'; + +export const SYNTAX_DROPDOWN_COMPONENT = 'sql-syntax-dropdown'; +export const SYNTAX_DROPDOWN_ID = 'sqlSyntaxDropdown'; +export const SHOW_EVENT = 'sql.syntax.dropdown.show'; +export const SHOWN_EVENT = 'sql.syntax.dropdown.shown'; +export const HIDE_EVENT = 'sql.syntax.dropdown.hide'; + +// prettier-ignore +const TEMPLATE = ` +
+`; + +const hideSyntaxDropdown = () => { + const $sqlSyntaxDropdown = $(`#${SYNTAX_DROPDOWN_ID}`); + if ($sqlSyntaxDropdown.length) { + ko.cleanNode($sqlSyntaxDropdown[0]); + $sqlSyntaxDropdown.remove(); + $(document).off('click', hideOnClickOutside); + } +}; + +const hideOnClickOutside = event => { + const $modal = $('.modal'); + if ( + $.contains(document, event.target) && + !$.contains($(`#${SYNTAX_DROPDOWN_ID}`)[0], event.target) && + ($modal[0].length === 0 || !$.contains($modal[0], event.target)) + ) { + hideSyntaxDropdown(); + } +}; + +class SqlSyntaxDropdownViewModel extends DisposableComponent { + constructor(params) { + super(); + + this.selected = ko.observable(); + + const expected = params.data.expected.map(expected => expected.text); + // TODO: Allow suppression of unknown columns etc. + if (params.data.ruleId) { + if (expected.length > 0) { + expected.push({ + divider: true + }); + } + expected.push({ + label: I18n('Ignore this type of error'), + suppressRule: params.data.ruleId.toString() + params.data.text.toLowerCase() + }); + } + this.expected = ko.observableArray(expected); + + const selectedSub = this.selected.subscribe(newValue => { + if (typeof newValue.suppressRule !== 'undefined') { + const suppressedRules = hueLocalStorage('hue.syntax.checker.suppressedRules') || {}; + suppressedRules[newValue.suppressRule] = true; + hueLocalStorage('hue.syntax.checker.suppressedRules', suppressedRules); + huePubSub.publish('editor.refresh.statement.locations', params.editorId); + } else { + params.editor.session.replace(params.range, newValue); + } + hideSyntaxDropdown(); + }); + + this.addDisposalCallback(() => { + selectedSub.dispose(); + }); + + this.left = ko.observable(params.source.left); + this.top = ko.observable(params.source.bottom); + + const closeOnEsc = e => { + if (e.keyCode === 27) { + hideSyntaxDropdown(); + } + }; + + $(document).on('keyup', closeOnEsc); + + this.addDisposalCallback(() => { + $(document).off('keyup', closeOnEsc); + }); + + window.setTimeout(() => { + $(document).on('click', hideOnClickOutside); + }, 0); + + this.addDisposalCallback(() => { + $(document).off('click', hideOnClickOutside); + }); + } +} + +componentUtils.registerComponent(SYNTAX_DROPDOWN_COMPONENT, SqlSyntaxDropdownViewModel, TEMPLATE); + +huePubSub.subscribe(HIDE_EVENT, hideSyntaxDropdown); +huePubSub.subscribe(SHOW_EVENT, details => { + hideSyntaxDropdown(); + const $sqlSyntaxDropdown = $( + `
` + ); + $('body').append($sqlSyntaxDropdown); + ko.applyBindings(details, $sqlSyntaxDropdown[0]); + huePubSub.publish(SHOWN_EVENT); +}); diff --git a/desktop/core/src/desktop/js/apps/editor/components/ko.syntaxDropdown.test.js b/desktop/core/src/desktop/js/apps/editor/components/ko.syntaxDropdown.test.js new file mode 100644 index 00000000000..efe6ba1e309 --- /dev/null +++ b/desktop/core/src/desktop/js/apps/editor/components/ko.syntaxDropdown.test.js @@ -0,0 +1,54 @@ +// Licensed to Cloudera, Inc. under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. Cloudera, Inc. licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { SHOW_EVENT, SHOWN_EVENT, SYNTAX_DROPDOWN_ID } from './ko.syntaxDropdown'; +import huePubSub from 'utils/huePubSub'; + +describe('ko.syntaxDropdown.js', () => { + const publishShowEvent = () => { + huePubSub.publish(SHOW_EVENT, { + data: { + text: 'floo', + expected: [{ text: 'foo' }], + ruleId: 123 + }, + source: { + left: 10, + bottom: 20 + } + }); + }; + + it('should render component on show event', async () => { + expect(document.querySelectorAll(`#${SYNTAX_DROPDOWN_ID}`)).toHaveLength(0); + + publishShowEvent(); + + expect(document.querySelectorAll(`#${SYNTAX_DROPDOWN_ID}`)).toHaveLength(1); + expect(window.document.documentElement.outerHTML).toMatchSnapshot(); + }); + + it('should match snapshot', async () => { + const shownPromise = new Promise(resolve => { + huePubSub.subscribeOnce(SHOWN_EVENT, resolve); + }); + publishShowEvent(); + + await shownPromise; + + expect(window.document.documentElement.outerHTML).toMatchSnapshot(); + }); +}); diff --git a/desktop/core/src/desktop/js/apps/notebook/app.js b/desktop/core/src/desktop/js/apps/notebook/app.js index 78d7377ca88..ce3544c279b 100644 --- a/desktop/core/src/desktop/js/apps/notebook/app.js +++ b/desktop/core/src/desktop/js/apps/notebook/app.js @@ -23,6 +23,7 @@ import 'ext/jquery.hotkeys'; import 'jquery/plugins/jquery.hdfstree'; import NotebookViewModel from './NotebookViewModel'; +import '../editor/components/ko.syntaxDropdown'; import { ACTIVE_SNIPPET_CONNECTOR_CHANGED_EVENT, IGNORE_NEXT_UNLOAD_EVENT diff --git a/desktop/core/src/desktop/templates/sql_syntax_dropdown.mako b/desktop/core/src/desktop/templates/sql_syntax_dropdown.mako deleted file mode 100644 index 2f37f7958d6..00000000000 --- a/desktop/core/src/desktop/templates/sql_syntax_dropdown.mako +++ /dev/null @@ -1,132 +0,0 @@ -## Licensed to Cloudera, Inc. under one -## or more contributor license agreements. See the NOTICE file -## distributed with this work for additional information -## regarding copyright ownership. Cloudera, Inc. licenses this file -## to you under the Apache License, Version 2.0 (the -## "License"); you may not use this file except in compliance -## with the License. You may obtain a copy of the License at -## -## http://www.apache.org/licenses/LICENSE-2.0 -## -## Unless required by applicable law or agreed to in writing, software -## distributed under the License is distributed on an "AS IS" BASIS, -## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -## See the License for the specific language governing permissions and -## limitations under the License. - -<%! -import sys -from desktop.lib.i18n import smart_unicode -if sys.version_info[0] > 2: - from django.utils.translation import gettext as _ -else: - from django.utils.translation import ugettext as _ -%> - -<%def name="sqlSyntaxDropdown()"> - - - - diff --git a/desktop/libs/notebook/src/notebook/templates/editor_components.mako b/desktop/libs/notebook/src/notebook/templates/editor_components.mako index 9e0c63d57f3..ca17cff1432 100644 --- a/desktop/libs/notebook/src/notebook/templates/editor_components.mako +++ b/desktop/libs/notebook/src/notebook/templates/editor_components.mako @@ -104,7 +104,6 @@ else: <%namespace name="dashboard" file="/common_dashboard.mako" /> -<%namespace name="sqlSyntaxDropdown" file="/sql_syntax_dropdown.mako" /> @@ -131,8 +130,6 @@ else: % endif -${ sqlSyntaxDropdown.sqlSyntaxDropdown() } -