perf(editor): Fix WorkflowDetails excessive re-rendering (#10767)

This commit is contained in:
Raúl Gómez Morales 2024-09-11 11:46:18 +02:00 committed by GitHub
parent 21936c88a8
commit 00013a2069
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 49 additions and 48 deletions

View file

@ -164,7 +164,16 @@ async function navigateToExecutionsView(openInNewTab: boolean) {
<div>
<div :class="{ 'main-header': true, expanded: !uiStore.sidebarMenuCollapsed }">
<div v-show="!hideMenuBar" class="top-menu">
<WorkflowDetails v-if="workflow?.name" :workflow="workflow" :read-only="readOnly" />
<WorkflowDetails
v-if="workflow?.name"
:id="workflow.id"
:tags="workflow.tags"
:name="workflow.name"
:meta="workflow.meta"
:scopes="workflow.scopes"
:active="workflow.active"
:read-only="readOnly"
/>
<TabBar
v-if="onWorkflowPage"
:items="tabBarItems"

View file

@ -59,7 +59,7 @@ describe('WorkflowDetails', () => {
it('renders workflow name and tags', async () => {
const { getByTestId, getByText } = renderComponent({
props: {
workflow,
...workflow,
readOnly: false,
},
});
@ -76,7 +76,7 @@ describe('WorkflowDetails', () => {
const onSaveButtonClick = vi.fn();
const { getByTestId } = renderComponent({
props: {
workflow,
...workflow,
readOnly: false,
},
global: {
@ -95,7 +95,7 @@ describe('WorkflowDetails', () => {
const { getByTestId } = renderComponent({
props: {
workflow,
...workflow,
readOnly: false,
},
});

View file

@ -58,8 +58,13 @@ import { useNpsSurveyStore } from '@/stores/npsSurvey.store';
import { useLocalStorage } from '@vueuse/core';
const props = defineProps<{
workflow: IWorkflowDb;
readOnly?: boolean;
id: IWorkflowDb['id'];
tags: IWorkflowDb['tags'];
name: IWorkflowDb['name'];
meta: IWorkflowDb['meta'];
scopes: IWorkflowDb['scopes'];
active: IWorkflowDb['active'];
}>();
const $style = useCssModule();
@ -115,11 +120,7 @@ const hasChanged = (prev: string[], curr: string[]) => {
};
const isNewWorkflow = computed(() => {
return (
!props.workflow.id ||
props.workflow.id === PLACEHOLDER_EMPTY_WORKFLOW_ID ||
props.workflow.id === 'new'
);
return !props.id || props.id === PLACEHOLDER_EMPTY_WORKFLOW_ID || props.id === 'new';
});
const isWorkflowSaving = computed(() => {
@ -138,9 +139,7 @@ const onExecutionsTab = computed(() => {
].includes((route.name as string) || '');
});
const workflowPermissions = computed(
() => getResourcePermissions(workflowsStore.getWorkflowById(props.workflow.id)?.scopes).workflow,
);
const workflowPermissions = computed(() => getResourcePermissions(props.scopes).workflow);
const workflowMenuItems = computed<ActionDropdownItem[]>(() => {
const actions: ActionDropdownItem[] = [
@ -155,7 +154,7 @@ const workflowMenuItems = computed<ActionDropdownItem[]>(() => {
actions.unshift({
id: WORKFLOW_MENU_ACTIONS.DUPLICATE,
label: locale.baseText('menuActions.duplicate'),
disabled: !onWorkflowPage.value || !props.workflow.id,
disabled: !onWorkflowPage.value || !props.id,
});
actions.push(
@ -219,11 +218,11 @@ const isWorkflowHistoryFeatureEnabled = computed(() => {
});
const workflowTagIds = computed(() => {
return (props.workflow.tags ?? []).map((tag) => (typeof tag === 'string' ? tag : tag.id));
return (props.tags ?? []).map((tag) => (typeof tag === 'string' ? tag : tag.id));
});
watch(
() => props.workflow.id,
() => props.id,
() => {
isTagsEditEnabled.value = false;
isNameEditEnabled.value = false;
@ -232,8 +231,8 @@ watch(
function getWorkflowId(): string | undefined {
let id: string | undefined = undefined;
if (props.workflow.id !== PLACEHOLDER_EMPTY_WORKFLOW_ID) {
id = props.workflow.id;
if (props.id !== PLACEHOLDER_EMPTY_WORKFLOW_ID) {
id = props.id;
} else if (route.params.name && route.params.name !== 'new') {
id = route.params.name as string;
}
@ -249,8 +248,8 @@ async function onSaveButtonClick() {
const id = getWorkflowId();
const name = props.workflow.name;
const tags = props.workflow.tags as string[];
const name = props.name;
const tags = props.tags as string[];
const saved = await workflowHelpers.saveCurrentWorkflow({
id,
@ -266,7 +265,7 @@ async function onSaveButtonClick() {
if (route.name === VIEWS.EXECUTION_DEBUG) {
await router.replace({
name: VIEWS.WORKFLOW,
params: { name: props.workflow.id },
params: { name: props.id },
});
}
}
@ -275,18 +274,18 @@ async function onSaveButtonClick() {
function onShareButtonClick() {
uiStore.openModalWithData({
name: WORKFLOW_SHARE_MODAL_KEY,
data: { id: props.workflow.id },
data: { id: props.id },
});
telemetry.track('User opened sharing modal', {
workflow_id: props.workflow.id,
workflow_id: props.id,
user_id_sharer: usersStore.currentUser?.id,
sub_view: route.name === VIEWS.WORKFLOWS ? 'Workflows listing' : 'Workflow editor',
});
}
function onTagsEditEnable() {
appliedTagIds.value = (props.workflow.tags ?? []) as string[];
appliedTagIds.value = (props.tags ?? []) as string[];
isTagsEditEnabled.value = true;
setTimeout(() => {
@ -297,7 +296,7 @@ function onTagsEditEnable() {
}
async function onTagsBlur() {
const current = (props.workflow.tags ?? []) as string[];
const current = (props.tags ?? []) as string[];
const tags = appliedTagIds.value;
if (!hasChanged(current, tags)) {
isTagsEditEnabled.value = false;
@ -311,7 +310,7 @@ async function onTagsBlur() {
const saved = await workflowHelpers.saveCurrentWorkflow({ tags });
telemetry.track('User edited workflow tags', {
workflow_id: props.workflow.id,
workflow_id: props.id,
new_tag_count: tags.length,
});
@ -355,7 +354,7 @@ async function onNameSubmit({
return;
}
if (newName === props.workflow.name) {
if (newName === props.name) {
isNameEditEnabled.value = false;
onSubmit(true);
@ -405,9 +404,9 @@ async function onWorkflowMenuSelect(action: WORKFLOW_MENU_ACTIONS): Promise<void
uiStore.openModalWithData({
name: DUPLICATE_MODAL_KEY,
data: {
id: props.workflow.id,
name: props.workflow.name,
tags: props.workflow.tags,
id: props.id,
name: props.name,
tags: props.tags,
},
});
break;
@ -418,7 +417,7 @@ async function onWorkflowMenuSelect(action: WORKFLOW_MENU_ACTIONS): Promise<void
const exportData: IWorkflowToShare = {
...data,
meta: {
...props.workflow.meta,
...props.meta,
instanceId: rootStore.instanceId,
},
tags: (tags ?? []).map((tagId) => {
@ -432,7 +431,7 @@ async function onWorkflowMenuSelect(action: WORKFLOW_MENU_ACTIONS): Promise<void
type: 'application/json;charset=utf-8',
});
let name = props.workflow.name || 'unsaved_workflow';
let name = props.name || 'unsaved_workflow';
name = name.replace(/[^a-z0-9]/gi, '_');
telemetry.track('User exported workflow', { workflow_id: workflowData.id });
@ -533,7 +532,7 @@ async function onWorkflowMenuSelect(action: WORKFLOW_MENU_ACTIONS): Promise<void
case WORKFLOW_MENU_ACTIONS.DELETE: {
const deleteConfirmed = await message.confirm(
locale.baseText('mainSidebar.confirmMessage.workflowDelete.message', {
interpolate: { workflowName: props.workflow.name },
interpolate: { workflowName: props.name },
}),
locale.baseText('mainSidebar.confirmMessage.workflowDelete.headline'),
{
@ -552,7 +551,7 @@ async function onWorkflowMenuSelect(action: WORKFLOW_MENU_ACTIONS): Promise<void
}
try {
await workflowsStore.deleteWorkflow(props.workflow.id);
await workflowsStore.deleteWorkflow(props.id);
} catch (error) {
toast.showError(error, locale.baseText('generic.deleteWorkflowError'));
return;
@ -608,15 +607,10 @@ function showCreateWorkflowSuccessToast(id?: string) {
<div :class="$style.container">
<BreakpointsObserver :value-x-s="15" :value-s-m="25" :value-m-d="50" class="name-container">
<template #default="{ value }">
<ShortenName
:name="workflow.name"
:limit="value"
:custom="true"
test-id="workflow-name-input"
>
<ShortenName :name="name" :limit="value" :custom="true" test-id="workflow-name-input">
<template #default="{ shortenedName }">
<InlineTextEdit
:model-value="workflow.name"
:model-value="name"
:preview-value="shortenedName"
:is-edit-enabled="isNameEditEnabled"
:max-length="MAX_WORKFLOW_NAME_LENGTH"
@ -645,9 +639,7 @@ function showCreateWorkflowSuccessToast(id?: string) {
/>
<div
v-else-if="
(workflow.tags ?? []).length === 0 &&
!readOnly &&
(isNewWorkflow || workflowPermissions.update)
(tags ?? []).length === 0 && !readOnly && (isNewWorkflow || workflowPermissions.update)
"
>
<span class="add-tag clickable" data-test-id="new-tag-link" @click="onTagsEditEnable">
@ -656,7 +648,7 @@ function showCreateWorkflowSuccessToast(id?: string) {
</div>
<WorkflowTagsContainer
v-else
:key="workflow.id"
:key="id"
:tag-ids="workflowTagIds"
:clickable="true"
:responsive="true"
@ -669,8 +661,8 @@ function showCreateWorkflowSuccessToast(id?: string) {
<PushConnectionTracker class="actions">
<span :class="`activator ${$style.group}`">
<WorkflowActivator
:workflow-active="workflow.active"
:workflow-id="workflow.id"
:workflow-active="active"
:workflow-id="id"
:workflow-permissions="workflowPermissions"
/>
</span>
@ -727,7 +719,7 @@ function showCreateWorkflowSuccessToast(id?: string) {
@click="onSaveButtonClick"
/>
<WorkflowHistoryButton
:workflow-id="props.workflow.id"
:workflow-id="props.id"
:is-feature-enabled="isWorkflowHistoryFeatureEnabled"
:is-new-workflow="isNewWorkflow"
@upgrade="goToWorkflowHistoryUpgrade"