mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 20:24:05 -08:00
fix(editor): Add Set up version control CTA (#6356)
* fix(editor): Add Set up version control CTA * fix(editor): add unit test * fix(editor): extend unit test * fix(editor): update menu sidebar styles * fix(editor): update menu sidebar styles * fix(editor): fixes after conflict * fix(editor): hide branch color when not connected * fix(editor): fix connected collapsed paddings
This commit is contained in:
parent
97295f67f0
commit
e72521d5ec
|
@ -193,9 +193,4 @@ export default defineComponent({
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.menuPrefix,
|
|
||||||
.menuSuffix {
|
|
||||||
padding: var(--spacing-xs) var(--spacing-l);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
v-if="!isCollapsed && userIsTrialing"
|
v-if="!isCollapsed && userIsTrialing"
|
||||||
/></template>
|
/></template>
|
||||||
<template #menuSuffix>
|
<template #menuSuffix>
|
||||||
<div v-if="hasVersionUpdates || versionControlStore.preferences.connected">
|
<div>
|
||||||
<div v-if="hasVersionUpdates" :class="$style.updates" @click="openUpdatesPanel">
|
<div v-if="hasVersionUpdates" :class="$style.updates" @click="openUpdatesPanel">
|
||||||
<div :class="$style.giftContainer">
|
<div :class="$style.giftContainer">
|
||||||
<GiftNotificationIcon />
|
<GiftNotificationIcon />
|
||||||
|
@ -46,10 +46,7 @@
|
||||||
}}
|
}}
|
||||||
</n8n-text>
|
</n8n-text>
|
||||||
</div>
|
</div>
|
||||||
<MainSidebarVersionControl
|
<MainSidebarVersionControl :is-collapsed="isCollapsed" />
|
||||||
v-if="versionControlStore.preferences.connected"
|
|
||||||
:is-collapsed="isCollapsed"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #footer v-if="showUserArea">
|
<template #footer v-if="showUserArea">
|
||||||
|
@ -115,15 +112,17 @@ import { userHelpers } from '@/mixins/userHelpers';
|
||||||
import { debounceHelper } from '@/mixins/debounce';
|
import { debounceHelper } from '@/mixins/debounce';
|
||||||
import Vue, { defineComponent } from 'vue';
|
import Vue, { defineComponent } from 'vue';
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useUIStore } from '@/stores/ui.store';
|
import {
|
||||||
import { useSettingsStore } from '@/stores/settings.store';
|
useUIStore,
|
||||||
import { useUsersStore } from '@/stores/users.store';
|
useSettingsStore,
|
||||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
useUsersStore,
|
||||||
import { useRootStore } from '@/stores/n8nRoot.store';
|
useWorkflowsStore,
|
||||||
import { useVersionsStore } from '@/stores/versions.store';
|
useRootStore,
|
||||||
|
useVersionsStore,
|
||||||
|
useCloudPlanStore,
|
||||||
|
useVersionControlStore,
|
||||||
|
} from '@/stores/';
|
||||||
import { isNavigationFailure } from 'vue-router';
|
import { isNavigationFailure } from 'vue-router';
|
||||||
import { useVersionControlStore } from '@/stores/versionControl.store';
|
|
||||||
import { useCloudPlanStore } from '@/stores/cloudPlan.store';
|
|
||||||
import ExecutionsUsage from '@/components/ExecutionsUsage.vue';
|
import ExecutionsUsage from '@/components/ExecutionsUsage.vue';
|
||||||
import MainSidebarVersionControl from '@/components/MainSidebarVersionControl.vue';
|
import MainSidebarVersionControl from '@/components/MainSidebarVersionControl.vue';
|
||||||
|
|
||||||
|
@ -155,8 +154,8 @@ export default defineComponent({
|
||||||
useUsersStore,
|
useUsersStore,
|
||||||
useVersionsStore,
|
useVersionsStore,
|
||||||
useWorkflowsStore,
|
useWorkflowsStore,
|
||||||
useVersionControlStore,
|
|
||||||
useCloudPlanStore,
|
useCloudPlanStore,
|
||||||
|
useVersionControlStore,
|
||||||
),
|
),
|
||||||
hasVersionUpdates(): boolean {
|
hasVersionUpdates(): boolean {
|
||||||
return this.versionsStore.hasVersionUpdates;
|
return this.versionsStore.hasVersionUpdates;
|
||||||
|
@ -543,8 +542,9 @@ export default defineComponent({
|
||||||
.updates {
|
.updates {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 26px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
padding: var(--spacing-2xs) var(--spacing-l);
|
||||||
|
margin: var(--spacing-2xs) 0 0;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
color: var(--color-text-base) !important;
|
color: var(--color-text-base) !important;
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useVersionControlStore } from '@/stores/versionControl.store';
|
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { useI18n, useLoadingService, useMessage, useToast } from '@/composables';
|
import { useRouter } from 'vue-router/composables';
|
||||||
import { useUIStore } from '@/stores';
|
|
||||||
import { VERSION_CONTROL_PUSH_MODAL_KEY } from '@/constants';
|
|
||||||
import { createEventBus } from 'n8n-design-system/utils';
|
import { createEventBus } from 'n8n-design-system/utils';
|
||||||
|
import { useI18n, useLoadingService, useMessage, useToast } from '@/composables';
|
||||||
|
import { useUIStore, useUsersStore, useVersionControlStore } from '@/stores';
|
||||||
|
import { VERSION_CONTROL_PUSH_MODAL_KEY, VIEWS } from '@/constants';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
isCollapsed: boolean;
|
isCollapsed: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
const loadingService = useLoadingService();
|
const loadingService = useLoadingService();
|
||||||
const uiStore = useUIStore();
|
const uiStore = useUIStore();
|
||||||
const versionControlStore = useVersionControlStore();
|
const versionControlStore = useVersionControlStore();
|
||||||
|
const usersStore = useUsersStore();
|
||||||
const message = useMessage();
|
const message = useMessage();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
const { i18n } = useI18n();
|
const { i18n } = useI18n();
|
||||||
|
@ -24,6 +26,8 @@ const currentBranch = computed(() => {
|
||||||
return versionControlStore.preferences.branchName;
|
return versionControlStore.preferences.branchName;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const setupButtonTooltipPlacement = computed(() => (props.isCollapsed ? 'right' : 'top'));
|
||||||
|
|
||||||
async function pushWorkfolder() {
|
async function pushWorkfolder() {
|
||||||
loadingService.startLoading();
|
loadingService.startLoading();
|
||||||
try {
|
try {
|
||||||
|
@ -68,76 +72,127 @@ async function pullWorkfolder() {
|
||||||
loadingService.setLoadingText(i18n.baseText('genericHelpers.loading'));
|
loadingService.setLoadingText(i18n.baseText('genericHelpers.loading'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const goToVersionControlSetup = async () => {
|
||||||
|
await router.push({ name: VIEWS.VERSION_CONTROL });
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
:class="{ [$style.sync]: true, [$style.collapsed]: isCollapsed }"
|
:class="{
|
||||||
|
[$style.sync]: true,
|
||||||
|
[$style.collapsed]: isCollapsed,
|
||||||
|
[$style.isConnected]:
|
||||||
|
versionControlStore.preferences.connected && versionControlStore.preferences.branchName,
|
||||||
|
}"
|
||||||
:style="{ borderLeftColor: versionControlStore.preferences.branchColor }"
|
:style="{ borderLeftColor: versionControlStore.preferences.branchColor }"
|
||||||
|
data-test-id="main-sidebar-version-control"
|
||||||
>
|
>
|
||||||
<span>
|
<div
|
||||||
<n8n-icon icon="code-branch" />
|
v-if="versionControlStore.preferences.connected && versionControlStore.preferences.branchName"
|
||||||
{{ currentBranch }}
|
:class="$style.connected"
|
||||||
</span>
|
data-test-id="main-sidebar-version-control-connected"
|
||||||
<div :class="{ 'pt-xs': !isCollapsed }">
|
>
|
||||||
<n8n-tooltip :disabled="!isCollapsed" :open-delay="tooltipOpenDelay" placement="right">
|
<span>
|
||||||
<template #content>
|
<n8n-icon icon="code-branch" />
|
||||||
<div>
|
{{ currentBranch }}
|
||||||
{{ i18n.baseText('settings.versionControl.button.pull') }}
|
</span>
|
||||||
</div>
|
<div :class="{ 'pt-xs': !isCollapsed }">
|
||||||
</template>
|
<n8n-tooltip :disabled="!isCollapsed" :open-delay="tooltipOpenDelay" placement="right">
|
||||||
<n8n-button
|
<template #content>
|
||||||
:class="{
|
<div>
|
||||||
'mr-2xs': !isCollapsed,
|
{{ i18n.baseText('settings.versionControl.button.pull') }}
|
||||||
'mb-2xs': isCollapsed && !versionControlStore.preferences.branchReadOnly,
|
</div>
|
||||||
}"
|
</template>
|
||||||
icon="arrow-down"
|
<n8n-button
|
||||||
type="tertiary"
|
:class="{
|
||||||
size="mini"
|
'mr-2xs': !isCollapsed,
|
||||||
:square="isCollapsed"
|
'mb-2xs': isCollapsed && !versionControlStore.preferences.branchReadOnly,
|
||||||
@click="pullWorkfolder"
|
}"
|
||||||
|
icon="arrow-down"
|
||||||
|
type="tertiary"
|
||||||
|
size="mini"
|
||||||
|
:square="isCollapsed"
|
||||||
|
@click="pullWorkfolder"
|
||||||
|
>
|
||||||
|
<span v-if="!isCollapsed">{{
|
||||||
|
i18n.baseText('settings.versionControl.button.pull')
|
||||||
|
}}</span>
|
||||||
|
</n8n-button>
|
||||||
|
</n8n-tooltip>
|
||||||
|
<n8n-tooltip
|
||||||
|
v-if="!versionControlStore.preferences.branchReadOnly"
|
||||||
|
:disabled="!isCollapsed"
|
||||||
|
:open-delay="tooltipOpenDelay"
|
||||||
|
placement="right"
|
||||||
>
|
>
|
||||||
<span v-if="!isCollapsed">{{
|
<template #content>
|
||||||
i18n.baseText('settings.versionControl.button.pull')
|
<div>
|
||||||
}}</span>
|
{{ i18n.baseText('settings.versionControl.button.push') }}
|
||||||
</n8n-button>
|
</div>
|
||||||
</n8n-tooltip>
|
</template>
|
||||||
<n8n-tooltip
|
<n8n-button
|
||||||
v-if="!versionControlStore.preferences.branchReadOnly"
|
:square="isCollapsed"
|
||||||
:disabled="!isCollapsed"
|
icon="arrow-up"
|
||||||
:open-delay="tooltipOpenDelay"
|
type="tertiary"
|
||||||
placement="right"
|
size="mini"
|
||||||
>
|
@click="pushWorkfolder"
|
||||||
<template #content>
|
>
|
||||||
<div>
|
<span v-if="!isCollapsed">{{
|
||||||
{{ i18n.baseText('settings.versionControl.button.push') }}
|
i18n.baseText('settings.versionControl.button.push')
|
||||||
</div>
|
}}</span>
|
||||||
</template>
|
</n8n-button>
|
||||||
<n8n-button
|
</n8n-tooltip>
|
||||||
:square="isCollapsed"
|
</div>
|
||||||
icon="arrow-up"
|
|
||||||
type="tertiary"
|
|
||||||
size="mini"
|
|
||||||
@click="pushWorkfolder"
|
|
||||||
>
|
|
||||||
<span v-if="!isCollapsed">{{
|
|
||||||
i18n.baseText('settings.versionControl.button.push')
|
|
||||||
}}</span>
|
|
||||||
</n8n-button>
|
|
||||||
</n8n-tooltip>
|
|
||||||
</div>
|
</div>
|
||||||
|
<n8n-tooltip
|
||||||
|
v-else-if="
|
||||||
|
versionControlStore.isEnterpriseVersionControlEnabled && usersStore.isInstanceOwner
|
||||||
|
"
|
||||||
|
:open-delay="tooltipOpenDelay"
|
||||||
|
:placement="setupButtonTooltipPlacement"
|
||||||
|
data-test-id="main-sidebar-version-control-setup"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div>
|
||||||
|
{{ i18n.baseText('settings.versionControl.button.setup.tooltip') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<n8n-button
|
||||||
|
icon="code-branch"
|
||||||
|
type="tertiary"
|
||||||
|
size="mini"
|
||||||
|
:square="isCollapsed"
|
||||||
|
@click="goToVersionControlSetup"
|
||||||
|
>
|
||||||
|
<span v-if="!isCollapsed">{{ i18n.baseText('settings.versionControl.button.setup') }}</span>
|
||||||
|
</n8n-button>
|
||||||
|
</n8n-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.sync {
|
.sync {
|
||||||
padding: var(--spacing-s) var(--spacing-s) var(--spacing-s) var(--spacing-m);
|
padding: var(--spacing-s) var(--spacing-s) var(--spacing-s) var(--spacing-l);
|
||||||
margin: 0 calc(var(--spacing-l) * -1) calc(var(--spacing-m) * -1);
|
margin: var(--spacing-2xs) 0 calc(var(--spacing-2xs) * -1);
|
||||||
background: var(--color-background-light);
|
background: var(--color-background-light);
|
||||||
border-top: var(--border-width-base) var(--border-style-base) var(--color-foreground-base);
|
border-top: var(--border-width-base) var(--border-style-base) var(--color-foreground-base);
|
||||||
border-left: var(--spacing-3xs) var(--border-style-base) var(--color-foreground-base);
|
|
||||||
font-size: var(--font-size-2xs);
|
font-size: var(--font-size-2xs);
|
||||||
|
|
||||||
|
&.isConnected {
|
||||||
|
padding-left: var(--spacing-m);
|
||||||
|
border-left: var(--spacing-3xs) var(--border-style-base) var(--color-foreground-base);
|
||||||
|
|
||||||
|
&.collapsed {
|
||||||
|
padding-left: var(--spacing-xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
color: var(--color-text-base);
|
color: var(--color-text-base);
|
||||||
}
|
}
|
||||||
|
@ -149,10 +204,13 @@ async function pullWorkfolder() {
|
||||||
|
|
||||||
.collapsed {
|
.collapsed {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding-left: var(--spacing-xs);
|
padding-left: var(--spacing-s);
|
||||||
|
padding-right: var(--spacing-s);
|
||||||
|
|
||||||
> span {
|
.connected {
|
||||||
display: none;
|
> span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -246,8 +246,11 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.versionContainer {
|
||||||
|
padding: var(--spacing-xs) var(--spacing-l);
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (max-height: 420px) {
|
@media screen and (max-height: 420px) {
|
||||||
.updatesSubmenu,
|
|
||||||
.versionContainer {
|
.versionContainer {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
import { describe, it, expect, vi } from 'vitest';
|
||||||
|
import { render } from '@testing-library/vue';
|
||||||
|
import { PiniaVuePlugin } from 'pinia';
|
||||||
|
import { createTestingPinia } from '@pinia/testing';
|
||||||
|
import { STORES } from '@/constants';
|
||||||
|
import { i18nInstance } from '@/plugins/i18n';
|
||||||
|
import { SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils';
|
||||||
|
import MainSidebarVersionControl from '@/components/MainSidebarVersionControl.vue';
|
||||||
|
import { useUsersStore, useVersionControlStore } from '@/stores';
|
||||||
|
import { merge } from 'lodash-es';
|
||||||
|
|
||||||
|
let pinia: ReturnType<typeof createTestingPinia>;
|
||||||
|
let versionControlStore: ReturnType<typeof useVersionControlStore>;
|
||||||
|
let usersStore: ReturnType<typeof useUsersStore>;
|
||||||
|
|
||||||
|
const renderComponent = (renderOptions: Parameters<typeof render>[1] = {}) => {
|
||||||
|
return render(
|
||||||
|
MainSidebarVersionControl,
|
||||||
|
{
|
||||||
|
pinia,
|
||||||
|
i18n: i18nInstance,
|
||||||
|
...renderOptions,
|
||||||
|
},
|
||||||
|
(vue) => {
|
||||||
|
vue.use(PiniaVuePlugin);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('MainSidebarVersionControl', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
pinia = createTestingPinia({
|
||||||
|
initialState: {
|
||||||
|
[STORES.SETTINGS]: {
|
||||||
|
settings: merge({}, SETTINGS_STORE_DEFAULT_STATE.settings),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
versionControlStore = useVersionControlStore();
|
||||||
|
usersStore = useUsersStore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render empty content', async () => {
|
||||||
|
const { getByTestId } = renderComponent({ props: { isCollapsed: false } });
|
||||||
|
expect(getByTestId('main-sidebar-version-control')).toBeInTheDocument();
|
||||||
|
expect(getByTestId('main-sidebar-version-control')).toBeEmptyDOMElement();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render setup content', async () => {
|
||||||
|
vi.spyOn(versionControlStore, 'isEnterpriseVersionControlEnabled', 'get').mockReturnValue(true);
|
||||||
|
vi.spyOn(usersStore, 'isInstanceOwner', 'get').mockReturnValue(true);
|
||||||
|
|
||||||
|
const { getByTestId, queryByTestId } = renderComponent({ props: { isCollapsed: false } });
|
||||||
|
expect(getByTestId('main-sidebar-version-control-setup')).toBeInTheDocument();
|
||||||
|
expect(queryByTestId('main-sidebar-version-control-connected')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render connected content', async () => {
|
||||||
|
vi.spyOn(versionControlStore, 'preferences', 'get').mockReturnValue({
|
||||||
|
branchName: 'main',
|
||||||
|
branches: [],
|
||||||
|
authorName: '',
|
||||||
|
authorEmail: '',
|
||||||
|
repositoryUrl: '',
|
||||||
|
branchReadOnly: false,
|
||||||
|
branchColor: '#F4A6DC',
|
||||||
|
connected: true,
|
||||||
|
publicKey: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const { getByTestId, queryByTestId } = renderComponent({ props: { isCollapsed: false } });
|
||||||
|
expect(getByTestId('main-sidebar-version-control-connected')).toBeInTheDocument();
|
||||||
|
expect(queryByTestId('main-sidebar-version-control-setup')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
|
@ -1365,6 +1365,8 @@
|
||||||
"settings.versionControl.sync.prompt.error": "Please enter a commit message",
|
"settings.versionControl.sync.prompt.error": "Please enter a commit message",
|
||||||
"settings.versionControl.button.push": "Push",
|
"settings.versionControl.button.push": "Push",
|
||||||
"settings.versionControl.button.pull": "Pull",
|
"settings.versionControl.button.pull": "Pull",
|
||||||
|
"settings.versionControl.button.setup": "Set up version control",
|
||||||
|
"settings.versionControl.button.setup.tooltip": "You have version control enabled. Go to the settings page to connect to your Git repository.",
|
||||||
"settings.versionControl.modals.push.title": "Commit and push changes",
|
"settings.versionControl.modals.push.title": "Commit and push changes",
|
||||||
"settings.versionControl.modals.push.description": "Select the files you want to stage in your commit and add a commit message. ",
|
"settings.versionControl.modals.push.description": "Select the files you want to stage in your commit and add a commit message. ",
|
||||||
"settings.versionControl.modals.push.description.workflows": "Since you are on the Workflows page, the modified workflow files have been pre-selected for you.",
|
"settings.versionControl.modals.push.description.workflows": "Since you are on the Workflows page, the modified workflow files have been pre-selected for you.",
|
||||||
|
|
Loading…
Reference in a new issue