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:
Csaba Tuncsik 2023-06-06 14:27:26 +02:00 committed by GitHub
parent 97295f67f0
commit e72521d5ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 215 additions and 81 deletions

View file

@ -193,9 +193,4 @@ export default defineComponent({
display: none !important;
}
}
.menuPrefix,
.menuSuffix {
padding: var(--spacing-xs) var(--spacing-l);
}
</style>

View file

@ -32,7 +32,7 @@
v-if="!isCollapsed && userIsTrialing"
/></template>
<template #menuSuffix>
<div v-if="hasVersionUpdates || versionControlStore.preferences.connected">
<div>
<div v-if="hasVersionUpdates" :class="$style.updates" @click="openUpdatesPanel">
<div :class="$style.giftContainer">
<GiftNotificationIcon />
@ -46,10 +46,7 @@
}}
</n8n-text>
</div>
<MainSidebarVersionControl
v-if="versionControlStore.preferences.connected"
:is-collapsed="isCollapsed"
/>
<MainSidebarVersionControl :is-collapsed="isCollapsed" />
</div>
</template>
<template #footer v-if="showUserArea">
@ -115,15 +112,17 @@ import { userHelpers } from '@/mixins/userHelpers';
import { debounceHelper } from '@/mixins/debounce';
import Vue, { defineComponent } from 'vue';
import { mapStores } from 'pinia';
import { useUIStore } from '@/stores/ui.store';
import { useSettingsStore } from '@/stores/settings.store';
import { useUsersStore } from '@/stores/users.store';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { useRootStore } from '@/stores/n8nRoot.store';
import { useVersionsStore } from '@/stores/versions.store';
import {
useUIStore,
useSettingsStore,
useUsersStore,
useWorkflowsStore,
useRootStore,
useVersionsStore,
useCloudPlanStore,
useVersionControlStore,
} from '@/stores/';
import { isNavigationFailure } from 'vue-router';
import { useVersionControlStore } from '@/stores/versionControl.store';
import { useCloudPlanStore } from '@/stores/cloudPlan.store';
import ExecutionsUsage from '@/components/ExecutionsUsage.vue';
import MainSidebarVersionControl from '@/components/MainSidebarVersionControl.vue';
@ -155,8 +154,8 @@ export default defineComponent({
useUsersStore,
useVersionsStore,
useWorkflowsStore,
useVersionControlStore,
useCloudPlanStore,
useVersionControlStore,
),
hasVersionUpdates(): boolean {
return this.versionsStore.hasVersionUpdates;
@ -543,8 +542,9 @@ export default defineComponent({
.updates {
display: flex;
align-items: center;
height: 26px;
cursor: pointer;
padding: var(--spacing-2xs) var(--spacing-l);
margin: var(--spacing-2xs) 0 0;
svg {
color: var(--color-text-base) !important;

View file

@ -1,18 +1,20 @@
<script lang="ts" setup>
import { useVersionControlStore } from '@/stores/versionControl.store';
import { computed, ref } from 'vue';
import { useI18n, useLoadingService, useMessage, useToast } from '@/composables';
import { useUIStore } from '@/stores';
import { VERSION_CONTROL_PUSH_MODAL_KEY } from '@/constants';
import { useRouter } from 'vue-router/composables';
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<{
isCollapsed: boolean;
}>();
const router = useRouter();
const loadingService = useLoadingService();
const uiStore = useUIStore();
const versionControlStore = useVersionControlStore();
const usersStore = useUsersStore();
const message = useMessage();
const toast = useToast();
const { i18n } = useI18n();
@ -24,6 +26,8 @@ const currentBranch = computed(() => {
return versionControlStore.preferences.branchName;
});
const setupButtonTooltipPlacement = computed(() => (props.isCollapsed ? 'right' : 'top'));
async function pushWorkfolder() {
loadingService.startLoading();
try {
@ -68,12 +72,27 @@ async function pullWorkfolder() {
loadingService.setLoadingText(i18n.baseText('genericHelpers.loading'));
}
}
const goToVersionControlSetup = async () => {
await router.push({ name: VIEWS.VERSION_CONTROL });
};
</script>
<template>
<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 }"
data-test-id="main-sidebar-version-control"
>
<div
v-if="versionControlStore.preferences.connected && versionControlStore.preferences.branchName"
:class="$style.connected"
data-test-id="main-sidebar-version-control-connected"
>
<span>
<n8n-icon icon="code-branch" />
@ -127,17 +146,53 @@ async function pullWorkfolder() {
</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>
</template>
<style lang="scss" module>
.sync {
padding: var(--spacing-s) var(--spacing-s) var(--spacing-s) var(--spacing-m);
margin: 0 calc(var(--spacing-l) * -1) calc(var(--spacing-m) * -1);
padding: var(--spacing-s) var(--spacing-s) var(--spacing-s) var(--spacing-l);
margin: var(--spacing-2xs) 0 calc(var(--spacing-2xs) * -1);
background: var(--color-background-light);
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);
&.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 {
color: var(--color-text-base);
}
@ -149,10 +204,13 @@ async function pullWorkfolder() {
.collapsed {
text-align: center;
padding-left: var(--spacing-xs);
padding-left: var(--spacing-s);
padding-right: var(--spacing-s);
.connected {
> span {
display: none;
}
}
}
</style>

View file

@ -246,8 +246,11 @@ export default defineComponent({
}
}
.versionContainer {
padding: var(--spacing-xs) var(--spacing-l);
}
@media screen and (max-height: 420px) {
.updatesSubmenu,
.versionContainer {
display: none;
}

View file

@ -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();
});
});

View file

@ -1365,6 +1365,8 @@
"settings.versionControl.sync.prompt.error": "Please enter a commit message",
"settings.versionControl.button.push": "Push",
"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.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.",