mirror of
https://github.com/n8n-io/n8n.git
synced 2026-05-28 15:27:03 +02:00
fix(editor): Remove resource center tooltip (no-changelog) (#30468)
Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
parent
71dab38e82
commit
94113f44a5
|
|
@ -1213,7 +1213,6 @@
|
|||
"experiments.resourceCenter.sidebar": "Resources",
|
||||
"experiments.resourceCenter.template.setupTime": "{minutes} minute set up",
|
||||
"experiments.resourceCenter.title": "Resources",
|
||||
"experiments.resourceCenter.tooltip.text": "Get inspired and learn with use cases",
|
||||
"experiments.resourceCenter.video.level": "Level: {level}",
|
||||
"experiments.templatesDataQuality.modalTitle": "Featured templates",
|
||||
"error": "Error",
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import MainSidebarHeader from '@/app/components/MainSidebarHeader.vue';
|
|||
import BottomMenu from '@/app/components/BottomMenu.vue';
|
||||
import MainSidebarSourceControl from '@/app/components/MainSidebarSourceControl.vue';
|
||||
import ProjectNavigation from '@/features/collaboration/projects/components/ProjectNavigation.vue';
|
||||
import ResourceCenterTooltip from '@/experiments/resourceCenter/components/ResourceCenterTooltip.vue';
|
||||
import { useResourceCenterStore } from '@/experiments/resourceCenter/stores/resourceCenter.store';
|
||||
import { LOCAL_STORAGE_SIDEBAR_WIDTH } from '@/app/constants';
|
||||
import { useSidebarExpandedExperiment } from '@/experiments/sidebarExpanded';
|
||||
|
|
@ -285,10 +284,6 @@ function openCommandBar(event: MouseEvent) {
|
|||
|
||||
const handleSelect = (key: string) => {
|
||||
switch (key) {
|
||||
case 'resource-center': {
|
||||
resourceCenterStore.markResourceCenterTooltipDismissed();
|
||||
break;
|
||||
}
|
||||
case 'about': {
|
||||
trackHelpItemClick('about');
|
||||
uiStore.openModal(ABOUT_MODAL_KEY);
|
||||
|
|
@ -384,7 +379,6 @@ useKeybindings({
|
|||
@select="handleSelect"
|
||||
/>
|
||||
<MainSidebarSourceControl :is-collapsed="isCollapsed" />
|
||||
<ResourceCenterTooltip />
|
||||
</N8nResizeWrapper>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,203 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||
import { useI18n } from '@n8n/i18n';
|
||||
import { N8nIcon, N8nTooltip } from '@n8n/design-system';
|
||||
import { useResourceCenterStore } from '../stores/resourceCenter.store';
|
||||
import { useSidebarLayout } from '@/app/composables/useSidebarLayout';
|
||||
|
||||
const resourceCenterStore = useResourceCenterStore();
|
||||
const { isCollapsed } = useSidebarLayout();
|
||||
const {
|
||||
markResourceCenterTooltipDismissed,
|
||||
trackResourceCenterTooltipView,
|
||||
trackResourceCenterTooltipDismiss,
|
||||
} = resourceCenterStore;
|
||||
const locale = useI18n();
|
||||
|
||||
const tooltipRef = ref<HTMLElement>();
|
||||
const isVisible = ref(false);
|
||||
const position = ref({ top: 0, left: 0 });
|
||||
const tooltipKey = ref(0);
|
||||
|
||||
const shouldShow = computed(() => resourceCenterStore.shouldShowResourceCenterTooltip);
|
||||
|
||||
const tooltipText = computed(() => {
|
||||
return locale.baseText('experiments.resourceCenter.tooltip.text');
|
||||
});
|
||||
|
||||
const calculatePosition = () => {
|
||||
const resourceCenterElement = document.querySelector(
|
||||
'[data-test-id="menu-item"][id="resource-center"]',
|
||||
);
|
||||
if (!resourceCenterElement) return;
|
||||
|
||||
const menuRect = resourceCenterElement.getBoundingClientRect();
|
||||
|
||||
position.value = {
|
||||
top: menuRect.top + menuRect.height / 2 - 5,
|
||||
left: menuRect.right,
|
||||
};
|
||||
|
||||
tooltipKey.value++;
|
||||
};
|
||||
|
||||
const showTooltip = async () => {
|
||||
isVisible.value = true;
|
||||
trackResourceCenterTooltipView();
|
||||
await nextTick();
|
||||
calculatePosition();
|
||||
};
|
||||
|
||||
const hideTooltip = () => {
|
||||
isVisible.value = false;
|
||||
};
|
||||
|
||||
const handleDismiss = () => {
|
||||
trackResourceCenterTooltipDismiss();
|
||||
markResourceCenterTooltipDismissed();
|
||||
hideTooltip();
|
||||
};
|
||||
|
||||
const handleResize = () => {
|
||||
if (isVisible.value) {
|
||||
calculatePosition();
|
||||
}
|
||||
};
|
||||
|
||||
const handleContentResize = () => {
|
||||
if (isVisible.value) {
|
||||
setTimeout(() => {
|
||||
calculatePosition();
|
||||
}, 500);
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
shouldShow,
|
||||
async (newValue) => {
|
||||
if (newValue) {
|
||||
await showTooltip();
|
||||
} else {
|
||||
hideTooltip();
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
// Recalculate position when sidebar collapse state changes
|
||||
watch(isCollapsed, async () => {
|
||||
if (isVisible.value) {
|
||||
await nextTick();
|
||||
// Small delay to allow sidebar animation to complete
|
||||
setTimeout(() => {
|
||||
calculatePosition();
|
||||
}, 300);
|
||||
}
|
||||
});
|
||||
|
||||
let contentResizeObserver: ResizeObserver | null = null;
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('resize', handleResize);
|
||||
window.addEventListener('scroll', handleResize);
|
||||
|
||||
const contentElement = document.getElementById('content');
|
||||
if (contentElement) {
|
||||
contentResizeObserver = new ResizeObserver(handleContentResize);
|
||||
contentResizeObserver.observe(contentElement);
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
window.removeEventListener('scroll', handleResize);
|
||||
|
||||
if (contentResizeObserver) {
|
||||
contentResizeObserver.disconnect();
|
||||
contentResizeObserver = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div
|
||||
v-if="isVisible && shouldShow"
|
||||
ref="tooltipRef"
|
||||
:key="tooltipKey"
|
||||
:class="$style.triggerContainer"
|
||||
:style="{
|
||||
position: 'fixed',
|
||||
top: position.top + 'px',
|
||||
left: position.left + 'px',
|
||||
}"
|
||||
>
|
||||
<N8nTooltip
|
||||
:visible="true"
|
||||
placement="right"
|
||||
:show-arrow="true"
|
||||
:popper-style="{ maxWidth: '260px', minWidth: '240px' }"
|
||||
>
|
||||
<template #content>
|
||||
<div :class="$style.tooltipContent">
|
||||
<span :class="$style.text">
|
||||
{{ tooltipText }}
|
||||
</span>
|
||||
<button
|
||||
:class="$style.dismissButton"
|
||||
type="button"
|
||||
aria-label="Dismiss tooltip"
|
||||
@click="handleDismiss"
|
||||
>
|
||||
<N8nIcon icon="x" size="small" />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<div :class="$style.tooltipTrigger"></div>
|
||||
</N8nTooltip>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
.triggerContainer {
|
||||
z-index: 9999;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.tooltipTrigger {
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.tooltipContent {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: var(--spacing--2xs);
|
||||
}
|
||||
|
||||
.text {
|
||||
flex: 1;
|
||||
font-size: var(--font-size--2xs);
|
||||
line-height: var(--line-height--md);
|
||||
}
|
||||
|
||||
.dismissButton {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
color: var(--color--text--tint-1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
flex-shrink: 0;
|
||||
|
||||
&:hover {
|
||||
color: var(--color--text);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
import { createComponentRenderer } from '@/__tests__/render';
|
||||
import { screen, waitFor } from '@testing-library/vue';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import ResourceCenterTooltip from '../ResourceCenterTooltip.vue';
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
trackView: vi.fn(),
|
||||
trackDismiss: vi.fn(),
|
||||
markDismissed: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../../stores/resourceCenter.store', () => ({
|
||||
useResourceCenterStore: () => ({
|
||||
shouldShowResourceCenterTooltip: true,
|
||||
trackResourceCenterTooltipView: mocks.trackView,
|
||||
trackResourceCenterTooltipDismiss: mocks.trackDismiss,
|
||||
markResourceCenterTooltipDismissed: mocks.markDismissed,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('@/app/composables/useSidebarLayout', () => ({
|
||||
useSidebarLayout: () => ({
|
||||
isCollapsed: false,
|
||||
}),
|
||||
}));
|
||||
|
||||
const renderComponent = createComponentRenderer(ResourceCenterTooltip, {
|
||||
global: {
|
||||
stubs: {
|
||||
N8nTooltip: {
|
||||
template: '<div><slot name="content" /><slot /></div>',
|
||||
},
|
||||
N8nIcon: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
describe('ResourceCenterTooltip', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('tracks a tooltip view when it becomes visible', async () => {
|
||||
renderComponent();
|
||||
|
||||
await waitFor(() => expect(mocks.trackView).toHaveBeenCalledTimes(1));
|
||||
expect(screen.getByText('Get inspired and learn with use cases')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('tracks dismissal and persists the dismissed state', async () => {
|
||||
renderComponent();
|
||||
|
||||
await userEvent.click(await screen.findByRole('button', { name: 'Dismiss tooltip' }));
|
||||
|
||||
expect(mocks.trackDismiss).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.markDismissed).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
|
@ -75,16 +75,6 @@ describe('resourceCenter.store', () => {
|
|||
);
|
||||
});
|
||||
|
||||
describe('tooltip persistence (GRO-284 fix)', () => {
|
||||
it('reads dismissed state from localStorage on init', () => {
|
||||
localStorage.setItem('n8n-resourceCenter-tooltipDismissed', 'true');
|
||||
setActivePinia(createPinia());
|
||||
const store = useResourceCenterStore();
|
||||
// shouldShowResourceCenterTooltip should be false since tooltip was dismissed
|
||||
expect(store.shouldShowResourceCenterTooltip).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sidebar auto-expand', () => {
|
||||
it('stops auto-expanding after the sidebar is marked as expanded', () => {
|
||||
mocks.getVariant.mockReturnValue('variant');
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import { OPEN_AI_API_CREDENTIAL_TYPE, deepCopy } from 'n8n-workflow';
|
|||
import { quickStartWorkflows } from '../data/quickStartWorkflows';
|
||||
|
||||
const LOCAL_STORAGE_CREDENTIAL_KEY = 'N8N_READY_TO_RUN_OPENAI_CREDENTIAL_ID';
|
||||
const TOOLTIP_STORAGE_KEY = 'n8n-resourceCenter-tooltipDismissed';
|
||||
const SIDEBAR_AUTO_EXPANDED_KEY = 'n8n-resourceCenter-sidebarAutoExpanded';
|
||||
|
||||
export const useResourceCenterStore = defineStore('resourceCenter', () => {
|
||||
|
|
@ -26,7 +25,6 @@ export const useResourceCenterStore = defineStore('resourceCenter', () => {
|
|||
const router = useRouter();
|
||||
|
||||
const isLoadingTemplates = ref(false);
|
||||
const hasTooltipBeenDismissed = ref(localStorage.getItem(TOOLTIP_STORAGE_KEY) === 'true');
|
||||
const hasSidebarBeenAutoExpanded = ref(
|
||||
localStorage.getItem(SIDEBAR_AUTO_EXPANDED_KEY) === 'true',
|
||||
);
|
||||
|
|
@ -37,23 +35,6 @@ export const useResourceCenterStore = defineStore('resourceCenter', () => {
|
|||
RESOURCE_CENTER_EXPERIMENT.variant,
|
||||
);
|
||||
|
||||
const shouldShowResourceCenterTooltip = computed(() => {
|
||||
return isFeatureEnabled() && !hasTooltipBeenDismissed.value;
|
||||
});
|
||||
|
||||
function markResourceCenterTooltipDismissed() {
|
||||
hasTooltipBeenDismissed.value = true;
|
||||
localStorage.setItem(TOOLTIP_STORAGE_KEY, 'true');
|
||||
}
|
||||
|
||||
function trackResourceCenterTooltipView() {
|
||||
telemetry.track('User viewed resource center tooltip');
|
||||
}
|
||||
|
||||
function trackResourceCenterTooltipDismiss() {
|
||||
telemetry.track('User dismissed resource center tooltip');
|
||||
}
|
||||
|
||||
async function fetchTemplateById(templateId: number): Promise<ITemplatesWorkflowFull | null> {
|
||||
try {
|
||||
return await templatesStore.fetchTemplateById(templateId.toString());
|
||||
|
|
@ -142,17 +123,13 @@ export const useResourceCenterStore = defineStore('resourceCenter', () => {
|
|||
return {
|
||||
isFeatureEnabled,
|
||||
isLoadingTemplates,
|
||||
shouldShowResourceCenterTooltip,
|
||||
shouldAutoExpandSidebar,
|
||||
fetchTemplateById,
|
||||
loadTemplates,
|
||||
getTemplateRoute,
|
||||
createAndOpenQuickStartWorkflow,
|
||||
markResourceCenterTooltipDismissed,
|
||||
markSidebarAutoExpanded,
|
||||
trackResourceCenterView,
|
||||
trackResourceCenterTooltipView,
|
||||
trackResourceCenterTooltipDismiss,
|
||||
trackTileClick,
|
||||
};
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user