mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-09 22:24:05 -08:00
fix(editor): Commit theme change from Save button (#9619)
This commit is contained in:
parent
d6a046b8ad
commit
744c94d94b
|
@ -84,12 +84,11 @@
|
||||||
<div>
|
<div>
|
||||||
<n8n-input-label :label="i18n.baseText('settings.personal.theme')">
|
<n8n-input-label :label="i18n.baseText('settings.personal.theme')">
|
||||||
<n8n-select
|
<n8n-select
|
||||||
|
v-model="currentSelectedTheme"
|
||||||
:class="$style.themeSelect"
|
:class="$style.themeSelect"
|
||||||
data-test-id="theme-select"
|
data-test-id="theme-select"
|
||||||
size="small"
|
size="small"
|
||||||
:model-value="currentTheme"
|
|
||||||
filterable
|
filterable
|
||||||
@update:model-value="selectTheme"
|
|
||||||
>
|
>
|
||||||
<n8n-option
|
<n8n-option
|
||||||
v-for="item in themeOptions"
|
v-for="item in themeOptions"
|
||||||
|
@ -139,11 +138,12 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
hasAnyChanges: false,
|
hasAnyBasicInfoChanges: false,
|
||||||
formInputs: null as null | IFormInputs,
|
formInputs: null as null | IFormInputs,
|
||||||
formBus: createEventBus(),
|
formBus: createEventBus(),
|
||||||
readyToSubmit: false,
|
readyToSubmit: false,
|
||||||
mfaDocsUrl: MFA_DOCS_URL,
|
mfaDocsUrl: MFA_DOCS_URL,
|
||||||
|
currentSelectedTheme: useUIStore().theme,
|
||||||
themeOptions: [
|
themeOptions: [
|
||||||
{
|
{
|
||||||
name: 'system',
|
name: 'system',
|
||||||
|
@ -160,6 +160,34 @@ export default defineComponent({
|
||||||
] as Array<{ name: ThemeOption; label: string }>,
|
] as Array<{ name: ThemeOption; label: string }>,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
...mapStores(useUIStore, useUsersStore, useSettingsStore),
|
||||||
|
currentUser(): IUser | null {
|
||||||
|
return this.usersStore.currentUser;
|
||||||
|
},
|
||||||
|
isExternalAuthEnabled(): boolean {
|
||||||
|
const isLdapEnabled =
|
||||||
|
this.settingsStore.settings.enterprise.ldap && this.currentUser?.signInType === 'ldap';
|
||||||
|
const isSamlEnabled =
|
||||||
|
this.settingsStore.isSamlLoginEnabled && this.settingsStore.isDefaultAuthenticationSaml;
|
||||||
|
return isLdapEnabled || isSamlEnabled;
|
||||||
|
},
|
||||||
|
isPersonalSecurityEnabled(): boolean {
|
||||||
|
return this.usersStore.isInstanceOwner || !this.isExternalAuthEnabled;
|
||||||
|
},
|
||||||
|
mfaDisabled(): boolean {
|
||||||
|
return !this.usersStore.mfaEnabled;
|
||||||
|
},
|
||||||
|
isMfaFeatureEnabled(): boolean {
|
||||||
|
return this.settingsStore.isMfaFeatureEnabled;
|
||||||
|
},
|
||||||
|
hasAnyPersonalisationChanges(): boolean {
|
||||||
|
return this.currentSelectedTheme !== this.uiStore.theme;
|
||||||
|
},
|
||||||
|
hasAnyChanges() {
|
||||||
|
return this.hasAnyBasicInfoChanges || this.hasAnyPersonalisationChanges;
|
||||||
|
},
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.formInputs = [
|
this.formInputs = [
|
||||||
{
|
{
|
||||||
|
@ -201,61 +229,45 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
...mapStores(useUIStore, useUsersStore, useSettingsStore),
|
|
||||||
currentUser(): IUser | null {
|
|
||||||
return this.usersStore.currentUser;
|
|
||||||
},
|
|
||||||
isExternalAuthEnabled(): boolean {
|
|
||||||
const isLdapEnabled =
|
|
||||||
this.settingsStore.settings.enterprise.ldap && this.currentUser?.signInType === 'ldap';
|
|
||||||
const isSamlEnabled =
|
|
||||||
this.settingsStore.isSamlLoginEnabled && this.settingsStore.isDefaultAuthenticationSaml;
|
|
||||||
return isLdapEnabled || isSamlEnabled;
|
|
||||||
},
|
|
||||||
isPersonalSecurityEnabled(): boolean {
|
|
||||||
return this.usersStore.isInstanceOwner || !this.isExternalAuthEnabled;
|
|
||||||
},
|
|
||||||
mfaDisabled(): boolean {
|
|
||||||
return !this.usersStore.mfaEnabled;
|
|
||||||
},
|
|
||||||
isMfaFeatureEnabled(): boolean {
|
|
||||||
return this.settingsStore.isMfaFeatureEnabled;
|
|
||||||
},
|
|
||||||
currentTheme(): ThemeOption {
|
|
||||||
return this.uiStore.theme;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
selectTheme(theme: ThemeOption) {
|
|
||||||
this.uiStore.setTheme(theme);
|
|
||||||
},
|
|
||||||
onInput() {
|
onInput() {
|
||||||
this.hasAnyChanges = true;
|
this.hasAnyBasicInfoChanges = true;
|
||||||
},
|
},
|
||||||
onReadyToSubmit(ready: boolean) {
|
onReadyToSubmit(ready: boolean) {
|
||||||
this.readyToSubmit = ready;
|
this.readyToSubmit = ready;
|
||||||
},
|
},
|
||||||
async onSubmit(form: { firstName: string; lastName: string; email: string }) {
|
async onSubmit(form: { firstName: string; lastName: string; email: string }) {
|
||||||
if (!this.hasAnyChanges || !this.usersStore.currentUserId) {
|
try {
|
||||||
|
await Promise.all([this.updateUserBasicInfo(form), this.updatePersonalisationSettings()]);
|
||||||
|
|
||||||
|
this.showToast({
|
||||||
|
title: this.i18n.baseText('settings.personal.personalSettingsUpdated'),
|
||||||
|
message: '',
|
||||||
|
type: 'success',
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
this.showError(e, this.i18n.baseText('settings.personal.personalSettingsUpdatedError'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async updateUserBasicInfo(form: { firstName: string; lastName: string; email: string }) {
|
||||||
|
if (!this.hasAnyBasicInfoChanges || !this.usersStore.currentUserId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
await this.usersStore.updateUser({
|
await this.usersStore.updateUser({
|
||||||
id: this.usersStore.currentUserId,
|
id: this.usersStore.currentUserId,
|
||||||
firstName: form.firstName,
|
firstName: form.firstName,
|
||||||
lastName: form.lastName,
|
lastName: form.lastName,
|
||||||
email: form.email,
|
email: form.email,
|
||||||
});
|
});
|
||||||
this.showToast({
|
this.hasAnyBasicInfoChanges = false;
|
||||||
title: this.i18n.baseText('settings.personal.personalSettingsUpdated'),
|
},
|
||||||
message: '',
|
async updatePersonalisationSettings() {
|
||||||
type: 'success',
|
if (!this.hasAnyPersonalisationChanges) {
|
||||||
});
|
return;
|
||||||
this.hasAnyChanges = false;
|
|
||||||
} catch (e) {
|
|
||||||
this.showError(e, this.i18n.baseText('settings.personal.personalSettingsUpdatedError'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.uiStore.setTheme(this.currentSelectedTheme);
|
||||||
},
|
},
|
||||||
onSaveClick() {
|
onSaveClick() {
|
||||||
this.formBus.emit('submit');
|
this.formBus.emit('submit');
|
||||||
|
|
|
@ -6,10 +6,12 @@ import { useUsersStore } from '@/stores/users.store';
|
||||||
import { createComponentRenderer } from '@/__tests__/render';
|
import { createComponentRenderer } from '@/__tests__/render';
|
||||||
import { setupServer } from '@/__tests__/server';
|
import { setupServer } from '@/__tests__/server';
|
||||||
import { ROLE } from '@/constants';
|
import { ROLE } from '@/constants';
|
||||||
|
import { useUIStore } from '@/stores/ui.store';
|
||||||
|
|
||||||
let pinia: ReturnType<typeof createPinia>;
|
let pinia: ReturnType<typeof createPinia>;
|
||||||
let settingsStore: ReturnType<typeof useSettingsStore>;
|
let settingsStore: ReturnType<typeof useSettingsStore>;
|
||||||
let usersStore: ReturnType<typeof useUsersStore>;
|
let usersStore: ReturnType<typeof useUsersStore>;
|
||||||
|
let uiStore: ReturnType<typeof useUIStore>;
|
||||||
let server: ReturnType<typeof setupServer>;
|
let server: ReturnType<typeof setupServer>;
|
||||||
|
|
||||||
const renderComponent = createComponentRenderer(SettingsPersonalView);
|
const renderComponent = createComponentRenderer(SettingsPersonalView);
|
||||||
|
@ -37,6 +39,7 @@ describe('SettingsPersonalView', () => {
|
||||||
|
|
||||||
settingsStore = useSettingsStore(pinia);
|
settingsStore = useSettingsStore(pinia);
|
||||||
usersStore = useUsersStore(pinia);
|
usersStore = useUsersStore(pinia);
|
||||||
|
uiStore = useUIStore(pinia);
|
||||||
|
|
||||||
usersStore.users[currentUser.id] = currentUser;
|
usersStore.users[currentUser.id] = currentUser;
|
||||||
usersStore.currentUserId = currentUser.id;
|
usersStore.currentUserId = currentUser.id;
|
||||||
|
@ -56,6 +59,53 @@ describe('SettingsPersonalView', () => {
|
||||||
expect(getByTestId('change-password-link')).toBeInTheDocument();
|
expect(getByTestId('change-password-link')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when changing theme', () => {
|
||||||
|
it('should disable save button when theme has not been changed', async () => {
|
||||||
|
const { getByTestId } = renderComponent({ pinia });
|
||||||
|
await waitAllPromises();
|
||||||
|
|
||||||
|
expect(getByTestId('save-settings-button')).toBeDisabled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should enable save button when theme is changed', async () => {
|
||||||
|
const { getByTestId, getByPlaceholderText, findByText } = renderComponent({ pinia });
|
||||||
|
await waitAllPromises();
|
||||||
|
|
||||||
|
getByPlaceholderText('Select').click();
|
||||||
|
const darkThemeOption = await findByText('Dark theme');
|
||||||
|
darkThemeOption.click();
|
||||||
|
|
||||||
|
await waitAllPromises();
|
||||||
|
expect(getByTestId('save-settings-button')).toBeEnabled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not update theme after changing the selected theme', async () => {
|
||||||
|
const { getByPlaceholderText, findByText } = renderComponent({ pinia });
|
||||||
|
await waitAllPromises();
|
||||||
|
|
||||||
|
getByPlaceholderText('Select').click();
|
||||||
|
const darkThemeOption = await findByText('Dark theme');
|
||||||
|
darkThemeOption.click();
|
||||||
|
|
||||||
|
await waitAllPromises();
|
||||||
|
expect(uiStore.theme).toBe('system');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should commit the theme change after clicking save', async () => {
|
||||||
|
const { getByPlaceholderText, findByText, getByTestId } = renderComponent({ pinia });
|
||||||
|
await waitAllPromises();
|
||||||
|
|
||||||
|
getByPlaceholderText('Select').click();
|
||||||
|
const darkThemeOption = await findByText('Dark theme');
|
||||||
|
darkThemeOption.click();
|
||||||
|
|
||||||
|
await waitAllPromises();
|
||||||
|
|
||||||
|
getByTestId('save-settings-button').click();
|
||||||
|
expect(uiStore.theme).toBe('dark');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('when external auth is enabled, email and password change', () => {
|
describe('when external auth is enabled, email and password change', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.spyOn(settingsStore, 'isSamlLoginEnabled', 'get').mockReturnValue(true);
|
vi.spyOn(settingsStore, 'isSamlLoginEnabled', 'get').mockReturnValue(true);
|
||||||
|
|
Loading…
Reference in a new issue