Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decompose UserPreferences for reuse. #14109

Merged
merged 1 commit into from
Jun 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions client/src/components/User/UserDeletion.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { mount } from "@vue/test-utils";
import { getLocalVue } from "jest/helpers";
import UserDeletion from "./UserDeletion";

const localVue = getLocalVue(true);

const TEST_USER_ID = "myTestUserId";
const TEST_EMAIL = `${TEST_USER_ID}@test.com`;
const TEST_ROOT = "/";

function mountComponent() {
const wrapper = mount(UserDeletion, {
propsData: { userId: TEST_USER_ID, root: TEST_ROOT, email: TEST_EMAIL },
localVue,
});
return wrapper;
}

import { ROOT_COMPONENT } from "utils/navigation";

describe("UserDeletion.vue", () => {
it("contains a localized link", async () => {
const wrapper = mountComponent();
const el = await wrapper.find(ROOT_COMPONENT.preferences.delete_account.selector);
expect(el.text()).toBeLocalizationOf("Delete Account");
});
});
117 changes: 117 additions & 0 deletions client/src/components/User/UserDeletion.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<template>
<b-row class="ml-3 mb-1">
<i class="pref-icon pt-1 fa fa-lg fa-radiation" />
<div class="pref-content pr-1">
<a id="delete-account" href="javascript:void(0)"
><b v-b-modal.modal-prevent-closing v-localize>Delete Account</b></a
>
<div v-localize class="form-text text-muted">Delete your account on this Galaxy server.</div>
<b-modal
id="modal-prevent-closing"
ref="modal"
centered
title="Account Deletion"
title-tag="h2"
@show="resetModal"
@hidden="resetModal"
@ok="handleOk">
<p>
<b-alert variant="danger" :show="showDeleteError">{{ deleteError }}</b-alert>
<b>
This action cannot be undone. Your account will be permanently deleted, along with the data
contained in it.
</b>
</p>
<b-form ref="form" @submit.prevent="handleSubmit">
<b-form-group
:state="nameState"
label="Enter your user email for this account as confirmation."
label-for="Email"
invalid-feedback="Incorrect email">
<b-form-input id="name-input" v-model="name" :state="nameState" required></b-form-input>
</b-form-group>
</b-form>
</b-modal>
</div>
</b-row>
</template>

<script>
import axios from "axios";
import Vue from "vue";
import BootstrapVue from "bootstrap-vue";
import { userLogoutClient } from "layout/menu";

Vue.use(BootstrapVue);

export default {
props: {
root: {
type: String,
required: true,
},
userId: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
},
data() {
return {
name: "",
nameState: null,
deleteError: "",
};
},
computed: {
showDeleteError() {
return this.deleteError !== "";
},
},
methods: {
checkFormValidity() {
const valid = this.$refs.form.checkValidity();
this.nameState = valid;
return valid;
},
resetModal() {
this.name = "";
this.nameState = null;
},
handleOk(bvModalEvt) {
// Prevent modal from closing
bvModalEvt.preventDefault();
// Trigger submit handler
this.handleSubmit();
},
async handleSubmit() {
if (!this.checkFormValidity()) {
return false;
}
if (this.email === this.name) {
this.nameState = true;
try {
await axios.delete(`${this.root}api/users/${this.userId}`);
} catch (e) {
if (e.response.status === 403) {
this.deleteError =
"User deletion must be configured on this instance in order to allow user self-deletion. Please contact an administrator for assistance.";
return false;
}
}
userLogoutClient();
} else {
this.nameState = false;
return false;
}
},
},
};
</script>

<style scoped>
@import "user-styles.scss";
</style>
100 changes: 15 additions & 85 deletions client/src/components/User/UserPreferences.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,41 +33,12 @@
</div>
</b-row>
<ConfigProvider v-slot="{ config }">
<b-row v-if="config && !config.single_user && config.enable_account_interface" class="ml-3 mb-1">
<i class="pref-icon pt-1 fa fa-lg fa-radiation" />
<div class="pref-content pr-1">
<a id="delete-account" href="javascript:void(0)"
><b v-b-modal.modal-prevent-closing v-localize>Delete Account</b></a
>
<div v-localize class="form-text text-muted">Delete your account on this Galaxy server.</div>
<b-modal
id="modal-prevent-closing"
ref="modal"
centered
title="Account Deletion"
title-tag="h2"
@show="resetModal"
@hidden="resetModal"
@ok="handleOk">
<p>
<b-alert variant="danger" :show="showDeleteError">{{ deleteError }}</b-alert>
<b>
This action cannot be undone. Your account will be permanently deleted, along with the
data contained in it.
</b>
</p>
<b-form ref="form" @submit.prevent="handleSubmit">
<b-form-group
:state="nameState"
label="Enter your user email for this account as confirmation."
label-for="Email"
invalid-feedback="Incorrect email">
<b-form-input id="name-input" v-model="name" :state="nameState" required></b-form-input>
</b-form-group>
</b-form>
</b-modal>
</div>
</b-row>
<UserDeletion
v-if="config && !config.single_user && config.enable_account_interface"
:email="email"
:root="root"
:user-id="userId">
</UserDeletion>
</ConfigProvider>
<p class="mt-2">
{{ titleYouAreUsing }} <strong>{{ diskUsage }}</strong> {{ titleOfDiskSpace }}
Expand All @@ -91,14 +62,17 @@ import axios from "axios";
import QueryStringParsing from "utils/query-string-parsing";
import { getUserPreferencesModel } from "components/User/UserPreferencesModel";
import ConfigProvider from "components/providers/ConfigProvider";
import { userLogoutAll, userLogoutClient } from "layout/menu";
import { userLogoutAll } from "layout/menu";
import UserDeletion from "./UserDeletion";

import "@fortawesome/fontawesome-svg-core";

Vue.use(BootstrapVue);

export default {
components: {
ConfigProvider,
UserDeletion,
},
props: {
userId: {
Expand All @@ -115,12 +89,9 @@ export default {
email: "",
diskUsage: "",
quotaUsageString: "",
baseUrl: `${getAppRoot()}user`,
root: getAppRoot(),
messageVariant: null,
message: null,
name: "",
nameState: null,
deleteError: "",
submittedNames: [],
titleYouAreUsing: _l("You are using"),
titleOfDiskSpace: _l("of disk space in this Galaxy instance."),
Expand All @@ -130,6 +101,9 @@ export default {
};
},
computed: {
baseUrl() {
return `${this.root}user`;
},
activeLinks() {
const activeLinks = {};
const UserPreferencesModel = getUserPreferencesModel();
Expand All @@ -156,9 +130,6 @@ export default {

return activeLinks;
},
showDeleteError() {
return this.deleteError !== "";
},
},
created() {
const message = QueryStringParsing.get("message");
Expand Down Expand Up @@ -237,50 +208,9 @@ export default {
},
});
},
checkFormValidity() {
const valid = this.$refs.form.checkValidity();
this.nameState = valid;
return valid;
},
resetModal() {
this.name = "";
this.nameState = null;
},
handleOk(bvModalEvt) {
// Prevent modal from closing
bvModalEvt.preventDefault();
// Trigger submit handler
this.handleSubmit();
},
async handleSubmit() {
if (!this.checkFormValidity()) {
return false;
}
if (this.email === this.name) {
this.nameState = true;
try {
await axios.delete(`${getAppRoot()}api/users/${this.userId}`);
} catch (e) {
if (e.response.status === 403) {
this.deleteError =
"User deletion must be configured on this instance in order to allow user self-deletion. Please contact an administrator for assistance.";
return false;
}
}
userLogoutClient();
} else {
this.nameState = false;
return false;
}
},
},
};
</script>
<style scoped>
.pref-content {
width: calc(100% - 3rem);
}
.pref-icon {
width: 3rem;
}
@import "user-styles.scss";
</style>
6 changes: 6 additions & 0 deletions client/src/components/User/user-styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.pref-content {
width: calc(100% - 3rem);
}
.pref-icon {
width: 3rem;
}