mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix(editor): Fix sidebar logo container layout (#13203)
This commit is contained in:
parent
1909b74350
commit
850d458858
|
@ -65,9 +65,7 @@ onMounted(() => {
|
|||
<div :class="containerClasses" data-test-id="n8n-logo">
|
||||
<LogoIcon :class="$style.logo" ref="logo" />
|
||||
<LogoText v-if="showLogoText" :class="$style.logoText" />
|
||||
<div v-if="showReleaseChannelTag" size="small" round :class="$style.releaseChannelTag">
|
||||
{{ releaseChannel }}
|
||||
</div>
|
||||
<div v-if="showReleaseChannelTag" :class="$style.releaseChannelTag">{{ releaseChannel }}</div>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
@ -76,6 +74,7 @@ onMounted(() => {
|
|||
.logoContainer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logoText {
|
||||
|
@ -91,12 +90,11 @@ onMounted(() => {
|
|||
background-color: var(--color-background-base);
|
||||
border: 1px solid var(--color-foreground-base);
|
||||
border-radius: var(--border-radius-base);
|
||||
font-size: var(--font-size-4xs);
|
||||
font-size: var(--font-size-3xs);
|
||||
font-weight: var(--font-weight-bold);
|
||||
text-transform: capitalize;
|
||||
line-height: var(--font-line-height-regular);
|
||||
height: var(--spacing-s);
|
||||
margin: 10px 0 0 3px;
|
||||
margin: 8px 0 0 3px;
|
||||
}
|
||||
|
||||
.authView {
|
||||
|
@ -104,12 +102,18 @@ onMounted(() => {
|
|||
margin-bottom: var(--spacing-xl);
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
.logo,
|
||||
.logoText {
|
||||
transform: scale(1.3);
|
||||
}
|
||||
|
||||
.sidebarExpanded .logo {
|
||||
.logoText {
|
||||
margin-left: var(--spacing-xs);
|
||||
margin-right: var(--spacing-3xs);
|
||||
}
|
||||
|
||||
.sidebarExpanded .logo {
|
||||
margin-left: var(--spacing-3xs);
|
||||
}
|
||||
|
||||
.sidebarCollapsed .logo {
|
||||
|
|
|
@ -44,6 +44,6 @@ exports[`Logo > renders the releaseChannelTag for non-stable releaseChannel 1`]
|
|||
<path d="M18.367 21.2h1.624v-3.442c0-1.131.685-1.627 1.46-1.627.76 0 1.357.509 1.357 1.55v3.52h1.624V17.35c0-1.664-.964-2.63-2.474-2.63-.952 0-1.485.381-1.865.877h-.102l-.14-.75h-1.484zm-14.376 0H2.367v-6.352h1.485l.14.75h.1c.381-.496.914-.877 1.866-.877 1.51 0 2.474.966 2.474 2.63v3.85H6.808V17.68c0-1.041-.596-1.55-1.358-1.55-.774 0-1.459.496-1.459 1.627z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
<div size="small" round="" class="releaseChannelTag">dev</div>
|
||||
<div class="releaseChannelTag">dev</div>
|
||||
</div>"
|
||||
`;
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
import { reactive } from 'vue';
|
||||
import { createComponentRenderer } from '@/__tests__/render';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import { type MockedStore, mockedStore } from '@/__tests__/utils';
|
||||
import { defaultSettings } from '@/__tests__/defaults';
|
||||
import MainSidebar from '@/components/MainSidebar.vue';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { useSourceControlStore } from '@/stores/sourceControl.store';
|
||||
|
||||
vi.mock('vue-router', () => ({
|
||||
useRouter: () => ({}),
|
||||
useRoute: () => reactive({}),
|
||||
RouterLink: vi.fn(),
|
||||
}));
|
||||
|
||||
let renderComponent: ReturnType<typeof createComponentRenderer>;
|
||||
let settingsStore: MockedStore<typeof useSettingsStore>;
|
||||
let uiStore: MockedStore<typeof useUIStore>;
|
||||
let sourceControlStore: MockedStore<typeof useSourceControlStore>;
|
||||
|
||||
describe('MainSidebar', () => {
|
||||
beforeEach(() => {
|
||||
renderComponent = createComponentRenderer(MainSidebar, {
|
||||
pinia: createTestingPinia(),
|
||||
});
|
||||
settingsStore = mockedStore(useSettingsStore);
|
||||
uiStore = mockedStore(useUIStore);
|
||||
sourceControlStore = mockedStore(useSourceControlStore);
|
||||
|
||||
settingsStore.settings = defaultSettings;
|
||||
});
|
||||
|
||||
it('renders the sidebar without error', () => {
|
||||
expect(() => renderComponent()).not.toThrow();
|
||||
});
|
||||
|
||||
test.each([
|
||||
[false, true, true],
|
||||
[true, false, false],
|
||||
[true, true, false],
|
||||
[false, false, false],
|
||||
])(
|
||||
'should render readonly tooltip when is opened %s and the environment is readonly %s',
|
||||
(sidebarMenuCollapsed, branchReadOnly, shouldRender) => {
|
||||
uiStore.sidebarMenuCollapsed = sidebarMenuCollapsed;
|
||||
sourceControlStore.preferences.branchReadOnly = branchReadOnly;
|
||||
|
||||
const { queryByTestId } = renderComponent();
|
||||
|
||||
expect(queryByTestId('read-only-env-icon') !== null).toBe(shouldRender);
|
||||
},
|
||||
);
|
||||
});
|
|
@ -339,7 +339,12 @@ onClickOutside(createBtn as Ref<VueInstance>, () => {
|
|||
</template>
|
||||
</i18n-t>
|
||||
</template>
|
||||
<N8nIcon icon="lock" size="xsmall" :class="$style.readOnlyEnvironmentIcon" />
|
||||
<N8nIcon
|
||||
data-test-id="read-only-env-icon"
|
||||
icon="lock"
|
||||
size="xsmall"
|
||||
:class="$style.readOnlyEnvironmentIcon"
|
||||
/>
|
||||
</N8nTooltip>
|
||||
</Logo>
|
||||
<N8nNavigationDropdown
|
||||
|
@ -368,7 +373,18 @@ onClickOutside(createBtn as Ref<VueInstance>, () => {
|
|||
</N8nTooltip>
|
||||
</template>
|
||||
<template #[createProjectAppendSlotName]="{ item }">
|
||||
<N8nTooltip v-if="item.disabled" placement="right" :content="projectsLimitReachedMessage">
|
||||
<N8nTooltip
|
||||
v-if="sourceControlStore.preferences.branchReadOnly"
|
||||
placement="right"
|
||||
:content="i18n.baseText('readOnlyEnv.cantAdd.project')"
|
||||
>
|
||||
<N8nIcon style="margin-left: auto; margin-right: 5px" icon="lock" size="xsmall" />
|
||||
</N8nTooltip>
|
||||
<N8nTooltip
|
||||
v-else-if="item.disabled"
|
||||
placement="right"
|
||||
:content="projectsLimitReachedMessage"
|
||||
>
|
||||
<N8nButton
|
||||
:size="'mini'"
|
||||
style="margin-left: auto"
|
||||
|
@ -462,19 +478,17 @@ onClickOutside(createBtn as Ref<VueInstance>, () => {
|
|||
|
||||
<style lang="scss" module>
|
||||
.sideMenu {
|
||||
display: grid;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
border-right: var(--border-width-base) var(--border-style-base) var(--color-foreground-base);
|
||||
transition: width 150ms ease-in-out;
|
||||
width: $sidebar-expanded-width;
|
||||
padding-top: 54px;
|
||||
min-width: $sidebar-expanded-width;
|
||||
max-width: 244px;
|
||||
background-color: var(--menu-background, var(--color-background-xlight));
|
||||
|
||||
.logo {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: var(--spacing-xs);
|
||||
|
@ -489,7 +503,7 @@ onClickOutside(createBtn as Ref<VueInstance>, () => {
|
|||
|
||||
&.sideMenuCollapsed {
|
||||
width: $sidebar-width;
|
||||
padding-top: 100px;
|
||||
min-width: auto;
|
||||
|
||||
.logo {
|
||||
flex-direction: column;
|
||||
|
@ -589,6 +603,6 @@ onClickOutside(createBtn as Ref<VueInstance>, () => {
|
|||
align-self: center;
|
||||
padding: 2px;
|
||||
border-radius: var(--border-radius-small);
|
||||
margin: 5px 5px 0;
|
||||
margin: 7px 12px 0 5px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -836,7 +836,7 @@ onBeforeUnmount(() => {
|
|||
}
|
||||
|
||||
.data-display-wrapper {
|
||||
height: calc(100% - var(--spacing-l)) !important;
|
||||
height: 100%;
|
||||
margin-top: var(--spacing-xl) !important;
|
||||
margin-bottom: var(--spacing-xl) !important;
|
||||
width: 100%;
|
||||
|
|
|
@ -911,6 +911,7 @@
|
|||
"readOnlyEnv.tooltip.link": "More info.",
|
||||
"readOnlyEnv.cantAdd.workflow": "You can't add new workflows to a protected n8n instance",
|
||||
"readOnlyEnv.cantAdd.credential": "You can't add new credentials to a protected n8n instance",
|
||||
"readOnlyEnv.cantAdd.project": "You can't add new projects to a protected n8n instance",
|
||||
"readOnlyEnv.cantAdd.any": "You can't create new workflows or credentials on a protected n8n instance",
|
||||
"readOnlyEnv.cantEditOrRun": "This workflow can't be edited or run manually because it's on a protected instance",
|
||||
"mainSidebar.aboutN8n": "About n8n",
|
||||
|
|
|
@ -15,6 +15,7 @@ import { STORES } from '@/constants';
|
|||
import { useUsersStore } from '@/stores/users.store';
|
||||
import { getResourcePermissions } from '@/permissions';
|
||||
import type { CreateProjectDto, UpdateProjectDto } from '@n8n/api-types';
|
||||
import { useSourceControlStore } from '@/stores/sourceControl.store';
|
||||
|
||||
export const useProjectsStore = defineStore(STORES.PROJECTS, () => {
|
||||
const route = useRoute();
|
||||
|
@ -22,6 +23,7 @@ export const useProjectsStore = defineStore(STORES.PROJECTS, () => {
|
|||
const settingsStore = useSettingsStore();
|
||||
const credentialsStore = useCredentialsStore();
|
||||
const usersStore = useUsersStore();
|
||||
const sourceControlStore = useSourceControlStore();
|
||||
|
||||
const projects = ref<ProjectListItem[]>([]);
|
||||
const myProjects = ref<ProjectListItem[]>([]);
|
||||
|
@ -60,8 +62,9 @@ export const useProjectsStore = defineStore(STORES.PROJECTS, () => {
|
|||
);
|
||||
const canCreateProjects = computed<boolean>(
|
||||
() =>
|
||||
hasUnlimitedProjects.value ||
|
||||
(isTeamProjectFeatureEnabled.value && !isTeamProjectLimitExceeded.value),
|
||||
(hasUnlimitedProjects.value ||
|
||||
(isTeamProjectFeatureEnabled.value && !isTeamProjectLimitExceeded.value)) &&
|
||||
!sourceControlStore.preferences.branchReadOnly,
|
||||
);
|
||||
const hasPermissionToCreateProjects = computed(() =>
|
||||
hasPermission(['rbac'], { rbac: { scope: 'project:create' } }),
|
||||
|
|
Loading…
Reference in a new issue