Skip to content

Commit

Permalink
fix(editor): Add tooltips to workflow history button (#10570)
Browse files Browse the repository at this point in the history
Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <[email protected]>
  • Loading branch information
cstuncsik and netroy authored Aug 28, 2024
1 parent 7522dde commit 4a125f5
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 76 deletions.
Original file line number Diff line number Diff line change
@@ -1,30 +1,22 @@
import WorkflowDetails from '@/components/MainHeader/WorkflowDetails.vue';
import { createComponentRenderer } from '@/__tests__/render';
import { STORES, WORKFLOW_SHARE_MODAL_KEY } from '@/constants';
import { EnterpriseEditionFeature, STORES, WORKFLOW_SHARE_MODAL_KEY } from '@/constants';
import { createTestingPinia } from '@pinia/testing';
import userEvent from '@testing-library/user-event';
import { useUIStore } from '@/stores/ui.store';

vi.mock('vue-router', async () => {
const actual = await import('vue-router');

return {
...actual,
useRoute: () => ({
value: {
params: {
id: '1',
},
},
}),
};
});
vi.mock('vue-router', () => ({
useRoute: () => vi.fn(),
useRouter: () => vi.fn(),
RouterLink: vi.fn(),
}));

const initialState = {
[STORES.SETTINGS]: {
settings: {
enterprise: {
sharing: true,
[EnterpriseEditionFeature.Sharing]: true,
[EnterpriseEditionFeature.WorkflowHistory]: true,
},
},
areTagsEnabled: true,
Expand All @@ -45,21 +37,26 @@ const initialState = {

const renderComponent = createComponentRenderer(WorkflowDetails, {
pinia: createTestingPinia({ initialState }),
global: {
stubs: {
RouterLink: true,
},
},
});

let uiStore: ReturnType<typeof useUIStore>;
const workflow = {
id: '1',
name: 'Test Workflow',
tags: ['1', '2'],
active: false,
};

describe('WorkflowDetails', () => {
beforeEach(() => {
uiStore = useUIStore();
});
it('renders workflow name and tags', async () => {
const workflow = {
id: '1',
name: 'Test Workflow',
tags: ['1', '2'],
};

const { getByTestId, getByText } = renderComponent({
props: {
workflow,
Expand All @@ -79,11 +76,7 @@ describe('WorkflowDetails', () => {
const onSaveButtonClick = vi.fn();
const { getByTestId } = renderComponent({
props: {
workflow: {
id: '1',
name: 'Test Workflow',
tags: [],
},
workflow,
readOnly: false,
},
global: {
Expand All @@ -102,11 +95,7 @@ describe('WorkflowDetails', () => {

const { getByTestId } = renderComponent({
props: {
workflow: {
id: '1',
name: 'Test Workflow',
tags: [],
},
workflow,
readOnly: false,
},
});
Expand Down
55 changes: 11 additions & 44 deletions packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import SaveButton from '@/components/SaveButton.vue';
import TagsDropdown from '@/components/TagsDropdown.vue';
import InlineTextEdit from '@/components/InlineTextEdit.vue';
import BreakpointsObserver from '@/components/BreakpointsObserver.vue';
import WorkflowHistoryButton from '@/components/MainHeader/WorkflowHistoryButton.vue';
import { useRootStore } from '@/stores/root.store';
import { useSettingsStore } from '@/stores/settings.store';
Expand Down Expand Up @@ -216,19 +217,6 @@ const isWorkflowHistoryFeatureEnabled = computed(() => {
return settingsStore.isEnterpriseFeatureEnabled[EnterpriseEditionFeature.WorkflowHistory];
});
const workflowHistoryRoute = computed<{ name: string; params: { workflowId: string } }>(() => {
return {
name: VIEWS.WORKFLOW_HISTORY,
params: {
workflowId: props.workflow.id,
},
};
});
const isWorkflowHistoryButtonDisabled = computed(() => {
return isNewWorkflow.value;
});
const workflowTagIds = computed(() => {
return (props.workflow.tags ?? []).map((tag) => (typeof tag === 'string' ? tag : tag.id));
});
Expand Down Expand Up @@ -588,6 +576,10 @@ function goToUpgrade() {
void uiStore.goToUpgrade('workflow_sharing', 'upgrade-workflow-sharing');
}
function goToWorkflowHistoryUpgrade() {
void uiStore.goToUpgrade('workflow-history', 'upgrade-workflow-history');
}
function showCreateWorkflowSuccessToast(id?: string) {
if (!id || ['new', PLACEHOLDER_EMPTY_WORKFLOW_ID].includes(id)) {
let toastTitle = locale.baseText('workflows.create.personal.toast.title');
Expand Down Expand Up @@ -732,20 +724,12 @@ function showCreateWorkflowSuccessToast(id?: string) {
data-test-id="workflow-save-button"
@click="onSaveButtonClick"
/>
<RouterLink
v-if="isWorkflowHistoryFeatureEnabled"
:to="workflowHistoryRoute"
:class="$style.workflowHistoryButton"
>
<N8nIconButton
:disabled="isWorkflowHistoryButtonDisabled"
data-test-id="workflow-history-button"
type="tertiary"
icon="history"
size="medium"
text
/>
</RouterLink>
<WorkflowHistoryButton
:workflow-id="props.workflow.id"
:is-feature-enabled="isWorkflowHistoryFeatureEnabled"
:is-new-workflow="isNewWorkflow"
@upgrade="goToWorkflowHistoryUpgrade"
/>
</div>
<div :class="[$style.workflowMenuContainer, $style.group]">
<input
Expand Down Expand Up @@ -848,21 +832,4 @@ $--header-spacing: 20px;
.disabledShareButton {
cursor: not-allowed;
}
.workflowHistoryButton {
width: 30px;
height: 30px;
color: var(--color-text-dark);
border-radius: var(--border-radius-base);
&:hover {
background-color: var(--color-background-base);
}
:disabled {
background: transparent;
border: none;
opacity: 0.5;
}
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { createComponentRenderer } from '@/__tests__/render';
import { within } from '@testing-library/vue';
import userEvent from '@testing-library/user-event';
import WorkflowHistoryButton from '@/components/MainHeader/WorkflowHistoryButton.vue';

vi.mock('vue-router', () => ({
useRoute: () => vi.fn(),
useRouter: () => vi.fn(),
RouterLink: vi.fn(),
}));

const renderComponent = createComponentRenderer(WorkflowHistoryButton, {
global: {
stubs: {
RouterLink: true,
'router-link': {
template: '<div><slot /></div>',
},
},
},
});

describe('WorkflowHistoryButton', () => {
it('should be disabled if the feature is disabled', async () => {
const { getByRole, emitted } = renderComponent({
props: {
workflowId: '1',
isNewWorkflow: false,
isFeatureEnabled: false,
},
});
expect(getByRole('button')).toBeDisabled();

await userEvent.hover(getByRole('button'));
expect(getByRole('tooltip')).toBeVisible();

within(getByRole('tooltip')).getByText('View plans').click();

expect(emitted()).toHaveProperty('upgrade');
});

it('should be disabled if the feature is enabled but the workflow is new', async () => {
const { getByRole } = renderComponent({
props: {
workflowId: '1',
isNewWorkflow: true,
isFeatureEnabled: true,
},
});
expect(getByRole('button')).toBeDisabled();
});

it('should be enabled if the feature is enabled and the workflow is not new', async () => {
const { getByRole } = renderComponent({
props: {
workflowId: '1',
isNewWorkflow: false,
isFeatureEnabled: true,
},
});
expect(getByRole('button')).toBeEnabled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<script lang="ts" setup>
import { computed } from 'vue';
import { VIEWS } from '@/constants';
import { useI18n } from '@/composables/useI18n';
const locale = useI18n();
const props = defineProps<{
workflowId: string;
isNewWorkflow: boolean;
isFeatureEnabled: boolean;
}>();
const emit = defineEmits<{
upgrade: [];
}>();
const workflowHistoryRoute = computed<{ name: string; params: { workflowId: string } }>(() => ({
name: VIEWS.WORKFLOW_HISTORY,
params: {
workflowId: props.workflowId,
},
}));
</script>

<template>
<N8nTooltip placement="bottom">
<RouterLink :to="workflowHistoryRoute" :class="$style.workflowHistoryButton">
<N8nIconButton
:disabled="isNewWorkflow || !isFeatureEnabled"
data-test-id="workflow-history-button"
type="tertiary"
icon="history"
size="medium"
text
/>
</RouterLink>
<template #content>
<span v-if="isFeatureEnabled && isNewWorkflow">
{{ locale.baseText('workflowHistory.button.tooltip.empty') }}
</span>
<span v-else-if="isFeatureEnabled">{{
locale.baseText('workflowHistory.button.tooltip.enabled')
}}</span>
<i18n-t v-else keypath="workflowHistory.button.tooltip.disabled">
<template #link>
<N8nLink size="small" @click="emit('upgrade')">
{{ locale.baseText('workflowHistory.button.tooltip.disabled.link') }}
</N8nLink>
</template>
</i18n-t>
</template>
</N8nTooltip>
</template>

<style lang="scss" module>
.workflowHistoryButton {
width: 30px;
height: 30px;
color: var(--color-text-dark);
border-radius: var(--border-radius-base);
&:hover {
background-color: var(--color-background-base);
}
:disabled {
background: transparent;
border: none;
opacity: 0.5;
}
}
</style>
4 changes: 4 additions & 0 deletions packages/editor-ui/src/plugins/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2166,6 +2166,10 @@
"workflowHistory.action.restore.success.title": "Successfully restored workflow version",
"workflowHistory.action.clone.success.title": "Successfully cloned workflow version",
"workflowHistory.action.clone.success.message": "Open cloned workflow in a new tab",
"workflowHistory.button.tooltip.empty": "This workflow currently has no history to view. Once you've made your first save, you'll be able to view previous versions",
"workflowHistory.button.tooltip.enabled": "Workflow history to view and restore previous versions of your workflows",
"workflowHistory.button.tooltip.disabled": "Upgrade to unlock workflow history to view and restore previous versions of your workflows. {link}",
"workflowHistory.button.tooltip.disabled.link": "View plans",
"workflows.heading": "Workflows",
"workflows.add": "Add workflow",
"workflows.project.add": "Add workflow to project",
Expand Down

0 comments on commit 4a125f5

Please sign in to comment.