mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 04:34:06 -08:00
feat(editor): Update element-plus
to 2.4.3 (no-changelog) (#10281)
Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
This commit is contained in:
parent
03c19723d2
commit
ecd287564d
|
@ -11,6 +11,21 @@ describe('Inline expression editor', () => {
|
|||
cy.on('uncaught:exception', (error) => error.name !== 'ExpressionError');
|
||||
});
|
||||
|
||||
describe('Basic UI functionality', () => {
|
||||
it('should open and close inline expression preview', () => {
|
||||
WorkflowPage.actions.zoomToFit();
|
||||
WorkflowPage.actions.openNode('Schedule');
|
||||
WorkflowPage.actions.openInlineExpressionEditor();
|
||||
WorkflowPage.getters.inlineExpressionEditorInput().clear();
|
||||
WorkflowPage.getters.inlineExpressionEditorInput().click().type('{{');
|
||||
WorkflowPage.getters.inlineExpressionEditorInput().type('123');
|
||||
WorkflowPage.getters.inlineExpressionEditorOutput().contains(/^123$/);
|
||||
// click outside to close
|
||||
ndv.getters.outputPanel().click();
|
||||
WorkflowPage.getters.inlineExpressionEditorOutput().should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Static data', () => {
|
||||
beforeEach(() => {
|
||||
WorkflowPage.actions.addNodeToCanvas('Hacker News');
|
||||
|
|
|
@ -65,7 +65,7 @@ describe('Workflow tags', () => {
|
|||
it('should detach a tag inline by clicking on dropdown list item', () => {
|
||||
wf.getters.createTagButton().click();
|
||||
wf.actions.addTags(TEST_TAGS);
|
||||
wf.getters.nthTagPill(1).click();
|
||||
wf.getters.workflowTagsContainer().click();
|
||||
wf.getters.tagsInDropdown().filter('.selected').first().click();
|
||||
cy.get('body').click(0, 0);
|
||||
wf.getters.workflowTags().click();
|
||||
|
@ -79,7 +79,7 @@ describe('Workflow tags', () => {
|
|||
wf.actions.addTags(TEST_TAGS);
|
||||
cy.get('body').click(0, 0);
|
||||
wf.getters.workflowTags().click();
|
||||
wf.getters.tagsDropdown().find('input:focus').type(NON_EXISTING_TAG);
|
||||
wf.getters.workflowTagsInput().type(NON_EXISTING_TAG);
|
||||
|
||||
getVisibleSelect()
|
||||
.find('li')
|
||||
|
|
|
@ -112,13 +112,13 @@ describe('Credentials', () => {
|
|||
workflowPage.getters.nodeCredentialsSelect().should('have.length', 2);
|
||||
|
||||
workflowPage.getters.nodeCredentialsSelect().first().click();
|
||||
getVisibleSelect().find('li').last().click();
|
||||
getVisibleSelect().find('li').contains('Create New Credential').click();
|
||||
// This one should show auth type selector
|
||||
credentialsModal.getters.credentialAuthTypeRadioButtons().should('have.length', 2);
|
||||
cy.get('body').type('{esc}');
|
||||
|
||||
workflowPage.getters.nodeCredentialsSelect().last().click();
|
||||
getVisibleSelect().find('li').last().click();
|
||||
getVisibleSelect().find('li').contains('Create New Credential').click();
|
||||
// This one should not show auth type selector
|
||||
credentialsModal.getters.credentialsAuthTypeSelector().should('not.exist');
|
||||
});
|
||||
|
|
|
@ -138,6 +138,8 @@ export class NDV extends BasePage {
|
|||
cy.getByTestId(`fixed-collection-${paramName}`),
|
||||
schemaViewNode: () => cy.getByTestId('run-data-schema-node'),
|
||||
schemaViewNodeName: () => cy.getByTestId('run-data-schema-node-name'),
|
||||
expressionExpanders: () => cy.getByTestId('expander'),
|
||||
expressionModalOutput: () => cy.getByTestId('expression-modal-output'),
|
||||
};
|
||||
|
||||
actions = {
|
||||
|
|
|
@ -3,7 +3,7 @@ export function getPopper() {
|
|||
}
|
||||
|
||||
export function getVisiblePopper() {
|
||||
return getPopper().filter(':visible');
|
||||
return getPopper().filter('[aria-hidden="false"]');
|
||||
}
|
||||
|
||||
export function getVisibleSelect() {
|
||||
|
|
|
@ -8,7 +8,7 @@ import { library } from '@fortawesome/fontawesome-svg-core';
|
|||
import { fas } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import ElementPlus from 'element-plus';
|
||||
import lang from 'element-plus/lib/locale/lang/en';
|
||||
import lang from 'element-plus/dist/locale/en.mjs'
|
||||
|
||||
import { N8nPlugin } from '../src/plugin';
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
"@fortawesome/fontawesome-svg-core": "^1.2.36",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.4",
|
||||
"@fortawesome/vue-fontawesome": "^3.0.3",
|
||||
"element-plus": "^2.3.6",
|
||||
"element-plus": "2.4.3",
|
||||
"markdown-it": "^13.0.2",
|
||||
"markdown-it-emoji": "^2.0.2",
|
||||
"markdown-it-link-attributes": "^4.0.1",
|
||||
|
|
|
@ -10,7 +10,6 @@ exports[`components > N8nCheckbox > should render with both child and label 1`]
|
|||
class="el-checkbox__input"
|
||||
>
|
||||
<input
|
||||
aria-hidden="false"
|
||||
class="el-checkbox__original"
|
||||
type="checkbox"
|
||||
value="Checkbox"
|
||||
|
@ -71,7 +70,6 @@ exports[`components > N8nCheckbox > should render with child 1`] = `
|
|||
class="el-checkbox__input"
|
||||
>
|
||||
<input
|
||||
aria-hidden="false"
|
||||
class="el-checkbox__original"
|
||||
type="checkbox"
|
||||
/>
|
||||
|
@ -106,7 +104,6 @@ exports[`components > N8nCheckbox > should render with label 1`] = `
|
|||
class="el-checkbox__input"
|
||||
>
|
||||
<input
|
||||
aria-hidden="false"
|
||||
class="el-checkbox__original"
|
||||
type="checkbox"
|
||||
value="Checkbox"
|
||||
|
@ -164,7 +161,6 @@ exports[`components > N8nCheckbox > should render without label and child conten
|
|||
class="el-checkbox__input"
|
||||
>
|
||||
<input
|
||||
aria-hidden="false"
|
||||
class="el-checkbox__original"
|
||||
type="checkbox"
|
||||
/>
|
||||
|
|
|
@ -9,6 +9,7 @@ exports[`components > N8nColorPicker > should render with input 1`] = `
|
|||
|
||||
<div
|
||||
aria-description="current color is . press enter to select a new color."
|
||||
aria-disabled="false"
|
||||
aria-label="color picker"
|
||||
class="el-color-picker el-color-picker--large el-tooltip__trigger el-tooltip__trigger"
|
||||
role="button"
|
||||
|
@ -106,6 +107,7 @@ exports[`components > N8nColorPicker > should render without input 1`] = `
|
|||
|
||||
<div
|
||||
aria-description="current color is . press enter to select a new color."
|
||||
aria-disabled="false"
|
||||
aria-label="color picker"
|
||||
class="el-color-picker el-color-picker--large el-tooltip__trigger el-tooltip__trigger"
|
||||
role="button"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { render } from '@testing-library/vue';
|
||||
import N8nDatatable from '../Datatable.vue';
|
||||
import { rows, columns } from './data';
|
||||
import { removeDynamicAttributes } from 'n8n-design-system/utils';
|
||||
|
||||
const stubs = [
|
||||
'n8n-option',
|
||||
|
@ -33,6 +34,7 @@ describe('components', () => {
|
|||
expect(wrapper.container.querySelectorAll('tbody tr td').length).toEqual(
|
||||
columns.length * rowsPerPage,
|
||||
);
|
||||
removeDynamicAttributes(wrapper.container);
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
|
|
|
@ -113,7 +113,6 @@ exports[`components > N8nDatatable > should render correctly 1`] = `
|
|||
<div class="el-select el-select--small" popperappendtobody="false" limitpopperwidth="false">
|
||||
<div class="select-trigger el-tooltip__trigger el-tooltip__trigger">
|
||||
<!--v-if-->
|
||||
<!-- fix: https://github.com/element-plus/element-plus/issues/11415 -->
|
||||
<!--v-if-->
|
||||
<div class="el-input el-input--small el-input--suffix">
|
||||
<!-- input -->
|
||||
|
@ -121,7 +120,7 @@ exports[`components > N8nDatatable > should render correctly 1`] = `
|
|||
<!--v-if-->
|
||||
<div class="el-input__wrapper">
|
||||
<!-- prefix slot -->
|
||||
<!--v-if--><input class="el-input__inner" type="text" readonly="" autocomplete="off" tabindex="0" placeholder="Select"><!-- suffix slot --><span class="el-input__suffix"><span class="el-input__suffix-inner"><i class="el-icon el-select__caret el-select__icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path fill="currentColor" d="M831.872 340.864 512 652.672 192.128 340.864a30.592 30.592 0 0 0-42.752 0 29.12 29.12 0 0 0 0 41.6L489.664 714.24a32 32 0 0 0 44.672 0l340.288-331.712a29.12 29.12 0 0 0 0-41.728 30.592 30.592 0 0 0-42.752 0z"></path></svg></i><!--v-if--><!--v-if--><!--v-if--><!--v-if--><!--v-if--><!--v-if--></span></span>
|
||||
<!--v-if--><input class="el-input__inner" role="combobox" aria-activedescendant="" aria-expanded="false" aria-autocomplete="none" aria-haspopup="listbox" type="text" readonly="" autocomplete="off" tabindex="0" placeholder="Select"><!-- suffix slot --><span class="el-input__suffix"><span class="el-input__suffix-inner"><i class="el-icon el-select__caret el-select__icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path fill="currentColor" d="M831.872 340.864 512 652.672 192.128 340.864a30.592 30.592 0 0 0-42.752 0 29.12 29.12 0 0 0 0 41.6L489.664 714.24a32 32 0 0 0 44.672 0l340.288-331.712a29.12 29.12 0 0 0 0-41.728 30.592 30.592 0 0 0-42.752 0z"></path></svg></i><!--v-if--><!--v-if--><!--v-if--><!--v-if--><!--v-if--><!--v-if--></span></span>
|
||||
</div><!-- append slot -->
|
||||
<!--v-if-->
|
||||
</div>
|
||||
|
|
|
@ -3,6 +3,7 @@ import { render, waitFor, within } from '@testing-library/vue';
|
|||
import userEvent from '@testing-library/user-event';
|
||||
import N8nSelect from '../Select.vue';
|
||||
import N8nOption from '../../N8nOption/Option.vue';
|
||||
import { removeDynamicAttributes } from 'n8n-design-system/utils';
|
||||
|
||||
describe('components', () => {
|
||||
describe('N8nSelect', () => {
|
||||
|
@ -21,6 +22,7 @@ describe('components', () => {
|
|||
],
|
||||
},
|
||||
});
|
||||
removeDynamicAttributes(wrapper.container);
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ exports[`components > N8nSelect > should render correctly 1`] = `
|
|||
<div class="el-select el-select--large" popperappendtobody="false" limitpopperwidth="false">
|
||||
<div class="select-trigger el-tooltip__trigger el-tooltip__trigger">
|
||||
<!--v-if-->
|
||||
<!-- fix: https://github.com/element-plus/element-plus/issues/11415 -->
|
||||
<!--v-if-->
|
||||
<div class="el-input el-input--large el-input--suffix">
|
||||
<!-- input -->
|
||||
|
@ -14,7 +13,7 @@ exports[`components > N8nSelect > should render correctly 1`] = `
|
|||
<!--v-if-->
|
||||
<div class="el-input__wrapper">
|
||||
<!-- prefix slot -->
|
||||
<!--v-if--><input class="el-input__inner" type="text" readonly="" autocomplete="off" tabindex="0" placeholder="Select"><!-- suffix slot --><span class="el-input__suffix"><span class="el-input__suffix-inner"><i class="el-icon el-select__caret el-select__icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path fill="currentColor" d="M831.872 340.864 512 652.672 192.128 340.864a30.592 30.592 0 0 0-42.752 0 29.12 29.12 0 0 0 0 41.6L489.664 714.24a32 32 0 0 0 44.672 0l340.288-331.712a29.12 29.12 0 0 0 0-41.728 30.592 30.592 0 0 0-42.752 0z"></path></svg></i><!--v-if--><!--v-if--><!--v-if--><!--v-if--><!--v-if--><!--v-if--></span></span>
|
||||
<!--v-if--><input class="el-input__inner" role="combobox" aria-activedescendant="" aria-expanded="false" aria-autocomplete="none" aria-haspopup="listbox" type="text" readonly="" autocomplete="off" tabindex="0" placeholder="Select"><!-- suffix slot --><span class="el-input__suffix"><span class="el-input__suffix-inner"><i class="el-icon el-select__caret el-select__icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path fill="currentColor" d="M831.872 340.864 512 652.672 192.128 340.864a30.592 30.592 0 0 0-42.752 0 29.12 29.12 0 0 0 0 41.6L489.664 714.24a32 32 0 0 0 44.672 0l340.288-331.712a29.12 29.12 0 0 0 0-41.728 30.592 30.592 0 0 0-42.752 0z"></path></svg></i><!--v-if--><!--v-if--><!--v-if--><!--v-if--><!--v-if--><!--v-if--></span></span>
|
||||
</div><!-- append slot -->
|
||||
<!--v-if-->
|
||||
</div>
|
||||
|
|
|
@ -4,3 +4,4 @@ export * from './markdown';
|
|||
export * from './typeguards';
|
||||
export * from './uid';
|
||||
export * from './valueByPath';
|
||||
export * from './testUtils';
|
||||
|
|
18
packages/design-system/src/utils/testUtils.ts
Normal file
18
packages/design-system/src/utils/testUtils.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
const DYNAMIC_ATTRIBUTES = ['aria-controls'];
|
||||
|
||||
/**
|
||||
* Deletes dynamic attributes from the container children so snapshots can be tested.
|
||||
*
|
||||
* Background:
|
||||
* Vue test utils use server rendering to render components (https://v1.test-utils.vuejs.org/api/render.html#render).
|
||||
* Element UI in SSR mode adds dynamic attributes to the rendered HTML each time the test is run (https://element-plus.org/en-US/guide/ssr#provide-an-id).
|
||||
*
|
||||
* NOTE: Make sure to manually remove same attributes from the expected snapshot.
|
||||
*/
|
||||
// TODO: Find a way to inject static value for dynamic attributes in tests
|
||||
export function removeDynamicAttributes(container: Element): void {
|
||||
DYNAMIC_ATTRIBUTES.forEach((attribute) => {
|
||||
const elements = container.querySelectorAll(`[${attribute}]`);
|
||||
elements.forEach((element) => element.removeAttribute(attribute));
|
||||
});
|
||||
}
|
|
@ -72,7 +72,7 @@ export const SETTINGS_STORE_DEFAULT_STATE: ISettingsState = {
|
|||
};
|
||||
|
||||
export const getDropdownItems = async (dropdownTriggerParent: HTMLElement) => {
|
||||
await userEvent.click(within(dropdownTriggerParent).getByRole('textbox'));
|
||||
await userEvent.click(within(dropdownTriggerParent).getByRole('combobox'));
|
||||
const selectTrigger = dropdownTriggerParent.querySelector(
|
||||
'.select-trigger[aria-describedby]',
|
||||
) as HTMLElement;
|
||||
|
@ -84,3 +84,9 @@ export const getDropdownItems = async (dropdownTriggerParent: HTMLElement) => {
|
|||
|
||||
return selectDropdown.querySelectorAll('.el-select-dropdown__item');
|
||||
};
|
||||
|
||||
export const getSelectedDropdownValue = async (items: NodeListOf<Element>) => {
|
||||
const selectedItem = Array.from(items).find((item) => item.classList.contains('selected'));
|
||||
expect(selectedItem).toBeInTheDocument();
|
||||
return selectedItem?.querySelector('p')?.textContent?.trim();
|
||||
};
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
import { type ContextMenuAction, useContextMenu } from '@/composables/useContextMenu';
|
||||
import { N8nActionDropdown } from 'n8n-design-system';
|
||||
import { watch, ref } from 'vue';
|
||||
import { onClickOutside } from '@vueuse/core';
|
||||
|
||||
const contextMenu = useContextMenu();
|
||||
const { position, isOpen, actions, target } = contextMenu;
|
||||
const dropdown = ref<InstanceType<typeof N8nActionDropdown>>();
|
||||
const emit = defineEmits<{ action: [action: ContextMenuAction, nodeIds: string[]] }>();
|
||||
const container = ref<HTMLDivElement>();
|
||||
|
||||
watch(
|
||||
isOpen,
|
||||
|
@ -26,7 +28,7 @@ function onActionSelect(item: string) {
|
|||
emit('action', action, contextMenu.targetNodeIds.value);
|
||||
}
|
||||
|
||||
function onClickOutside(event: MouseEvent) {
|
||||
function closeMenu(event: MouseEvent) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
contextMenu.close();
|
||||
|
@ -37,12 +39,14 @@ function onVisibleChange(open: boolean) {
|
|||
contextMenu.close();
|
||||
}
|
||||
}
|
||||
|
||||
onClickOutside(container, closeMenu);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport v-if="isOpen" to="body">
|
||||
<div
|
||||
v-on-click-outside="onClickOutside"
|
||||
ref="container"
|
||||
:class="$style.contextMenu"
|
||||
:style="{
|
||||
left: `${position[0]}px`,
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
<ExpandableInputBase :model-value="modelValue" :placeholder="placeholder">
|
||||
<input
|
||||
ref="inputRef"
|
||||
v-on-click-outside="onClickOutside"
|
||||
class="el-input__inner"
|
||||
:value="modelValue"
|
||||
:placeholder="placeholder"
|
||||
|
@ -19,6 +18,7 @@
|
|||
import type { EventBus } from 'n8n-design-system';
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
import ExpandableInputBase from './ExpandableInputBase.vue';
|
||||
import { onClickOutside } from '@vueuse/core';
|
||||
|
||||
type Props = {
|
||||
modelValue: string;
|
||||
|
@ -68,11 +68,11 @@ function onEnter() {
|
|||
}
|
||||
}
|
||||
|
||||
function onClickOutside(e: Event) {
|
||||
if (e.type === 'click' && inputRef.value) {
|
||||
onClickOutside(inputRef, () => {
|
||||
if (inputRef.value) {
|
||||
emit('blur', inputRef.value.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function onEscape() {
|
||||
emit('esc');
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, ref, toRaw, watch } from 'vue';
|
||||
import { onClickOutside } from '@vueuse/core';
|
||||
|
||||
import ExpressionFunctionIcon from '@/components/ExpressionFunctionIcon.vue';
|
||||
import InlineExpressionEditorInput from '@/components/InlineExpressionEditor/InlineExpressionEditorInput.vue';
|
||||
|
@ -21,6 +22,7 @@ const segments = ref<Segment[]>([]);
|
|||
const editorState = ref<EditorState>();
|
||||
const selection = ref<SelectionRange>();
|
||||
const inlineInput = ref<InstanceType<typeof InlineExpressionEditorInput>>();
|
||||
const container = ref<HTMLDivElement>();
|
||||
|
||||
type Props = {
|
||||
path: string;
|
||||
|
@ -156,15 +158,13 @@ watch(isDragging, (newIsDragging) => {
|
|||
}
|
||||
});
|
||||
|
||||
onClickOutside(container, (event) => onBlur(event));
|
||||
|
||||
defineExpose({ focus });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-on-click-outside="onBlur"
|
||||
:class="$style['expression-parameter-input']"
|
||||
@keydown.tab="onBlur"
|
||||
>
|
||||
<div ref="container" :class="$style['expression-parameter-input']" @keydown.tab="onBlur">
|
||||
<div
|
||||
:class="[
|
||||
$style['all-sections'],
|
||||
|
|
|
@ -17,8 +17,9 @@
|
|||
}"
|
||||
v-text="`${connection.displayName}${connection.required ? ' *' : ''}`"
|
||||
/>
|
||||
<OnClickOutside @trigger="expandConnectionGroup(connection.type, false)">
|
||||
<div
|
||||
v-on-click-outside="() => expandConnectionGroup(connection.type, false)"
|
||||
ref="connectedNodesWrapper"
|
||||
:class="{
|
||||
[$style.connectedNodesWrapper]: true,
|
||||
[$style.connectedNodesWrapperExpanded]: expandedGroups.includes(connection.type),
|
||||
|
@ -28,7 +29,9 @@
|
|||
>
|
||||
<div
|
||||
v-if="
|
||||
connectedNodes[connection.type].length >= 1 ? connection.maxConnections !== 1 : true
|
||||
connectedNodes[connection.type].length >= 1
|
||||
? connection.maxConnections !== 1
|
||||
: true
|
||||
"
|
||||
:class="{
|
||||
[$style.plusButton]: true,
|
||||
|
@ -112,6 +115,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</OnClickOutside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -129,6 +133,7 @@ import NodeIcon from '@/components/NodeIcon.vue';
|
|||
import TitledList from '@/components/TitledList.vue';
|
||||
import type { ConnectionTypes, INodeInputConfiguration, INodeTypeDescription } from 'n8n-workflow';
|
||||
import { useDebounce } from '@/composables/useDebounce';
|
||||
import { OnClickOutside } from '@vueuse/components';
|
||||
|
||||
interface Props {
|
||||
rootNode: INodeUi;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { within } from '@testing-library/vue';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { createComponentRenderer } from '@/__tests__/render';
|
||||
import { getDropdownItems } from '@/__tests__/utils';
|
||||
import { getDropdownItems, getSelectedDropdownValue } from '@/__tests__/utils';
|
||||
import { createProjectListItem, createProjectSharingData } from '@/__tests__/data/projects';
|
||||
import ProjectSharing from '@/components/Projects/ProjectSharing.vue';
|
||||
|
||||
|
@ -112,7 +112,6 @@ describe('ProjectSharing', () => {
|
|||
expect(queryByTestId('project-sharing-owner')).not.toBeInTheDocument();
|
||||
|
||||
const projectSelect = getByTestId('project-sharing-select');
|
||||
const projectSelectInput = projectSelect.querySelector('input') as HTMLInputElement;
|
||||
|
||||
// Get the dropdown items
|
||||
let projectSelectDropdownItems = await getDropdownItems(projectSelect);
|
||||
|
@ -123,11 +122,13 @@ describe('ProjectSharing', () => {
|
|||
expect(queryByTestId('project-sharing-list-item')).not.toBeInTheDocument();
|
||||
projectSelectDropdownItems = await getDropdownItems(projectSelect);
|
||||
expect(projectSelectDropdownItems).toHaveLength(3);
|
||||
expect(projectSelectDropdownItems[0].textContent).toContain(projectSelectInput.value);
|
||||
|
||||
const selectedValue = await getSelectedDropdownValue(projectSelectDropdownItems);
|
||||
expect(selectedValue).toBeTruthy();
|
||||
expect(emitted()['update:modelValue']).toEqual([
|
||||
[
|
||||
expect.objectContaining({
|
||||
name: projectSelectInput.value,
|
||||
name: selectedValue,
|
||||
}),
|
||||
],
|
||||
]);
|
||||
|
@ -136,12 +137,13 @@ describe('ProjectSharing', () => {
|
|||
await userEvent.click(projectSelectDropdownItems[1]);
|
||||
projectSelectDropdownItems = await getDropdownItems(projectSelect);
|
||||
expect(projectSelectDropdownItems).toHaveLength(3);
|
||||
expect(projectSelectDropdownItems[1].textContent).toContain(projectSelectInput.value);
|
||||
const newSelectedValue = await getSelectedDropdownValue(projectSelectDropdownItems);
|
||||
expect(newSelectedValue).toBeTruthy();
|
||||
expect(emitted()['update:modelValue']).toEqual([
|
||||
expect.any(Array),
|
||||
[
|
||||
expect.objectContaining({
|
||||
name: projectSelectInput.value,
|
||||
name: newSelectedValue,
|
||||
}),
|
||||
],
|
||||
]);
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
class="resource-locator"
|
||||
:data-test-id="`resource-locator-${parameter.name}`"
|
||||
>
|
||||
<OnClickOutside @trigger="hideResourceDropdown">
|
||||
<ResourceLocatorDropdown
|
||||
ref="dropdown"
|
||||
v-on-click-outside="hideResourceDropdown"
|
||||
:model-value="modelValue ? modelValue.value : ''"
|
||||
:show="resourceDropdownVisible"
|
||||
:filterable="isSearchable"
|
||||
|
@ -140,6 +140,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</ResourceLocatorDropdown>
|
||||
</OnClickOutside>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -176,6 +177,7 @@ import { useDebounce } from '@/composables/useDebounce';
|
|||
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { ndvEventBus } from '@/event-bus';
|
||||
import { OnClickOutside } from '@vueuse/components';
|
||||
|
||||
interface IResourceLocatorQuery {
|
||||
results: INodeListSearchItems[];
|
||||
|
@ -191,6 +193,7 @@ export default defineComponent({
|
|||
ExpressionParameterInput,
|
||||
ParameterIssues,
|
||||
ResourceLocatorDropdown,
|
||||
OnClickOutside,
|
||||
},
|
||||
props: {
|
||||
parameter: {
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
|
||||
<div
|
||||
v-show="showActions"
|
||||
ref="stickOptions"
|
||||
:class="{ 'sticky-options': true, 'no-select-on-click': true, 'force-show': forceActions }"
|
||||
>
|
||||
<div
|
||||
|
@ -58,7 +59,6 @@
|
|||
<font-awesome-icon icon="trash" />
|
||||
</div>
|
||||
<n8n-popover
|
||||
v-on-click-outside="() => setColorPopoverVisible(false)"
|
||||
effect="dark"
|
||||
trigger="click"
|
||||
placement="top"
|
||||
|
@ -109,6 +109,8 @@ import { defineComponent, ref } from 'vue';
|
|||
import type { PropType, StyleValue } from 'vue';
|
||||
import { mapStores } from 'pinia';
|
||||
|
||||
import { onClickOutside } from '@vueuse/core';
|
||||
|
||||
import { isNumber, isString } from '@/utils/typeGuards';
|
||||
import type {
|
||||
INodeUi,
|
||||
|
@ -178,6 +180,9 @@ export default defineComponent({
|
|||
const toast = useToast();
|
||||
const forceActions = ref(false);
|
||||
const isColorPopoverVisible = ref(false);
|
||||
|
||||
const stickOptions = ref<HTMLElement>();
|
||||
|
||||
const setForceActions = (value: boolean) => {
|
||||
forceActions.value = value;
|
||||
};
|
||||
|
@ -200,6 +205,8 @@ export default defineComponent({
|
|||
emit: emit as (event: string, ...args: unknown[]) => void,
|
||||
});
|
||||
|
||||
onClickOutside(stickOptions, () => setColorPopoverVisible(false));
|
||||
|
||||
return {
|
||||
deviceSupport,
|
||||
toast,
|
||||
|
@ -209,6 +216,7 @@ export default defineComponent({
|
|||
setForceActions,
|
||||
isColorPopoverVisible,
|
||||
setColorPopoverVisible,
|
||||
stickOptions,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
<template>
|
||||
<div
|
||||
v-on-click-outside="onClickOutside"
|
||||
:class="{ 'tags-container': true, focused }"
|
||||
@keydown.stop
|
||||
>
|
||||
<div ref="container" :class="{ 'tags-container': true, focused }" @keydown.stop>
|
||||
<n8n-select
|
||||
ref="selectRef"
|
||||
:teleported="true"
|
||||
|
@ -73,6 +69,7 @@ import { useTagsStore } from '@/stores/tags.store';
|
|||
import type { EventBus, N8nOption, N8nSelect } from 'n8n-design-system';
|
||||
import type { PropType } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { onClickOutside } from '@vueuse/core';
|
||||
|
||||
type SelectRef = InstanceType<typeof N8nSelect>;
|
||||
type TagRef = InstanceType<typeof N8nOption>;
|
||||
|
@ -116,6 +113,8 @@ export default defineComponent({
|
|||
const focused = ref(false);
|
||||
const preventUpdate = ref(false);
|
||||
|
||||
const container = ref<HTMLDivElement | undefined>();
|
||||
|
||||
const allTags = computed<ITag[]>(() => {
|
||||
return tagsStore.allTags;
|
||||
});
|
||||
|
@ -252,18 +251,13 @@ export default defineComponent({
|
|||
});
|
||||
}
|
||||
|
||||
function onClickOutside(e: Event) {
|
||||
const tagsDropdown = document.querySelector('.tags-dropdown');
|
||||
const tagsModal = document.querySelector('#tags-manager-modal');
|
||||
|
||||
const clickInsideTagsDropdowns =
|
||||
tagsDropdown?.contains(e.target as Node) ?? tagsDropdown === e.target;
|
||||
const clickInsideTagsModal = tagsModal?.contains(e.target as Node) ?? tagsModal === e.target;
|
||||
|
||||
if (!clickInsideTagsDropdowns && !clickInsideTagsModal && e.type === 'click') {
|
||||
onClickOutside(
|
||||
container,
|
||||
() => {
|
||||
emit('blur');
|
||||
}
|
||||
}
|
||||
},
|
||||
{ ignore: ['.tags-dropdown', '#tags-manager-modal'] },
|
||||
);
|
||||
|
||||
return {
|
||||
i18n,
|
||||
|
@ -285,7 +279,7 @@ export default defineComponent({
|
|||
filterOptions,
|
||||
onVisibleChange,
|
||||
onRemoveTag,
|
||||
onClickOutside,
|
||||
container,
|
||||
...useToast(),
|
||||
};
|
||||
},
|
||||
|
|
|
@ -116,8 +116,10 @@ describe('WorkflowLMChatModal', () => {
|
|||
await fireEvent.click(chatSendButton);
|
||||
}
|
||||
|
||||
await waitFor(() => expect(chatDialog.querySelectorAll('.chat-message')).toHaveLength(1));
|
||||
await waitFor(() =>
|
||||
expect(chatDialog.querySelectorAll('.chat-message-from-user')).toHaveLength(1),
|
||||
);
|
||||
|
||||
expect(chatDialog.querySelector('.chat-message')).toHaveTextContent('Hello!');
|
||||
expect(chatDialog.querySelector('.chat-message-from-user')).toHaveTextContent('Hello!');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import type { Plugin } from 'vue';
|
||||
import VueTouchEvents from 'vue3-touch-events';
|
||||
import { vOnClickOutside } from '@vueuse/components';
|
||||
|
||||
export const GlobalDirectivesPlugin: Plugin = {
|
||||
install(app) {
|
||||
app.use(VueTouchEvents);
|
||||
app.directive('on-click-outside', vOnClickOutside);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -105,7 +105,7 @@ describe('SettingsSourceControl', () => {
|
|||
expect(saveSettingsButton).toBeDisabled();
|
||||
|
||||
const branchSelect = getByTestId('source-control-branch-select');
|
||||
await userEvent.click(within(branchSelect).getByRole('textbox'));
|
||||
await userEvent.click(within(branchSelect).getByRole('combobox'));
|
||||
|
||||
await waitFor(() => expect(getByText('main')).toBeVisible());
|
||||
await userEvent.click(getByText('main'));
|
||||
|
@ -137,7 +137,7 @@ describe('SettingsSourceControl', () => {
|
|||
expect(refreshSshKeyButton).toBeVisible();
|
||||
});
|
||||
|
||||
await userEvent.click(within(sshKeyTypeSelect).getByRole('textbox'));
|
||||
await userEvent.click(within(sshKeyTypeSelect).getByRole('combobox'));
|
||||
await waitFor(() => expect(getByText('RSA')).toBeVisible());
|
||||
await userEvent.click(getByText('RSA'));
|
||||
await userEvent.click(refreshSshKeyButton);
|
||||
|
|
|
@ -1036,8 +1036,8 @@ importers:
|
|||
specifier: ^3.0.3
|
||||
version: 3.0.3(@fortawesome/fontawesome-svg-core@1.2.36)(vue@3.4.21(typescript@5.5.2))
|
||||
element-plus:
|
||||
specifier: ^2.3.6
|
||||
version: 2.3.6(vue@3.4.21(typescript@5.5.2))
|
||||
specifier: 2.4.3
|
||||
version: 2.4.3(vue@3.4.21(typescript@5.5.2))
|
||||
markdown-it:
|
||||
specifier: ^13.0.2
|
||||
version: 13.0.2
|
||||
|
@ -3065,8 +3065,8 @@ packages:
|
|||
resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
||||
'@element-plus/icons-vue@2.1.0':
|
||||
resolution: {integrity: sha512-PSBn3elNoanENc1vnCfh+3WA9fimRC7n+fWkf3rE5jvv+aBohNHABC/KAR5KWPecxWxDTVT1ERpRbOMRcOV/vA==}
|
||||
'@element-plus/icons-vue@2.3.1':
|
||||
resolution: {integrity: sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==}
|
||||
peerDependencies:
|
||||
vue: ^3.2.0
|
||||
|
||||
|
@ -7097,9 +7097,6 @@ packages:
|
|||
dayjs@1.11.10:
|
||||
resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
|
||||
|
||||
dayjs@1.11.6:
|
||||
resolution: {integrity: sha512-zZbY5giJAinCG+7AGaw0wIhNZ6J8AhWuSXKvuc1KAyMiRsvGQWqh4L+MomvhdAYjN+lqvVCMq1I41e3YHvXkyQ==}
|
||||
|
||||
de-indent@1.0.2:
|
||||
resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
|
||||
|
||||
|
@ -7409,8 +7406,8 @@ packages:
|
|||
electron-to-chromium@1.4.703:
|
||||
resolution: {integrity: sha512-094ZZC4nHXPKl/OwPinSMtLN9+hoFkdfQGKnvXbY+3WEAYtVDpz9UhJIViiY6Zb8agvqxiaJzNG9M+pRZWvSZw==}
|
||||
|
||||
element-plus@2.3.6:
|
||||
resolution: {integrity: sha512-GLz0pXUYI2zRfIgyI6W7SWmHk6dSEikP9yR++hsQUyy63+WjutoiGpA3SZD4cGPSXUzRFeKfVr8CnYhK5LqXZw==}
|
||||
element-plus@2.4.3:
|
||||
resolution: {integrity: sha512-b3q26j+lM4SBqiyzw8HybybGnP2pk4MWgrnzzzYW5qKQUgV6EG1Zg7nMCfgCVccI8tNvZoTiUHb2mFaiB9qT8w==}
|
||||
peerDependencies:
|
||||
vue: ^3.2.0
|
||||
|
||||
|
@ -12855,17 +12852,6 @@ packages:
|
|||
'@vue/composition-api':
|
||||
optional: true
|
||||
|
||||
vue-demi@0.14.6:
|
||||
resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==}
|
||||
engines: {node: '>=12'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@vue/composition-api': ^1.0.0-rc.1
|
||||
vue: ^3.0.0-0 || ^2.6.0
|
||||
peerDependenciesMeta:
|
||||
'@vue/composition-api':
|
||||
optional: true
|
||||
|
||||
vue-demi@0.14.8:
|
||||
resolution: {integrity: sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -15596,7 +15582,7 @@ snapshots:
|
|||
|
||||
'@discoveryjs/json-ext@0.5.7': {}
|
||||
|
||||
'@element-plus/icons-vue@2.1.0(vue@3.4.21(typescript@5.5.2))':
|
||||
'@element-plus/icons-vue@2.3.1(vue@3.4.21(typescript@5.5.2))':
|
||||
dependencies:
|
||||
vue: 3.4.21(typescript@5.5.2)
|
||||
|
||||
|
@ -19251,7 +19237,7 @@ snapshots:
|
|||
'@types/web-bluetooth': 0.0.16
|
||||
'@vueuse/metadata': 9.13.0
|
||||
'@vueuse/shared': 9.13.0(vue@3.4.21(typescript@5.5.2))
|
||||
vue-demi: 0.14.6(vue@3.4.21(typescript@5.5.2))
|
||||
vue-demi: 0.14.8(vue@3.4.21(typescript@5.5.2))
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
|
@ -19269,7 +19255,7 @@ snapshots:
|
|||
|
||||
'@vueuse/shared@9.13.0(vue@3.4.21(typescript@5.5.2))':
|
||||
dependencies:
|
||||
vue-demi: 0.14.6(vue@3.4.21(typescript@5.5.2))
|
||||
vue-demi: 0.14.8(vue@3.4.21(typescript@5.5.2))
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
|
@ -20637,8 +20623,6 @@ snapshots:
|
|||
|
||||
dayjs@1.11.10: {}
|
||||
|
||||
dayjs@1.11.6: {}
|
||||
|
||||
de-indent@1.0.2: {}
|
||||
|
||||
debug@2.6.9:
|
||||
|
@ -20941,17 +20925,17 @@ snapshots:
|
|||
|
||||
electron-to-chromium@1.4.703: {}
|
||||
|
||||
element-plus@2.3.6(vue@3.4.21(typescript@5.5.2)):
|
||||
element-plus@2.4.3(vue@3.4.21(typescript@5.5.2)):
|
||||
dependencies:
|
||||
'@ctrl/tinycolor': 3.6.0
|
||||
'@element-plus/icons-vue': 2.1.0(vue@3.4.21(typescript@5.5.2))
|
||||
'@element-plus/icons-vue': 2.3.1(vue@3.4.21(typescript@5.5.2))
|
||||
'@floating-ui/dom': 1.4.5
|
||||
'@popperjs/core': '@sxzz/popperjs-es@2.11.7'
|
||||
'@types/lodash': 4.14.195
|
||||
'@types/lodash-es': 4.17.6
|
||||
'@vueuse/core': 9.13.0(vue@3.4.21(typescript@5.5.2))
|
||||
async-validator: 4.2.5
|
||||
dayjs: 1.11.6
|
||||
dayjs: 1.11.10
|
||||
escape-html: 1.0.3
|
||||
lodash: 4.17.21
|
||||
lodash-es: 4.17.21
|
||||
|
@ -27352,10 +27336,6 @@ snapshots:
|
|||
dependencies:
|
||||
vue: 3.4.21(typescript@5.5.2)
|
||||
|
||||
vue-demi@0.14.6(vue@3.4.21(typescript@5.5.2)):
|
||||
dependencies:
|
||||
vue: 3.4.21(typescript@5.5.2)
|
||||
|
||||
vue-demi@0.14.8(vue@3.4.21(typescript@5.5.2)):
|
||||
dependencies:
|
||||
vue: 3.4.21(typescript@5.5.2)
|
||||
|
|
Loading…
Reference in a new issue