mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-10 06:34:05 -08:00
fix(editor): Disable expression editor modal opening on readonly field (#8457)
This commit is contained in:
parent
9e93980957
commit
eb27ed068b
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div ref="root" @keydown.stop></div>
|
||||
<div ref="root" :class="$style.editor" @keydown.stop></div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
@ -67,6 +67,7 @@ export default defineComponent({
|
|||
history(),
|
||||
expressionInputHandler(),
|
||||
EditorView.lineWrapping,
|
||||
EditorView.editable.of(!this.isReadOnly),
|
||||
EditorState.readOnly.of(this.isReadOnly),
|
||||
EditorView.contentAttributes.of({ 'data-gramm': 'false' }), // disable grammarly
|
||||
EditorView.domEventHandlers({ scroll: forceParse }),
|
||||
|
@ -146,4 +147,14 @@ export default defineComponent({
|
|||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss"></style>
|
||||
<style lang="scss" module>
|
||||
.editor div[contenteditable='false'] {
|
||||
background-color: var(--disabled-fill, var(--color-background-light));
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
:deep(.cm-content) {
|
||||
border-radius: var(--border-radius-base);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -75,6 +75,7 @@ export default defineComponent({
|
|||
outputTheme(),
|
||||
EditorState.readOnly.of(true),
|
||||
EditorView.lineWrapping,
|
||||
EditorView.editable.of(false),
|
||||
EditorView.domEventHandlers({ scroll: forceParse }),
|
||||
];
|
||||
|
||||
|
|
|
@ -20,8 +20,11 @@
|
|||
@blur="onBlur"
|
||||
@change="onChange"
|
||||
/>
|
||||
<n8n-icon
|
||||
<n8n-button
|
||||
v-if="!isDragging"
|
||||
square
|
||||
outline
|
||||
type="tertiary"
|
||||
icon="external-link-alt"
|
||||
size="xsmall"
|
||||
:class="$style['expression-editor-modal-opener']"
|
||||
|
@ -185,27 +188,21 @@ export default defineComponent({
|
|||
background-color: var(--color-code-background);
|
||||
padding: 3px;
|
||||
line-height: 9px;
|
||||
border: var(--border-base);
|
||||
border-top-left-radius: var(--border-radius-base);
|
||||
border-bottom-right-radius: var(--input-border-bottom-right-radius, var(--border-radius-base));
|
||||
border-right-color: var(
|
||||
--input-border-right-color,
|
||||
var(--input-border-color, var(--border-color-base))
|
||||
);
|
||||
border-bottom-color: var(
|
||||
--input-border-bottom-color,
|
||||
var(--input-border-color, var(--border-color-base))
|
||||
);
|
||||
border: var(--input-border-color, var(--border-color-base))
|
||||
var(--input-border-style, var(--border-style-base))
|
||||
var(--input-border-width, var(--border-width-base));
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
border: var(--input-border-color, var(--border-color-base))
|
||||
var(--input-border-style, var(--border-style-base))
|
||||
var(--input-border-width, var(--border-width-base));
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 9px !important;
|
||||
height: 9px;
|
||||
transform: rotate(270deg);
|
||||
|
||||
&:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div ref="root" data-test-id="inline-expression-editor-input"></div>
|
||||
<div ref="root" :class="$style.editor" data-test-id="inline-expression-editor-input"></div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
@ -169,4 +169,18 @@ export default defineComponent({
|
|||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss"></style>
|
||||
<style lang="scss" module>
|
||||
.editor div[contenteditable='false'] {
|
||||
background-color: var(--disabled-fill, var(--color-background-light));
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.cm-editor) {
|
||||
padding-left: 0;
|
||||
}
|
||||
:deep(.cm-content) {
|
||||
padding-left: var(--spacing-2xs);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
import userEvent from '@testing-library/user-event';
|
||||
import { createComponentRenderer } from '@/__tests__/render';
|
||||
import ExpressionEditorModalInput from '@/components/ExpressionEditorModal/ExpressionEditorModalInput.vue';
|
||||
|
||||
const renderComponent = createComponentRenderer(ExpressionEditorModalInput);
|
||||
|
||||
const originalRangeGetBoundingClientRect = Range.prototype.getBoundingClientRect;
|
||||
const originalRangeGetClientRects = Range.prototype.getClientRects;
|
||||
|
||||
describe('ExpressionParameterInput', () => {
|
||||
beforeAll(() => {
|
||||
Range.prototype.getBoundingClientRect = vi.fn();
|
||||
Range.prototype.getClientRects = () => ({
|
||||
item: vi.fn(),
|
||||
length: 0,
|
||||
[Symbol.iterator]: vi.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
Range.prototype.getBoundingClientRect = originalRangeGetBoundingClientRect;
|
||||
Range.prototype.getClientRects = originalRangeGetClientRects;
|
||||
});
|
||||
test.each([
|
||||
['not be editable', 'readonly', true, ''],
|
||||
['be editable', 'not readonly', false, 'test'],
|
||||
])('should %s when %s', async (_, __, isReadOnly, expected) => {
|
||||
const { getByRole } = renderComponent({
|
||||
props: {
|
||||
modelValue: '',
|
||||
path: '',
|
||||
isReadOnly,
|
||||
},
|
||||
});
|
||||
|
||||
await userEvent.type(getByRole('textbox'), 'test');
|
||||
expect(getByRole('textbox')).toHaveTextContent(expected);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,36 @@
|
|||
import { createPinia, setActivePinia } from 'pinia';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { createComponentRenderer } from '@/__tests__/render';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
import ExpressionParameterInput from '@/components/ExpressionParameterInput.vue';
|
||||
|
||||
const renderComponent = createComponentRenderer(ExpressionParameterInput);
|
||||
|
||||
let pinia: ReturnType<typeof createPinia>;
|
||||
let workflowsStore: ReturnType<typeof useWorkflowsStore>;
|
||||
let ndvStore: ReturnType<typeof useNDVStore>;
|
||||
|
||||
describe('ExpressionParameterInput', () => {
|
||||
beforeEach(() => {
|
||||
pinia = createPinia();
|
||||
setActivePinia(pinia);
|
||||
workflowsStore = useWorkflowsStore();
|
||||
ndvStore = useNDVStore();
|
||||
});
|
||||
|
||||
test.each([
|
||||
['not readonly', false, expect.anything()],
|
||||
['readonly', true, expect.anything()],
|
||||
])('should emit open expression editor modal when %s', async (_, isReadOnly, expected) => {
|
||||
const { getByTestId, emitted } = renderComponent({
|
||||
props: {
|
||||
modelValue: '',
|
||||
isReadOnly,
|
||||
},
|
||||
});
|
||||
|
||||
await userEvent.click(getByTestId('expander'));
|
||||
expect(emitted().modalOpenerClick).toEqual(expected);
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue