feat(editor): Evaluation feature Phase one readiness (no-changelog) (#13383)

Co-authored-by: Oleg Ivaniv <me@olegivaniv.com>
This commit is contained in:
Raúl Gómez Morales
2025-02-21 12:58:28 +01:00
committed by GitHub
parent 7bd83d7d33
commit b2293b7ad5
52 changed files with 2452 additions and 2255 deletions

View File

@@ -1,31 +1,43 @@
<script setup lang="ts">
import { useI18n } from '@/composables/useI18n';
import { ElCollapseTransition } from 'element-plus';
import { ref, nextTick } from 'vue';
import { computed, nextTick, ref, useCssModule } from 'vue';
interface EvaluationStep {
title: string;
title?: string;
warning?: boolean;
small?: boolean;
expanded?: boolean;
description?: string;
issues?: Array<{ field: string; message: string }>;
showIssues?: boolean;
tooltip?: string;
}
const props = withDefaults(defineProps<EvaluationStep>(), {
description: '',
warning: false,
small: false,
expanded: true,
expanded: false,
issues: () => [],
showIssues: true,
title: '',
});
const locale = useI18n();
const isExpanded = ref(props.expanded);
const contentRef = ref<HTMLElement | null>(null);
const containerRef = ref<HTMLElement | null>(null);
const showTooltip = ref(false);
const $style = useCssModule();
const containerClass = computed(() => {
return {
[$style.wrap]: true,
[$style.expanded]: isExpanded.value,
[$style.hasIssues]: props.issues.length > 0,
};
});
const toggleExpand = async () => {
isExpanded.value = !isExpanded.value;
@@ -36,66 +48,98 @@ const toggleExpand = async () => {
}
}
};
const handleMouseEnter = () => {
if (!props.tooltip) return;
showTooltip.value = true;
};
const handleMouseLeave = () => {
showTooltip.value = false;
};
const renderIssues = computed(() => props.showIssues && props.issues.length);
const issuesList = computed(() => props.issues.map((issue) => issue.message).join(', '));
</script>
<template>
<div
ref="containerRef"
:class="[$style.evaluationStep, small && $style.small]"
data-test-id="evaluation-step"
>
<div :class="$style.content">
<div :class="$style.header">
<div :class="[$style.icon, warning && $style.warning]">
<slot name="icon" />
<div :class="containerClass">
<slot name="containerPrefix" />
<div
ref="containerRef"
:class="[$style.evaluationStep, small && $style.small]"
data-test-id="evaluation-step"
@mouseenter="handleMouseEnter"
@mouseleave="handleMouseLeave"
>
<div :class="$style.content">
<div :class="$style.header" @click="toggleExpand">
<h3 :class="$style.title">
<span :class="$style.label">
<slot v-if="$slots.title" name="title" />
<span v-else>{{ title }}</span>
<N8nInfoTip
v-if="tooltip"
:class="$style.infoTip"
:bold="true"
type="tooltip"
theme="info"
tooltip-placement="top"
>
{{ tooltip }}
</N8nInfoTip>
</span>
</h3>
<span v-if="renderIssues" :class="$style.warningIcon">
<N8nInfoTip :bold="true" type="tooltip" theme="warning" tooltip-placement="right">
{{ issuesList }}
</N8nInfoTip>
</span>
<button
v-if="$slots.cardContent"
:class="$style.collapseButton"
:aria-expanded="isExpanded"
data-test-id="evaluation-step-collapse-button"
>
{{
isExpanded
? locale.baseText('testDefinition.edit.step.collapse')
: locale.baseText('testDefinition.edit.step.configure')
}}
<font-awesome-icon :icon="isExpanded ? 'angle-down' : 'angle-right'" size="lg" />
</button>
</div>
<h3 :class="$style.title">{{ title }}</h3>
<span v-if="issues.length > 0 && showIssues" :class="$style.warningIcon">
<N8nInfoTip :bold="true" type="tooltip" theme="warning" tooltip-placement="right">
{{ issues.map((issue) => issue.message).join(', ') }}
</N8nInfoTip>
</span>
<button
v-if="$slots.cardContent"
:class="$style.collapseButton"
:aria-expanded="isExpanded"
:aria-controls="'content-' + title.replace(/\s+/g, '-')"
data-test-id="evaluation-step-collapse-button"
@click="toggleExpand"
>
{{
isExpanded
? locale.baseText('testDefinition.edit.step.collapse')
: locale.baseText('testDefinition.edit.step.expand')
}}
<font-awesome-icon :icon="isExpanded ? 'angle-down' : 'angle-right'" size="lg" />
</button>
</div>
<div v-if="description" :class="$style.description">{{ description }}</div>
<ElCollapseTransition v-if="$slots.cardContent">
<div v-show="isExpanded" :class="$style.cardContentWrapper">
<div ref="contentRef" :class="$style.cardContent" data-test-id="evaluation-step-content">
<slot name="cardContent" />
<ElCollapseTransition v-if="$slots.cardContent">
<div v-show="isExpanded" :class="$style.cardContentWrapper">
<div
ref="contentRef"
:class="$style.cardContent"
data-test-id="evaluation-step-content"
>
<div v-if="description" :class="$style.description">{{ description }}</div>
<slot name="cardContent" />
</div>
</div>
</div>
</ElCollapseTransition>
</ElCollapseTransition>
</div>
</div>
</div>
</template>
<style module lang="scss">
.wrap {
position: relative;
}
.evaluationStep {
display: grid;
grid-template-columns: 1fr;
gap: var(--spacing-m);
background: var(--color-background-light);
padding: var(--spacing-s);
border-radius: var(--border-radius-xlarge);
box-shadow: var(--box-shadow-base);
background: var(--color-background-xlight);
border-radius: var(--border-radius-large);
border: var(--border-base);
width: 100%;
color: var(--color-text-dark);
position: relative;
z-index: 1;
&.small {
width: 80%;
margin-left: auto;
@@ -117,13 +161,14 @@ const toggleExpand = async () => {
.content {
display: grid;
gap: var(--spacing-2xs);
}
.header {
display: flex;
gap: var(--spacing-2xs);
align-items: center;
cursor: pointer;
padding: var(--spacing-s);
}
.title {
@@ -131,17 +176,28 @@ const toggleExpand = async () => {
font-size: var(--font-size-s);
line-height: 1.125rem;
}
.label {
display: flex;
align-items: center;
gap: var(--spacing-4xs);
}
.infoTip {
opacity: 0;
}
.evaluationStep:hover .infoTip {
opacity: 1;
}
.warningIcon {
color: var(--color-warning);
}
.cardContent {
font-size: var(--font-size-s);
margin-top: var(--spacing-xs);
padding: 0 var(--spacing-s);
margin: var(--spacing-s) 0;
}
.collapseButton {
cursor: pointer;
pointer-events: none;
border: none;
background: none;
padding: 0;
@@ -151,9 +207,16 @@ const toggleExpand = async () => {
text-wrap: none;
overflow: hidden;
min-width: fit-content;
.hasIssues & {
color: var(--color-danger);
}
}
.cardContentWrapper {
height: max-content;
.expanded & {
border-top: var(--border-base);
}
}
.description {
@@ -161,4 +224,15 @@ const toggleExpand = async () => {
color: var(--color-text-light);
line-height: 1rem;
}
.customTooltip {
position: absolute;
left: 0;
background: var(--color-background-dark);
color: var(--color-text-light);
padding: var(--spacing-3xs) var(--spacing-2xs);
border-radius: var(--border-radius-base);
font-size: var(--font-size-2xs);
pointer-events: none;
}
</style>