Browse Source

Merge pull request #38839 from nextcloud/fix/36976-_9.1.4.11/8.2_-_The_frames_of_the_input_fields_do_not_meet_the_contrast_requirements

Pytal 10 months ago
parent
commit
ba25c66f3d

File diff suppressed because it is too large
+ 0 - 0
apps/settings/css/settings.css


File diff suppressed because it is too large
+ 0 - 0
apps/settings/css/settings.css.map


+ 2 - 6
apps/settings/css/settings.scss

@@ -1400,7 +1400,7 @@ doesnotexist:-o-prefocus, .strengthify-wrapper {
 			.quota {
 				min-width: $grid-col-min-width;
 
-				.multiselect {
+				.select {
 					width: 100%;
 					color: var(--color-text-dark);
 					vertical-align: baseline;
@@ -1470,10 +1470,6 @@ doesnotexist:-o-prefocus, .strengthify-wrapper {
 			}
 
 			&:hover {
-				input:not([type='submit']):not(:focus):not(:active) {
-					border-color: var(--color-border) !important;
-				}
-
 				&:not(#grid-header) {
 					box-shadow: 5px 0 0 var(--color-primary-element) inset;
 				}
@@ -1614,7 +1610,7 @@ doesnotexist:-o-prefocus, .strengthify-wrapper {
 				}
 
 				/* Fill the grid cell */
-				.multiselect.multiselect-vue {
+				.v-select.select-vue {
 					min-width: 100%;
 					width: 100%;
 				}

+ 198 - 132
apps/settings/src/components/Users/UserRow.vue

@@ -73,23 +73,20 @@
 		</div>
 		<!-- dirty hack to ellipsis on two lines -->
 		<div v-if="user.backendCapabilities.setDisplayName" class="displayName">
-			<form :class="{'icon-loading-small': loading.displayName}"
-				class="displayName"
-				@submit.prevent="updateDisplayName">
-				<label class="hidden-visually" :for="'displayName'+user.id+rand">{{ t('settings', 'Edit display name') }}</label>
-				<input :id="'displayName'+user.id+rand"
-					ref="displayName"
-					:disabled="loading.displayName||loading.all"
-					:value="user.displayname"
-					autocapitalize="off"
-					autocomplete="off"
-					autocorrect="off"
-					spellcheck="false"
-					type="text">
-				<input class="icon-confirm"
-					type="submit"
-					value="">
-			</form>
+			<label class="hidden-visually" :for="'displayName'+user.id+rand">{{ t('settings', 'Edit display name') }}</label>
+			<NcTextField :id="'displayName'+user.id+rand"
+				:show-trailing-button="true"
+				class="user-row-text-field"
+				:class="{'icon-loading-small': loading.displayName}"
+				:disabled="loading.displayName||loading.all"
+				trailing-button-icon="arrowRight"
+				:value.sync="editedDisplayName"
+				autocapitalize="off"
+				autocomplete="off"
+				autocorrect="off"
+				spellcheck="false"
+				type="text"
+				@trailing-button-click="updateDisplayName" />
 		</div>
 		<div v-else class="name">
 			{{ user.id }}
@@ -99,143 +96,131 @@
 				</div>
 			</div>
 		</div>
-		<form v-if="settings.canChangePassword && user.backendCapabilities.setPassword"
-			:class="{'icon-loading-small': loading.password}"
-			class="password"
-			@submit.prevent="updatePassword">
+		<div v-if="settings.canChangePassword && user.backendCapabilities.setPassword" class="password">
 			<label class="hidden-visually" :for="'password'+user.id+rand">{{ t('settings', 'Add new password') }}</label>
-			<input :id="'password'+user.id+rand"
-				ref="password"
+			<NcTextField :id="'password'+user.id+rand"
+				:show-trailing-button="true"
+				class="user-row-text-field"
+				:class="{'icon-loading-small': loading.password}"
 				:disabled="loading.password || loading.all"
 				:minlength="minPasswordLength"
 				maxlength="469"
 				:placeholder="t('settings', 'Add new password')"
+				trailing-button-icon="arrowRight"
+				:value.sync="editedPassword"
 				autocapitalize="off"
 				autocomplete="new-password"
 				autocorrect="off"
 				required
 				spellcheck="false"
 				type="password"
-				value="">
-			<input class="icon-confirm" type="submit" value="">
-		</form>
+				@trailing-button-click="updatePassword" />
+		</div>
+
 		<div v-else />
-		<form :class="{'icon-loading-small': loading.mailAddress}"
-			class="mailAddress"
-			@submit.prevent="updateEmail">
+
+		<div class="mailAddress">
 			<label class="hidden-visually" :for="'mailAddress'+user.id+rand">{{ t('settings', 'Add new email address') }}</label>
-			<input :id="'mailAddress'+user.id+rand"
-				ref="mailAddress"
+			<NcTextField :id="'mailAddress'+user.id+rand"
+				:show-trailing-button="true"
+				class="user-row-text-field"
+				:class="{'icon-loading-small': loading.mailAddress}"
 				:disabled="loading.mailAddress||loading.all"
 				:placeholder="t('settings', 'Add new email address')"
-				:value="user.email"
+				trailing-button-icon="arrowRight"
+				:value.sync="editedMail"
 				autocapitalize="off"
 				autocomplete="new-password"
 				autocorrect="off"
 				spellcheck="false"
-				type="email">
-			<input class="icon-confirm" type="submit" value="">
-		</form>
+				type="email"
+				@trailing-button-click="updateEmail" />
+		</div>
 		<div :class="{'icon-loading-small': loading.groups}" class="groups">
 			<label class="hidden-visually" :for="'groups'+user.id+rand">{{ t('settings', 'Add user to group') }}</label>
-			<NcMultiselect :id="'groups'+user.id+rand"
+			<NcSelect :input-id="'groups'+user.id+rand"
 				:close-on-select="false"
 				:disabled="loading.groups||loading.all"
-				:limit="2"
 				:multiple="true"
 				:options="availableGroups"
 				:placeholder="t('settings', 'Add user to group')"
-				:tag-width="60"
 				:taggable="settings.isAdmin"
 				:value="userGroups"
-				class="multiselect-vue"
+				class="select-vue"
 				label="name"
-				tag-placeholder="create"
-				track-by="id"
-				@remove="removeUserGroup"
-				@select="addUserGroup"
-				@tag="createGroup">
-				<span slot="noResult">{{ t('settings', 'No results') }}</span>
-			</NcMultiselect>
+				:no-wrap="true"
+				:selectable="() => userGroups.length < 2"
+				:create-option="(value) => ({ name: value, isCreating: true })"
+				@option:created="createGroup"
+				@option:selected="options => addUserGroup(options.at(-1))"
+				@option:deselected="removeUserGroup" />
 		</div>
 		<div v-if="subAdminsGroups.length>0 && settings.isAdmin"
 			:class="{'icon-loading-small': loading.subadmins}"
 			class="subadmins">
 			<label class="hidden-visually" :for="'subadmins'+user.id+rand">{{ t('settings', 'Set user as admin for') }}</label>
-			<NcMultiselect :id="'subadmins'+user.id+rand"
+			<NcSelect :id="'subadmins'+user.id+rand"
 				:close-on-select="false"
 				:disabled="loading.subadmins||loading.all"
-				:limit="2"
+				label="name"
 				:multiple="true"
+				:no-wrap="true"
+				:selectable="() => userSubAdminsGroups.length < 2"
 				:options="subAdminsGroups"
 				:placeholder="t('settings', 'Set user as admin for')"
-				:tag-width="60"
 				:value="userSubAdminsGroups"
-				class="multiselect-vue"
-				label="name"
-				track-by="id"
-				@remove="removeUserSubAdmin"
-				@select="addUserSubAdmin">
-				<span slot="noResult">{{ t('settings', 'No results') }}</span>
-			</NcMultiselect>
+				class="select-vue"
+				@option:deselected="removeUserSubAdmin"
+				@option:selected="options => addUserSubAdmin(options.at(-1))" />
 		</div>
 		<div :title="usedSpace"
 			:class="{'icon-loading-small': loading.quota}"
 			class="quota">
 			<label class="hidden-visually" :for="'quota'+user.id+rand">{{ t('settings', 'Select user quota') }}</label>
-			<NcMultiselect :id="'quota'+user.id+rand"
-				:allow-empty="false"
+			<NcSelect v-model="userQuota"
+				:close-on-select="true"
+				:create-option="validateQuota"
 				:disabled="loading.quota||loading.all"
+				:input-id="'quota'+user.id+rand"
+				class="select-vue"
 				:options="quotaOptions"
 				:placeholder="t('settings', 'Select user quota')"
 				:taggable="true"
-				:value="userQuota"
-				class="multiselect-vue"
-				label="label"
-				tag-placeholder="create"
-				track-by="id"
-				@input="setUserQuota"
-				@tag="validateQuota" />
+				@option:selected="setUserQuota" />
 		</div>
 		<div v-if="showConfig.showLanguages"
 			:class="{'icon-loading-small': loading.languages}"
 			class="languages">
 			<label class="hidden-visually" :for="'language'+user.id+rand">{{ t('settings', 'Set the language') }}</label>
-			<NcMultiselect :id="'language'+user.id+rand"
+			<NcSelect :id="'language'+user.id+rand"
 				:allow-empty="false"
 				:disabled="loading.languages||loading.all"
-				:options="languages"
+				:options="availableLanguages"
 				:placeholder="t('settings', 'No language set')"
 				:value="userLanguage"
-				class="multiselect-vue"
-				group-label="label"
-				group-values="languages"
 				label="name"
-				track-by="code"
+				class="select-vue"
 				@input="setUserLanguage" />
 		</div>
+
+		<div v-if="showConfig.showStoragePath || showConfig.showUserBackend"
+			class="storageLocation" />
+		<div v-if="showConfig.showLastLogin" />
+
 		<div :class="{'icon-loading-small': loading.manager}" class="managers">
-			<NcMultiselect ref="manager"
-				v-model="currentManager"
+			<label class="hidden-visually" :for="'manager'+user.id+rand">{{ t('settings', 'Set the language') }}</label>
+			<NcSelect v-model="currentManager"
+				:input-id="'manager'+user.id+rand"
 				:close-on-select="true"
-				:user-select="true"
+				label="displayname"
 				:options="possibleManagers"
 				:placeholder="t('settings', 'Select manager')"
-				class="multiselect-vue"
-				label="displayname"
-				track-by="id"
-				@search-change="searchUserManager"
-				@remove="updateUserManager"
-				@select="updateUserManager">
-				<span slot="noResult">{{ t('settings', 'No results') }}</span>
-			</NcMultiselect>
+				class="select-vue"
+				@search="searchUserManager"
+				@option:selected="updateUserManager"
+				@input="updateUserManager" />
 		</div>
 
-		<!-- don't show this on edit mode -->
-		<div v-if="showConfig.showStoragePath || showConfig.showUserBackend"
-			class="storageLocation" />
-		<div v-if="showConfig.showLastLogin" />
-
 		<div class="userActions">
 			<div v-if="!loading.all"
 				class="toggleUserActions">
@@ -243,7 +228,7 @@
 					<NcActionButton icon="icon-checkmark"
 						:title="t('settings', 'Done')"
 						:aria-label="t('settings', 'Done')"
-						@click="editing = false" />
+						@click="handleDoneButton" />
 				</NcActions>
 				<div v-click-outside="hideMenu" class="userPopoverMenuWrapper">
 					<button class="icon-more"
@@ -268,11 +253,13 @@
 import ClickOutside from 'vue-click-outside'
 
 import NcPopoverMenu from '@nextcloud/vue/dist/Components/NcPopoverMenu.js'
-import NcMultiselect from '@nextcloud/vue/dist/Components/NcMultiselect.js'
+import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
 import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
 import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
+import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
 import UserRowSimple from './UserRowSimple.vue'
 import UserRowMixin from '../../mixins/UserRowMixin.js'
+import { showSuccess, showError } from '@nextcloud/dialogs'
 
 export default {
 	name: 'UserRow',
@@ -281,7 +268,8 @@ export default {
 		NcPopoverMenu,
 		NcActions,
 		NcActionButton,
-		NcMultiselect,
+		NcSelect,
+		NcTextField,
 	},
 	directives: {
 		ClickOutside,
@@ -331,6 +319,10 @@ export default {
 	},
 	data() {
 		return {
+			// default quota is set to unlimited
+			unlimitedQuota: { id: 'none', label: t('settings', 'Unlimited') },
+			// temporary value used for multiselect change
+			selectedQuota: false,
 			rand: parseInt(Math.random() * 1000),
 			openedMenu: false,
 			feedbackMessage: '',
@@ -351,6 +343,9 @@ export default {
 				wipe: false,
 				manager: false,
 			},
+			editedDisplayName: this.user.displayname,
+			editedPassword: '',
+			editedMail: this.user.email ?? '',
 		}
 	},
 	computed: {
@@ -383,6 +378,27 @@ export default {
 			}
 			return actions.concat(this.externalActions)
 		},
+
+		// mapping saved values to objects
+		userQuota: {
+			get() {
+				if (this.selectedQuota !== false) {
+					return this.selectedQuota
+				}
+				if (this.settings.defaultQuota !== this.unlimitedQuota.id && OC.Util.computerFileSize(this.settings.defaultQuota) >= 0) {
+					// if value is valid, let's map the quotaOptions or return custom quota
+					return { id: this.settings.defaultQuota, label: this.settings.defaultQuota }
+				}
+				return this.unlimitedQuota // unlimited
+			},
+			set(quota) {
+				this.selectedQuota = quota
+			},
+		},
+
+		availableLanguages() {
+			return this.languages[0].languages.concat(this.languages[1].languages)
+		},
 	},
 	async beforeMount() {
 		await this.searchUserManager()
@@ -444,14 +460,22 @@ export default {
 		},
 
 		updateUserManager(manager) {
+			if (manager === null) {
+				this.currentManager = ''
+			}
 			this.loading.manager = true
-			this.$store.dispatch('setUserData', {
-				userid: this.user.id,
-				key: 'manager',
-				value: this.currentManager ? this.currentManager.id : '',
-			}).then(() => {
+			try {
+				this.$store.dispatch('setUserData', {
+					userid: this.user.id,
+					key: 'manager',
+					value: this.currentManager ? this.currentManager.id : '',
+				})
+			} catch (error) {
+				showError(t('setting', 'Update of user manager was failed'))
+				console.error(error)
+			} finally {
 				this.loading.manager = false
-			})
+			}
 		},
 
 		deleteUser() {
@@ -501,15 +525,16 @@ export default {
 		 * @param {string} displayName The display name
 		 */
 		updateDisplayName() {
-			const displayName = this.$refs.displayName.value
 			this.loading.displayName = true
 			this.$store.dispatch('setUserData', {
 				userid: this.user.id,
 				key: 'displayname',
-				value: displayName,
+				value: this.editedDisplayName,
 			}).then(() => {
 				this.loading.displayName = false
-				this.$refs.displayName.value = displayName
+				if (this.editedDisplayName === this.user.displayname) {
+					showSuccess(t('setting', 'Display name was successfully changed'))
+				}
 			})
 		},
 
@@ -519,16 +544,21 @@ export default {
 		 * @param {string} password The email address
 		 */
 		updatePassword() {
-			const password = this.$refs.password.value
 			this.loading.password = true
-			this.$store.dispatch('setUserData', {
-				userid: this.user.id,
-				key: 'password',
-				value: password,
-			}).then(() => {
+			if (this.editedPassword.length === 0) {
+				showError(t('setting', "Password can't be empty"))
 				this.loading.password = false
-				this.$refs.password.value = '' // empty & show placeholder
-			})
+			} else {
+				this.$store.dispatch('setUserData', {
+					userid: this.user.id,
+					key: 'password',
+					value: this.editedPassword,
+				}).then(() => {
+					this.loading.password = false
+					this.editedPassword = ''
+					showSuccess(t('setting', 'Password was successfully changed'))
+				})
+			}
 		},
 
 		/**
@@ -537,16 +567,23 @@ export default {
 		 * @param {string} mailAddress The email address
 		 */
 		updateEmail() {
-			const mailAddress = this.$refs.mailAddress.value
 			this.loading.mailAddress = true
-			this.$store.dispatch('setUserData', {
-				userid: this.user.id,
-				key: 'email',
-				value: mailAddress,
-			}).then(() => {
+			if (this.editedMail === '') {
+				showError(t('setting', "Email can't be empty"))
 				this.loading.mailAddress = false
-				this.$refs.mailAddress.value = mailAddress
-			})
+				this.editedMail = this.user.email
+			} else {
+				this.$store.dispatch('setUserData', {
+					userid: this.user.id,
+					key: 'email',
+					value: this.editedMail,
+				}).then(() => {
+					this.loading.mailAddress = false
+					if (this.editedMail === this.user.email) {
+						showSuccess(t('setting', 'Email was successfully changed'))
+					}
+				})
+			}
 		},
 
 		/**
@@ -554,7 +591,7 @@ export default {
 		 *
 		 * @param {string} gid Group id
 		 */
-		async createGroup(gid) {
+		async createGroup({ name: gid }) {
 			this.loading = { groups: true, subadmins: true }
 			try {
 				await this.$store.dispatch('addGroup', gid)
@@ -574,12 +611,17 @@ export default {
 		 * @param {object} group Group object
 		 */
 		async addUserGroup(group) {
-			if (group.canAdd === false) {
-				return false
+			if (group.isCreating) {
+				// This is NcSelect's internal value for a new inputted group name
+				// Ignore
+				return
 			}
 			this.loading.groups = true
 			const userid = this.user.id
 			const gid = group.id
+			if (group.canAdd === false) {
+				return false
+			}
 			try {
 				await this.$store.dispatch('addUserGroup', { userid, gid })
 			} catch (error) {
@@ -598,11 +640,9 @@ export default {
 			if (group.canRemove === false) {
 				return false
 			}
-
 			this.loading.groups = true
 			const userid = this.user.id
 			const gid = group.id
-
 			try {
 				await this.$store.dispatch('removeUserGroup', {
 					userid,
@@ -627,7 +667,6 @@ export default {
 			this.loading.subadmins = true
 			const userid = this.user.id
 			const gid = group.id
-
 			try {
 				await this.$store.dispatch('addUserSubAdmin', {
 					userid,
@@ -668,6 +707,10 @@ export default {
 		 * @return {string}
 		 */
 		async setUserQuota(quota = 'none') {
+			// Make sure correct label is set for unlimited quota
+			if (quota === 'none') {
+				quota = this.unlimitedQuota
+			}
 			this.loading.quota = true
 			// ensure we only send the preset id
 			quota = quota.id ? quota.id : quota
@@ -689,18 +732,22 @@ export default {
 		/**
 		 * Validate quota string to make sure it's a valid human file size
 		 *
-		 * @param {string} quota Quota in readable format '5 GB'
-		 * @return {Promise|boolean}
+		 * @param {string | object} quota Quota in readable format '5 GB' or Object {id: '5 GB', label: '5GB'}
+		 * @return {object} The validated quota object or unlimited quota if input is invalid
 		 */
 		validateQuota(quota) {
+			if (typeof quota === 'object') {
+				quota = quota?.id || quota.label
+			}
 			// only used for new presets sent through @Tag
 			const validQuota = OC.Util.computerFileSize(quota)
-			if (validQuota !== null && validQuota >= 0) {
+			if (validQuota === null) {
+				return this.unlimitedQuota
+			} else {
 				// unify format output
-				return this.setUserQuota(OC.Util.humanFileSize(OC.Util.computerFileSize(quota)))
+				quota = OC.Util.humanFileSize(OC.Util.computerFileSize(quota))
+				return { id: quota, label: quota }
 			}
-			// if no valid do not change
-			return false
 		},
 
 		/**
@@ -718,10 +765,9 @@ export default {
 					key: 'language',
 					value: lang.code,
 				})
+				this.loading.languages = false
 			} catch (error) {
 				console.error(error)
-			} finally {
-				this.loading.languages = false
 			}
 			return lang
 		},
@@ -744,6 +790,14 @@ export default {
 				})
 		},
 
+		handleDoneButton() {
+			this.editing = false
+			if (this.editedDisplayName !== this.user.displayname) {
+				this.editedDisplayName = this.user.displayname
+			} else if (this.editedMail !== this.user.email) {
+				this.editedMail = this.user.email
+			}
+		},
 	},
 }
 </script>
@@ -752,7 +806,19 @@ export default {
 	.row--menu-opened {
 		z-index: 1 !important;
 	}
-	.row::v-deep .multiselect__single {
-		z-index: auto !important;
-	}
+
+  .row :deep() {
+		.mailAddress,
+		.password,
+		.displayName {
+			.input-field,
+			.input-field__input {
+				height: 48px!important;
+			}
+			.button-vue--icon-only {
+				height: 44px!important;
+			}
+		}
+  }
+
 </style>

+ 82 - 0
cypress/e2e/settings/users.cy.ts

@@ -0,0 +1,82 @@
+/**
+ * @copyright Copyright (c) 2023 Ferdinand Thiessen <opensource@fthiessen.de>
+ *
+ * @author Ferdinand Thiessen <opensource@fthiessen.de>
+ *
+ * @license AGPL-3.0-or-later
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+import { User } from '@nextcloud/cypress'
+
+const admin = new User('admin', 'admin')
+const jdoe = new User('jdoe', 'jdoe')
+
+describe('Setting: Users list', function() {
+	before(function() {
+		cy.createUser(jdoe)
+		cy.login(admin)
+	})
+
+	after(() => {
+		cy.deleteUser(jdoe)
+	})
+
+	it('Can change the password', function() {
+		// open the User settings
+		cy.visit('/settings/users')
+
+		cy.get(`.user-list-grid .row[data-id="${jdoe.userId}"]`).within(($row) => {
+			// see that the list of users contains the user jdoe
+			cy.contains(jdoe.userId).should('exist')
+			// toggle the edit mode for the user jdoe
+			cy.get('.userActions button .icon-rename').click()
+		})
+
+		cy.get(`.user-list-grid .row[data-id="${jdoe.userId}"]`).within(($row) => {
+			// see that the edit mode is on
+			cy.wrap($row).should('have.class', 'row--editable')
+			// see that the password of user0 is ""
+			cy.get('input[type="password"]').should('exist').and('have.value', '')
+			// set the password for user0 to 123456
+			cy.get('input[type="password"]').type('123456')
+			// When I set the password for user0 to 123456
+			cy.get('input[type="password"]').should('have.value', '123456')
+			cy.get('.password button').click()
+
+			// Ignore failure if modal is not shown
+			cy.once('fail', (error) => {
+				expect(error.name).to.equal('AssertionError')
+				expect(error).to.have.property('node', '.modal-container')
+			})
+			// Make sure no confirmation modal is shown
+			cy.root().closest('body').find('.modal-container').then(($modal) => {
+				if ($modal.length > 0) {
+					cy.wrap($modal).find('input[type="password"]').type(admin.password)
+					cy.wrap($modal).find('button').contains('Confirm').click()
+				}
+			})
+
+			// see that the password cell for user user0 is done loading
+			cy.get('.user-row-text-field.icon-loading-small').should('exist')
+			cy.waitUntil(() => cy.get('.user-row-text-field.icon-loading-small').should('not.exist'), { timeout: 10000 })
+			// password input is emptied on change
+			cy.get('input[type="password"]').should('have.value', '')
+		})
+		// Success message is shown
+		cy.get('.toastify.toast-success').contains(/Password.+successfully.+changed/i).should('exist')
+	})
+})

+ 1 - 1
cypress/support/commands.ts

@@ -185,4 +185,4 @@ Cypress.Commands.add('resetUserTheming', (user?: User) => {
 
 Cypress.Commands.add('runOccCommand', (command: string) => {
 	cy.exec(`docker exec --user www-data nextcloud-cypress-tests-server php ./occ ${command}`)
-})
+})

File diff suppressed because it is too large
+ 0 - 0
dist/settings-users-8351.js


+ 0 - 2
dist/settings-users-8351.js.LICENSE.txt

@@ -14,8 +14,6 @@
 
 /*! For license information please see NcCounterBubble.js.LICENSE.txt */
 
-/*! For license information please see NcMultiselect.js.LICENSE.txt */
-
 /**
  * @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
  *

File diff suppressed because it is too large
+ 0 - 0
dist/settings-users-8351.js.map


File diff suppressed because it is too large
+ 0 - 0
dist/settings-vue-settings-apps-users-management.js


File diff suppressed because it is too large
+ 0 - 0
dist/settings-vue-settings-apps-users-management.js.map


+ 2 - 3
tests/acceptance/features/bootstrap/UsersSettingsContext.php

@@ -117,8 +117,7 @@ class UsersSettingsContext implements Context, ActorAwareInterface {
 	 * @return Locator
 	 */
 	public static function optionInInputForUser($cell, $user) {
-		return Locator::forThe()->css(".multiselect__option--highlight")->
-			descendantOf(self::classCellForUser($cell, $user))->
+		return Locator::forThe()->css(".vs__dropdown-option--highlight")->
 			describedAs("Selected $cell option in $cell input for user $user in Users Settings");
 	}
 
@@ -152,7 +151,7 @@ class UsersSettingsContext implements Context, ActorAwareInterface {
 	 * @return Locator
 	 */
 	public static function selectedSelectOption($cell, $user) {
-		return Locator::forThe()->css(".multiselect__single")->
+		return Locator::forThe()->css(".vs__selected .name-parts")->
 			descendantOf(self::classCellForUser($cell, $user))->
 			describedAs("The selected option of the $cell select for the user $user in Users Settings");
 	}

+ 12 - 12
tests/acceptance/features/users.feature

@@ -126,18 +126,18 @@ Feature: users
 #    And I see that the displayName cell for user user0 is done loading
 #    Then I see that the displayName of user0 is user1
 
-  Scenario: change password
-    Given I act as Jane
-    And I am logged in as the admin
-    And I open the User settings
-    And I see that the list of users contains the user user0
-    When I toggle the edit mode for the user user0
-    Then I see that the edit mode is on for user user0
-    And I see that the password of user0 is ""
-    When I set the password for user0 to 123456
-    And I see that the password cell for user user0 is done loading
-    # password input is emptied on change
-    Then I see that the password of user0 is ""
+#  Scenario: change password
+#    Given I act as Jane
+#    And I am logged in as the admin
+#    And I open the User settings
+#    And I see that the list of users contains the user user0
+#    When I toggle the edit mode for the user user0
+#    Then I see that the edit mode is on for user user0
+#    And I see that the password of user0 is ""
+#    When I set the password for user0 to 123456
+#    And I see that the password cell for user user0 is done loading
+#    # password input is emptied on change
+#    Then I see that the password of user0 is ""
 
 #  Scenario: change email
 #    Given I act as Jane

Some files were not shown because too many files changed in this diff