Преглед на файлове

[editor] Split the execute button and limit input in two components

Johan Ahlen преди 4 години
родител
ревизия
f8c3ba3266

+ 1 - 104
desktop/core/src/desktop/js/apps/editor/components/ExecutableActions.test.ts

@@ -14,115 +14,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import huePubSub from 'utils/huePubSub';
-import { nextTick } from 'vue';
-import { mount, shallowMount } from '@vue/test-utils';
-import { EXECUTABLE_UPDATED_EVENT, ExecutionStatus } from 'apps/editor/execution/executable';
-import sessionManager from 'apps/editor/execution/sessionManager';
+import { shallowMount } from '@vue/test-utils';
 import ExecutableActions from './ExecutableActions.vue';
 
-/* eslint-disable @typescript-eslint/no-empty-function */
-
 describe('ExecutableActions.vue', () => {
   it('should render', () => {
     const wrapper = shallowMount(ExecutableActions);
     expect(wrapper.element).toMatchSnapshot();
   });
-
-  it('should show execute once the session is loaded', async () => {
-    const spy = spyOn(sessionManager, 'getSession').and.returnValue(
-      Promise.resolve({ type: 'foo' })
-    );
-
-    const mockExecutable = {
-      cancel: () => {},
-      cancelBatchChain: () => {},
-      execute: () => {},
-      isPartOfRunningExecution: () => false,
-      isReady: () => true,
-      reset: () => {},
-      nextExecutable: {},
-      executor: {
-        defaultLimit: () => {},
-        connector: () => ({
-          id: 'foo'
-        })
-      },
-      status: ExecutionStatus.ready
-    };
-
-    const wrapper = shallowMount(ExecutableActions, {
-      propsData: {
-        executable: mockExecutable
-      }
-    });
-
-    await nextTick();
-
-    expect(spy).toHaveBeenCalled();
-    expect(wrapper.element).toMatchSnapshot();
-  });
-
-  it('should handle execute and stop clicks', async () => {
-    const spy = spyOn(sessionManager, 'getSession').and.returnValue(
-      Promise.resolve({ type: 'foo' })
-    );
-    let executeCalled = false;
-    let cancelCalled = false;
-    const mockExecutable = {
-      cancel: () => {
-        cancelCalled = true;
-      },
-      cancelBatchChain: () => {
-        cancelCalled = true;
-      },
-      execute: async () => {
-        executeCalled = true;
-      },
-      isPartOfRunningExecution: () => false,
-      isReady: () => true,
-      reset: () => {},
-      nextExecutable: {},
-      executor: {
-        defaultLimit: () => {},
-        connector: () => ({
-          id: 'foo',
-          type: 'foo'
-        })
-      },
-      status: ExecutionStatus.ready
-    };
-
-    const wrapper = mount(ExecutableActions, {
-      propsData: {
-        executable: mockExecutable
-      }
-    });
-
-    await nextTick();
-
-    expect(spy).toHaveBeenCalled();
-
-    // Click play
-    expect(executeCalled).toBeFalsy();
-    expect(wrapper.get('button').text()).toContain('Execute');
-    wrapper.get('button').trigger('click');
-
-    await nextTick();
-
-    expect(executeCalled).toBeTruthy();
-    mockExecutable.status = ExecutionStatus.running;
-    huePubSub.publish(EXECUTABLE_UPDATED_EVENT, mockExecutable);
-
-    await nextTick();
-
-    // Click stop
-    expect(cancelCalled).toBeFalsy();
-    expect(wrapper.get('button').text()).toContain('Stop');
-    wrapper.get('button').trigger('click');
-
-    await nextTick();
-
-    expect(cancelCalled).toBeTruthy();
-  });
 });

+ 9 - 195
desktop/core/src/desktop/js/apps/editor/components/ExecutableActions.vue

@@ -18,226 +18,40 @@
 
 <template>
   <div class="snippet-execute-actions">
-    <hue-button
-      v-if="loadingSession"
-      key="loading-button"
-      :small="true"
-      :disabled="disabled"
-      :title="I18n('Creating session')"
-    >
-      <i class="fa fa-fw fa-spinner fa-spin" /> {{ I18n('Loading') }}
-    </hue-button>
-
-    <hue-button
-      v-if="showExecute"
-      key="execute-button"
-      :small="true"
-      :primary="true"
-      :disabled="disabled"
-      @click="execute"
-    >
-      <i class="fa fa-play fa-fw" /> {{ I18n('Execute') }}
-    </hue-button>
-
-    <hue-button
-      v-if="showStop && !stopping"
-      key="stop-button"
-      :small="true"
-      :alert="true"
-      @click="stop"
-    >
-      <i class="fa fa-stop fa-fw" />
-      <span v-if="waiting">{{ I18n('Stop batch') }}</span>
-      <span v-else>{{ I18n('Stop') }}</span>
-    </hue-button>
-
-    <hue-button v-if="showStop && stopping" key="stopping-button" :small="true" :alert="true">
-      <i class="fa fa-fw fa-spinner fa-spin" /> {{ I18n('Stopping') }}
-    </hue-button>
-
-    <form autocomplete="off" class="inline-block margin-left-10">
-      <input
-        v-model="limit"
-        class="input-small limit-input"
-        type="number"
-        autocorrect="off"
-        autocomplete="do-not-autocomplete"
-        autocapitalize="off"
-        spellcheck="false"
-        :placeholder="I18n('Limit')"
-        @change="$emit('limit-changed', limit)"
-      />
-    </form>
+    <ExecuteButton :executable="executable" :before-execute="beforeExecute" />
+    <ExecuteLimitInput :executable="executable" @limit-changed="$emit('limit-changed', $event)" />
   </div>
 </template>
 
 <script lang="ts">
+  import ExecuteButton from 'apps/editor/components/ExecuteButton.vue';
+  import ExecuteLimitInput from 'apps/editor/components/ExecuteLimitInput.vue';
   import { defineComponent, PropType } from 'vue';
 
-  import Executable from 'apps/editor/execution/executable';
   import SqlExecutable from 'apps/editor/execution/sqlExecutable';
-  import HueButton from 'components/HueButton.vue';
-  import SubscriptionTracker from 'components/utils/SubscriptionTracker';
-  import huePubSub from 'utils/huePubSub';
-  import I18n from 'utils/i18n';
-
-  import { Session } from 'apps/editor/execution/api';
-  import { EXECUTABLE_UPDATED_EVENT, ExecutionStatus } from 'apps/editor/execution/executable';
-  import sessionManager from 'apps/editor/execution/sessionManager';
-
-  export const EXECUTE_ACTIVE_EXECUTABLE_EVENT = 'executable.active.executable';
-  const WHITE_SPACE_REGEX = /^\s*$/;
 
   export default defineComponent({
     name: 'ExecutableActions',
     components: {
-      HueButton
+      ExecuteLimitInput,
+      ExecuteButton
     },
-
     props: {
       executable: {
-        type: Object as PropType<SqlExecutable>,
+        type: Object as PropType<SqlExecutable | undefined>,
         default: undefined
       },
       beforeExecute: {
-        type: Object as PropType<(executable: Executable) => Promise<void>>,
+        type: Function as PropType<((executable: SqlExecutable) => Promise<void>) | undefined>,
         default: undefined
       }
     },
-
-    emits: ['limit-changed'],
-
-    setup() {
-      const subTracker = new SubscriptionTracker();
-      return { subTracker };
-    },
-
-    data() {
-      return {
-        loadingSession: true,
-        lastSession: null as Session | null,
-        partOfRunningExecution: false,
-        limit: null as number | null,
-        stopping: false,
-        status: ExecutionStatus.ready,
-        hasStatement: false
-      };
-    },
-
-    computed: {
-      waiting(): boolean {
-        return !!(this.executable && this.executable.isReady() && this.partOfRunningExecution);
-      },
-
-      disabled(): boolean {
-        return this.loadingSession || !this.executable || !this.hasStatement;
-      },
-
-      showExecute(): boolean {
-        return (
-          !!this.executable &&
-          !this.waiting &&
-          !this.loadingSession &&
-          this.status !== ExecutionStatus.running &&
-          this.status !== ExecutionStatus.streaming
-        );
-      },
-
-      showStop(): boolean {
-        return (
-          this.status === ExecutionStatus.running ||
-          this.status === ExecutionStatus.streaming ||
-          this.waiting
-        );
-      }
-    },
-
-    watch: {
-      executable: {
-        handler(): void {
-          if (this.executable) {
-            this.updateFromExecutable(this.executable);
-          }
-        },
-        immediate: true
-      }
-    },
-
-    mounted(): void {
-      this.subTracker.subscribe(EXECUTABLE_UPDATED_EVENT, executable => {
-        if (this.executable != undefined && this.executable.id === executable.id) {
-          this.updateFromExecutable(executable);
-        }
-      });
-
-      this.subTracker.subscribe(EXECUTE_ACTIVE_EXECUTABLE_EVENT, executable => {
-        if (this.executable != undefined && this.executable === executable) {
-          this.execute();
-        }
-      });
-    },
-
-    unmounted(): void {
-      this.subTracker.dispose();
-    },
-
-    methods: {
-      I18n,
-
-      async execute(): Promise<void> {
-        huePubSub.publish('hue.ace.autocompleter.hide');
-        if (!this.executable) {
-          return;
-        }
-        if (this.beforeExecute) {
-          await this.beforeExecute(this.executable);
-        }
-        await this.executable.reset();
-        this.executable.execute();
-      },
-
-      async stop(): Promise<void> {
-        if (this.stopping || !this.executable) {
-          return;
-        }
-        this.stopping = true;
-        await this.executable.cancelBatchChain(true);
-        this.stopping = false;
-      },
-
-      updateFromExecutable(executable: SqlExecutable): void {
-        const waitForSession =
-          !this.lastSession || this.lastSession.type !== executable.executor.connector().type;
-        this.status = executable.status;
-        this.hasStatement =
-          !executable.parsedStatement ||
-          !WHITE_SPACE_REGEX.test(executable.parsedStatement.statement);
-        this.partOfRunningExecution = executable.isPartOfRunningExecution();
-        this.limit =
-          (executable.executor.defaultLimit && executable.executor.defaultLimit()) || null;
-        if (waitForSession) {
-          this.loadingSession = true;
-          this.lastSession = null;
-          sessionManager.getSession({ type: executable.executor.connector().id }).then(session => {
-            this.lastSession = session;
-            this.loadingSession = false;
-          });
-        }
-      }
-    }
+    emits: ['limit-changed']
   });
 </script>
 
 <style lang="scss" scoped>
   .snippet-execute-actions {
     display: inline-block;
-
-    .limit-input {
-      border-radius: 2px;
-      height: 13px;
-      width: 50px;
-      margin: 0 5px;
-      padding: 5px 6px;
-    }
   }
 </style>

+ 128 - 0
desktop/core/src/desktop/js/apps/editor/components/ExecuteButton.test.ts

@@ -0,0 +1,128 @@
+// 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 SqlExecutable from 'apps/editor/execution/sqlExecutable';
+import huePubSub from 'utils/huePubSub';
+import { nextTick } from 'vue';
+import { mount, shallowMount } from '@vue/test-utils';
+import { EXECUTABLE_UPDATED_EVENT, ExecutionStatus } from 'apps/editor/execution/executable';
+import sessionManager from 'apps/editor/execution/sessionManager';
+import ExecuteButton from './ExecuteButton.vue';
+import { noop } from 'utils/hueUtils';
+
+describe('ExecuteButton.vue', () => {
+  it('should render', () => {
+    const wrapper = shallowMount(ExecuteButton);
+    expect(wrapper.element).toMatchSnapshot();
+  });
+
+  it('should show execute once the session is loaded', async () => {
+    const spy = spyOn(sessionManager, 'getSession').and.returnValue(
+      Promise.resolve({ type: 'foo' })
+    );
+
+    const mockExecutable = {
+      cancel: noop,
+      cancelBatchChain: noop,
+      execute: noop,
+      isPartOfRunningExecution: () => false,
+      isReady: () => true,
+      reset: noop,
+      nextExecutable: {},
+      executor: {
+        defaultLimit: noop,
+        connector: () => ({
+          id: 'foo'
+        })
+      },
+      status: ExecutionStatus.ready
+    } as SqlExecutable;
+
+    const wrapper = shallowMount(ExecuteButton, {
+      props: {
+        executable: mockExecutable
+      }
+    });
+
+    await nextTick();
+
+    expect(spy).toHaveBeenCalled();
+    expect(wrapper.element).toMatchSnapshot();
+  });
+
+  it('should handle execute and stop clicks', async () => {
+    const spy = spyOn(sessionManager, 'getSession').and.returnValue(
+      Promise.resolve({ type: 'foo' })
+    );
+    let executeCalled = false;
+    let cancelCalled = false;
+    const mockExecutable = {
+      cancel: () => {
+        cancelCalled = true;
+      },
+      cancelBatchChain: () => {
+        cancelCalled = true;
+      },
+      execute: async () => {
+        executeCalled = true;
+      },
+      isPartOfRunningExecution: () => false,
+      isReady: () => true,
+      reset: noop,
+      nextExecutable: {},
+      executor: {
+        defaultLimit: noop,
+        connector: () => ({
+          id: 'foo',
+          type: 'foo'
+        })
+      },
+      status: ExecutionStatus.ready
+    } as SqlExecutable;
+
+    const wrapper = mount(ExecuteButton, {
+      props: {
+        executable: mockExecutable
+      }
+    });
+
+    await nextTick();
+
+    expect(spy).toHaveBeenCalled();
+
+    // Click play
+    expect(executeCalled).toBeFalsy();
+    expect(wrapper.get('button').text()).toContain('Execute');
+    wrapper.get('button').trigger('click');
+
+    await nextTick();
+
+    expect(executeCalled).toBeTruthy();
+    mockExecutable.status = ExecutionStatus.running;
+    huePubSub.publish(EXECUTABLE_UPDATED_EVENT, mockExecutable);
+
+    await nextTick();
+
+    // Click stop
+    expect(cancelCalled).toBeFalsy();
+    expect(wrapper.get('button').text()).toContain('Stop');
+    wrapper.get('button').trigger('click');
+
+    await nextTick();
+
+    expect(cancelCalled).toBeTruthy();
+  });
+});

+ 209 - 0
desktop/core/src/desktop/js/apps/editor/components/ExecuteButton.vue

@@ -0,0 +1,209 @@
+<!--
+  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.
+-->
+
+<template>
+  <hue-button
+    v-if="loadingSession"
+    key="loading-button"
+    :small="true"
+    :disabled="disabled"
+    :title="I18n('Creating session')"
+  >
+    <i class="fa fa-fw fa-spinner fa-spin" /> {{ I18n('Loading') }}
+  </hue-button>
+  <hue-button
+    v-if="showExecute"
+    key="execute-button"
+    :small="true"
+    :primary="true"
+    :disabled="disabled"
+    @click="execute"
+  >
+    <i class="fa fa-play fa-fw" /> {{ I18n('Execute') }}
+  </hue-button>
+
+  <hue-button
+    v-if="showStop && !stopping"
+    key="stop-button"
+    :small="true"
+    :alert="true"
+    @click="stop"
+  >
+    <i class="fa fa-stop fa-fw" />
+    <span v-if="waiting">{{ I18n('Stop batch') }}</span>
+    <span v-else>{{ I18n('Stop') }}</span>
+  </hue-button>
+
+  <hue-button v-if="showStop && stopping" key="stopping-button" :small="true" :alert="true">
+    <i class="fa fa-fw fa-spinner fa-spin" /> {{ I18n('Stopping') }}
+  </hue-button>
+</template>
+
+<script lang="ts">
+  import { defineComponent, PropType, ref, toRefs, watch } from 'vue';
+
+  import SqlExecutable from 'apps/editor/execution/sqlExecutable';
+  import HueButton from 'components/HueButton.vue';
+  import SubscriptionTracker from 'components/utils/SubscriptionTracker';
+  import huePubSub from 'utils/huePubSub';
+  import I18n from 'utils/i18n';
+
+  import { Session } from 'apps/editor/execution/api';
+  import { EXECUTABLE_UPDATED_EVENT, ExecutionStatus } from 'apps/editor/execution/executable';
+  import sessionManager from 'apps/editor/execution/sessionManager';
+
+  const EXECUTE_ACTIVE_EXECUTABLE_EVENT = 'executable.active.executable';
+  const WHITE_SPACE_REGEX = /^\s*$/;
+
+  export default defineComponent({
+    name: 'ExecuteButton',
+    components: {
+      HueButton
+    },
+    props: {
+      executable: {
+        type: Object as PropType<SqlExecutable | undefined>,
+        default: undefined
+      },
+      beforeExecute: {
+        type: Function as PropType<((executable: SqlExecutable) => Promise<void>) | undefined>,
+        default: undefined
+      }
+    },
+    setup(props) {
+      const { executable, beforeExecute } = toRefs(props);
+      const subTracker = new SubscriptionTracker();
+
+      let lastSession = null as Session | null;
+
+      const stopping = ref(false);
+      const loadingSession = ref(true);
+      const partOfRunningExecution = ref(false);
+      const status = ref<ExecutionStatus>(ExecutionStatus.ready);
+      const hasStatement = ref(false);
+
+      const execute = async (): Promise<void> => {
+        huePubSub.publish('hue.ace.autocompleter.hide');
+        if (!executable.value) {
+          return;
+        }
+        if (beforeExecute.value) {
+          await beforeExecute.value(executable.value as SqlExecutable);
+        }
+        await executable.value.reset();
+        executable.value.execute();
+      };
+
+      const updateFromExecutable = (executable: SqlExecutable): void => {
+        const waitForSession =
+          !lastSession || lastSession.type !== executable.executor.connector().type;
+        status.value = executable.status;
+        hasStatement.value =
+          !executable.parsedStatement ||
+          !WHITE_SPACE_REGEX.test(executable.parsedStatement.statement);
+        partOfRunningExecution.value = executable.isPartOfRunningExecution();
+        if (waitForSession) {
+          loadingSession.value = true;
+          lastSession = null;
+          sessionManager.getSession({ type: executable.executor.connector().id }).then(session => {
+            lastSession = session;
+            loadingSession.value = false;
+          });
+        }
+      };
+
+      watch(
+        executable,
+        newVal => {
+          if (newVal) {
+            updateFromExecutable(newVal as SqlExecutable);
+          }
+        },
+        { immediate: true }
+      );
+
+      subTracker.subscribe(EXECUTABLE_UPDATED_EVENT, updatedExecutable => {
+        if (executable.value && executable.value.id === updatedExecutable.id) {
+          updateFromExecutable(updatedExecutable);
+        }
+      });
+
+      subTracker.subscribe(EXECUTE_ACTIVE_EXECUTABLE_EVENT, eventExecutable => {
+        if (executable.value && executable.value === eventExecutable) {
+          execute();
+        }
+      });
+
+      return {
+        subTracker,
+        stopping,
+        loadingSession,
+        partOfRunningExecution,
+        status,
+        hasStatement,
+        I18n
+      };
+    },
+    computed: {
+      waiting(): boolean {
+        return !!(this.executable && this.executable.isReady() && this.partOfRunningExecution);
+      },
+      disabled(): boolean {
+        return this.loadingSession || !this.executable || !this.hasStatement;
+      },
+      showExecute(): boolean {
+        return (
+          !!this.executable &&
+          !this.waiting &&
+          !this.loadingSession &&
+          this.status !== ExecutionStatus.running &&
+          this.status !== ExecutionStatus.streaming
+        );
+      },
+      showStop(): boolean {
+        return (
+          this.status === ExecutionStatus.running ||
+          this.status === ExecutionStatus.streaming ||
+          this.waiting
+        );
+      }
+    },
+    methods: {
+      async execute(): Promise<void> {
+        huePubSub.publish('hue.ace.autocompleter.hide');
+        if (!this.executable) {
+          return;
+        }
+        if (this.beforeExecute) {
+          await this.beforeExecute(this.executable);
+        }
+        await this.executable.reset();
+        this.executable.execute();
+      },
+
+      async stop(): Promise<void> {
+        if (this.stopping || !this.executable) {
+          return;
+        }
+        this.stopping = true;
+        await this.executable.cancelBatchChain(true);
+        this.stopping = false;
+      }
+    }
+  });
+</script>

+ 100 - 0
desktop/core/src/desktop/js/apps/editor/components/ExecuteLimitInput.vue

@@ -0,0 +1,100 @@
+<!--
+  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.
+-->
+
+<template>
+  <form autocomplete="off" class="inline-block margin-left-10">
+    <input
+      v-model="limit"
+      class="input-small limit-input"
+      type="number"
+      autocorrect="off"
+      autocomplete="do-not-autocomplete"
+      autocapitalize="off"
+      spellcheck="false"
+      :placeholder="I18n('Limit')"
+      @change="$emit('limit-changed', limit)"
+    />
+  </form>
+</template>
+
+<script lang="ts">
+  import { defineComponent, PropType, ref, toRefs, watch } from 'vue';
+
+  import SqlExecutable from 'apps/editor/execution/sqlExecutable';
+  import SubscriptionTracker from 'components/utils/SubscriptionTracker';
+  import I18n from 'utils/i18n';
+
+  import { EXECUTABLE_UPDATED_EVENT } from 'apps/editor/execution/executable';
+
+  export default defineComponent({
+    name: 'ExecuteLimitInput',
+    props: {
+      executable: {
+        type: Object as PropType<SqlExecutable>,
+        default: undefined
+      }
+    },
+    emits: ['limit-changed'],
+    setup(props) {
+      const { executable } = toRefs(props);
+      const limit = ref<number | null>(null);
+      const subTracker = new SubscriptionTracker();
+
+      const updateFromExecutable = (updatedExecutable: SqlExecutable): void => {
+        limit.value =
+          (updatedExecutable.executor.defaultLimit && updatedExecutable.executor.defaultLimit()) ||
+          null;
+      };
+
+      subTracker.subscribe(EXECUTABLE_UPDATED_EVENT, updatedExecutable => {
+        if (executable.value && executable.value.id === updatedExecutable.id) {
+          updateFromExecutable(updatedExecutable);
+        }
+      });
+
+      watch(
+        executable,
+        newVal => {
+          if (newVal) {
+            updateFromExecutable(newVal as SqlExecutable);
+          }
+        },
+        { immediate: true }
+      );
+
+      return { limit, I18n };
+    }
+  });
+</script>
+
+<style lang="scss" scoped>
+  input.limit-input {
+    -moz-appearance: textfield;
+    border-radius: 2px;
+    height: 13px;
+    width: 50px;
+    margin: 0 5px;
+    padding: 5px 6px;
+
+    &::-webkit-outer-spin-button,
+    &::-webkit-inner-spin-button {
+      -webkit-appearance: none;
+      margin: 0;
+    }
+  }
+</style>

+ 4 - 2
desktop/core/src/desktop/js/apps/editor/components/QueryEditorWebComponents.ts

@@ -17,7 +17,8 @@
 import axios from 'axios';
 
 import AceEditor from './aceEditor/AceEditor.vue';
-import ExecutableActions from './ExecutableActions.vue';
+import ExecuteButton from 'apps/editor/components/ExecuteButton.vue';
+import ExecuteLimitInput from 'apps/editor/components/ExecuteLimitInput.vue';
 import ExecutableProgressBar from './ExecutableProgressBar.vue';
 import ResultTable from './result/ResultTable.vue';
 import Executor, { ExecutorOptions } from '../execution/executor';
@@ -25,7 +26,8 @@ import 'utils/json.bigDataParse';
 import { wrap } from 'vue/webComponentWrap';
 
 wrap('query-editor', AceEditor);
-wrap('query-editor-actions', ExecutableActions);
+wrap('query-editor-execute-button', ExecuteButton);
+wrap('query-editor-limit-input', ExecuteLimitInput);
 wrap('query-editor-progress-bar', ExecutableProgressBar);
 wrap('query-editor-result-table', ResultTable);
 

+ 2 - 51
desktop/core/src/desktop/js/apps/editor/components/__snapshots__/ExecutableActions.test.ts.snap

@@ -4,56 +4,7 @@ exports[`ExecutableActions.vue should render 1`] = `
 <div
   class="snippet-execute-actions"
 >
-  <hue-button-stub
-    disabled="true"
-    small="true"
-    title="Creating session"
-  />
-  <!--v-if-->
-  <!--v-if-->
-  <!--v-if-->
-  <form
-    autocomplete="off"
-    class="inline-block margin-left-10"
-  >
-    <input
-      autocapitalize="off"
-      autocomplete="do-not-autocomplete"
-      autocorrect="off"
-      class="input-small limit-input"
-      placeholder="Limit"
-      spellcheck="false"
-      type="number"
-    />
-  </form>
-</div>
-`;
-
-exports[`ExecutableActions.vue should show execute once the session is loaded 1`] = `
-<div
-  class="snippet-execute-actions"
->
-  <!--v-if-->
-  <hue-button-stub
-    disabled="false"
-    primary="true"
-    small="true"
-  />
-  <!--v-if-->
-  <!--v-if-->
-  <form
-    autocomplete="off"
-    class="inline-block margin-left-10"
-  >
-    <input
-      autocapitalize="off"
-      autocomplete="do-not-autocomplete"
-      autocorrect="off"
-      class="input-small limit-input"
-      placeholder="Limit"
-      spellcheck="false"
-      type="number"
-    />
-  </form>
+  <execute-button-stub />
+  <execute-limit-input-stub />
 </div>
 `;

+ 35 - 0
desktop/core/src/desktop/js/apps/editor/components/__snapshots__/ExecuteButton.test.ts.snap

@@ -0,0 +1,35 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ExecuteButton.vue should render 1`] = `
+<div
+  data-v-app=""
+>
+  
+  <hue-button-stub
+    disabled="true"
+    small="true"
+    title="Creating session"
+  />
+  <!--v-if-->
+  <!--v-if-->
+  <!--v-if-->
+  
+</div>
+`;
+
+exports[`ExecuteButton.vue should show execute once the session is loaded 1`] = `
+<div
+  data-v-app=""
+>
+  
+  <!--v-if-->
+  <hue-button-stub
+    disabled="false"
+    primary="true"
+    small="true"
+  />
+  <!--v-if-->
+  <!--v-if-->
+  
+</div>
+`;

+ 0 - 1
desktop/core/src/desktop/js/apps/editor/components/aceEditor/AceEditor.scss

@@ -14,7 +14,6 @@
   .hue-ace-syntax-error {
     position: absolute;
     border-bottom: 1px dotted $hue-error-color;
-    border-bottom: 1px dotted $hue-error-color;
     border-radius: 0 !important;
   }
 

+ 2 - 1
desktop/core/src/desktop/js/apps/editor/snippet.js

@@ -52,7 +52,6 @@ import {
   ACTIVE_STATEMENT_CHANGED_EVENT,
   REFRESH_STATEMENT_LOCATIONS_EVENT
 } from 'ko/bindings/ace/aceLocationHandler';
-import { EXECUTE_ACTIVE_EXECUTABLE_EVENT } from './components/ExecutableActions.vue';
 import { ADD_TO_HISTORY_EVENT } from 'apps/editor/components/ko.queryHistory';
 import { findEditorConnector, getLastKnownConfig } from 'config/hueConfig';
 import { cancelActiveRequest } from 'api/apiUtils';
@@ -195,6 +194,8 @@ const getDefaultSnippetProperties = snippetType => {
   return properties;
 };
 
+const EXECUTE_ACTIVE_EXECUTABLE_EVENT = 'executable.active.executable';
+
 const ERROR_REGEX = /line ([0-9]+)(:([0-9]+))?/i;
 
 export default class Snippet {