Convert gr-group to lit
Change-Id: I5e79bba6190d01a30d791e08cb9b1125bc6471a3
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.js b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.js
index cf0fdd4..6bd1ac5 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.js
@@ -505,7 +505,7 @@
suite('groups', () => {
let getGroupConfigStub;
setup(() => {
- stub('gr-group', '_loadGroup').callsFake(() => Promise.resolve({}));
+ stub('gr-group', 'loadGroup').callsFake(() => Promise.resolve({}));
stub('gr-group-members', '_loadGroupDetails').callsFake(() => {});
getGroupConfigStub = stubRestApi('getGroupConfig');
diff --git a/polygerrit-ui/app/elements/admin/gr-group/gr-group.ts b/polygerrit-ui/app/elements/admin/gr-group/gr-group.ts
index 9fde66c..d9bd304 100644
--- a/polygerrit-ui/app/elements/admin/gr-group/gr-group.ts
+++ b/polygerrit-ui/app/elements/admin/gr-group/gr-group.ts
@@ -16,35 +16,26 @@
*/
import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea';
-import '../../../styles/gr-font-styles';
-import '../../../styles/gr-form-styles';
-import '../../../styles/gr-subpage-styles';
-import '../../../styles/shared-styles';
import '../../shared/gr-autocomplete/gr-autocomplete';
import '../../shared/gr-button/gr-button';
import '../../shared/gr-copy-clipboard/gr-copy-clipboard';
import '../../shared/gr-select/gr-select';
-import {GrAutocomplete} from '../../shared/gr-autocomplete/gr-autocomplete';
-import {GrButton} from '../../shared/gr-button/gr-button';
-import {GrCopyClipboard} from '../../shared/gr-copy-clipboard/gr-copy-clipboard';
-import {GrSelect} from '../../shared/gr-select/gr-select';
-import {PolymerElement} from '@polymer/polymer/polymer-element';
-import {htmlTemplate} from './gr-group_html';
-import {customElement, property, observe} from '@polymer/decorators';
import {
AutocompleteSuggestion,
AutocompleteQuery,
} from '../../shared/gr-autocomplete/gr-autocomplete';
import {GroupId, GroupInfo, GroupName} from '../../../types/common';
-import {
- fireEvent,
- firePageError,
- fireTitleChange,
-} from '../../../utils/event-util';
+import {firePageError, fireTitleChange} from '../../../utils/event-util';
import {appContext} from '../../../services/app-context';
import {ErrorCallback} from '../../../api/rest';
import {convertToString} from '../../../utils/string-util';
import {BindValueChangeEvent} from '../../../types/events';
+import {fontStyles} from '../../../styles/gr-font-styles';
+import {formStyles} from '../../../styles/gr-form-styles';
+import {sharedStyles} from '../../../styles/shared-styles';
+import {subpageStyles} from '../../../styles/gr-subpage-styles';
+import {LitElement, css, html} from 'lit';
+import {customElement, property, state} from 'lit/decorators';
const INTERNAL_GROUP_REGEX = /^[\da-f]{40}$/;
@@ -59,22 +50,6 @@
},
};
-export interface GrGroup {
- $: {
- loading: HTMLDivElement;
- loadedContent: HTMLDivElement;
- visibleToAll: GrSelect;
- inputUpdateNameBtn: GrButton;
- Title: HTMLHeadingElement;
- groupNameInput: GrAutocomplete;
- groupName: HTMLHeadingElement;
- groupOwnerInput: GrAutocomplete;
- groupOwner: HTMLHeadingElement;
- inputUpdateOwnerBtn: GrButton;
- uuid: GrCopyClipboard;
- };
-}
-
export interface GroupNameChangedDetail {
name: GroupName;
external: boolean;
@@ -91,72 +66,249 @@
}
@customElement('gr-group')
-export class GrGroup extends PolymerElement {
- static get template() {
- return htmlTemplate;
- }
-
+export class GrGroup extends LitElement {
/**
* Fired when the group name changes.
*
* @event name-changed
*/
+ private readonly query: AutocompleteQuery;
+
@property({type: String})
groupId?: GroupId;
- @property({type: Boolean})
- _rename = false;
+ @state() private originalOwnerName?: string;
- @property({type: Boolean})
- _groupIsInternal = false;
+ @state() private originalDescriptionName?: string;
- @property({type: Boolean})
- _description = false;
+ @state() private originalOptionsVisibleToAll?: boolean;
- @property({type: Boolean})
- _owner = false;
+ @state() private submitTypes = Object.values(OPTIONS);
- @property({type: Boolean})
- _options = false;
+ /* private but used in test */
+ @state() isAdmin = false;
- @property({type: Boolean})
- _loading = true;
+ /* private but used in test */
+ @state() groupOwner = false;
- @property({type: Object})
- _groupConfig?: GroupInfo;
+ /* private but used in test */
+ @state() groupIsInternal = false;
- @property({type: String})
- _groupConfigOwner?: string;
+ /* private but used in test */
+ @state() loading = true;
- @property({type: Object})
- _groupName?: GroupName;
+ /* private but used in test */
+ @state() groupConfig?: GroupInfo;
- @property({type: Boolean})
- _groupOwner = false;
+ /* private but used in test */
+ @state() groupConfigOwner?: string;
- @property({type: Array})
- _submitTypes = Object.values(OPTIONS);
-
- @property({type: Object})
- _query: AutocompleteQuery;
-
- @property({type: Boolean})
- _isAdmin = false;
+ /* private but used in test */
+ @state() originalName?: GroupName;
private readonly restApiService = appContext.restApiService;
constructor() {
super();
- this._query = (input: string) => this._getGroupSuggestions(input);
+ this.query = (input: string) => this.getGroupSuggestions(input);
}
override connectedCallback() {
super.connectedCallback();
- this._loadGroup();
+ this.loadGroup();
}
- _loadGroup() {
+ static override get styles() {
+ return [
+ fontStyles,
+ formStyles,
+ sharedStyles,
+ subpageStyles,
+ css`
+ h3.edited:after {
+ color: var(--deemphasized-text-color);
+ content: ' *';
+ }
+ `,
+ ];
+ }
+
+ override render() {
+ return html`
+ <div class="main gr-form-styles read-only">
+ <div id="loading" class="${this.computeLoadingClass()}">Loading...</div>
+ <div id="loadedContent" class="${this.computeLoadingClass()}">
+ <h1 id="Title" class="heading-1">
+ ${convertToString(this.originalName)}
+ </h1>
+ <h2 id="configurations" class="heading-2">General</h2>
+ <div id="form">
+ <fieldset>
+ ${this.renderGroupUUID()} ${this.renderGroupName()}
+ ${this.renderGroupOwner()} ${this.renderGroupDescription()}
+ ${this.renderGroupOptions()}
+ </fieldset>
+ </div>
+ </div>
+ </div>
+ `;
+ }
+
+ private renderGroupUUID() {
+ return html`
+ <h3 id="groupUUID" class="heading-3">Group UUID</h3>
+ <fieldset>
+ <gr-copy-clipboard
+ id="uuid"
+ .text=${this.getGroupUUID()}
+ ></gr-copy-clipboard>
+ </fieldset>
+ `;
+ }
+
+ private renderGroupName() {
+ const groupNameEdited = this.originalName !== this.groupConfig?.name;
+ return html`
+ <h3
+ id="groupName"
+ class="heading-3 ${this.computeHeaderClass(groupNameEdited)}"
+ >
+ Group Name
+ </h3>
+ <fieldset>
+ <span class="value">
+ <gr-autocomplete
+ id="groupNameInput"
+ .text=${convertToString(this.groupConfig?.name)}
+ ?disabled=${this.computeGroupDisabled()}
+ @text-changed=${this.handleNameTextChanged}
+ ></gr-autocomplete>
+ </span>
+ <span class="value" ?disabled=${this.computeGroupDisabled()}>
+ <gr-button
+ id="inputUpdateNameBtn"
+ ?disabled=${!groupNameEdited}
+ @click=${this.handleSaveName}
+ >
+ Rename Group</gr-button
+ >
+ </span>
+ </fieldset>
+ `;
+ }
+
+ private renderGroupOwner() {
+ const groupOwnerNameEdited =
+ this.originalOwnerName !== this.groupConfig?.owner;
+ return html`
+ <h3
+ id="groupOwner"
+ class="heading-3 ${this.computeHeaderClass(groupOwnerNameEdited)}"
+ >
+ Owners
+ </h3>
+ <fieldset>
+ <span class="value">
+ <gr-autocomplete
+ id="groupOwnerInput"
+ .text=${convertToString(this.groupConfig?.owner)}
+ .value=${convertToString(this.groupConfigOwner)}
+ .query=${this.query}
+ ?disabled=${this.computeGroupDisabled()}
+ @text-changed=${this.handleOwnerTextChanged}
+ @value-changed=${this.handleOwnerValueChanged}
+ >
+ </gr-autocomplete>
+ </span>
+ <span class="value" ?disabled=${this.computeGroupDisabled()}>
+ <gr-button
+ id="inputUpdateOwnerBtn"
+ ?disabled=${!groupOwnerNameEdited}
+ @click=${this.handleSaveOwner}
+ >
+ Change Owners</gr-button
+ >
+ </span>
+ </fieldset>
+ `;
+ }
+
+ private renderGroupDescription() {
+ const groupDescriptionEdited =
+ this.originalDescriptionName !== this.groupConfig?.description;
+ return html`
+ <h3 class="heading-3 ${this.computeHeaderClass(groupDescriptionEdited)}">
+ Description
+ </h3>
+ <fieldset>
+ <div>
+ <iron-autogrow-textarea
+ class="description"
+ autocomplete="on"
+ ?disabled=${this.computeGroupDisabled()}
+ .bindValue=${convertToString(this.groupConfig?.description)}
+ @bind-value-changed=${this.handleDescriptionBindValueChanged}
+ ></iron-autogrow-textarea>
+ </div>
+ <span class="value" ?disabled=${this.computeGroupDisabled()}>
+ <gr-button
+ ?disabled=${!groupDescriptionEdited}
+ @click=${this.handleSaveDescription}
+ >
+ Save Description
+ </gr-button>
+ </span>
+ </fieldset>
+ `;
+ }
+
+ private renderGroupOptions() {
+ const groupOptionsEdited =
+ this.originalOptionsVisibleToAll !==
+ this.groupConfig?.options?.visible_to_all;
+ return html`
+ <h3
+ id="options"
+ class="heading-3 ${this.computeHeaderClass(groupOptionsEdited)}"
+ >
+ Group Options
+ </h3>
+ <fieldset>
+ <section>
+ <span class="title">
+ Make group visible to all registered users
+ </span>
+ <span class="value">
+ <gr-select
+ id="visibleToAll"
+ .bindValue="${this.groupConfig?.options?.visible_to_all}"
+ @bind-value-changed=${this.handleOptionsBindValueChanged}
+ >
+ <select ?disabled=${this.computeGroupDisabled()}>
+ ${this.submitTypes.map(
+ item => html`
+ <option value=${item.value}>${item.label}</option>
+ `
+ )}
+ </select>
+ </gr-select>
+ </span>
+ </section>
+ <span class="value" ?disabled=${this.computeGroupDisabled()}>
+ <gr-button
+ ?disabled=${!groupOptionsEdited}
+ @click=${this.handleSaveOptions}
+ >
+ Save Group Options
+ </gr-button>
+ </span>
+ </fieldset>
+ `;
+ }
+
+ /* private but used in test */
+ async loadGroup() {
if (!this.groupId) {
return;
}
@@ -167,154 +319,127 @@
firePageError(response);
};
- return this.restApiService
- .getGroupConfig(this.groupId, errFn)
- .then(config => {
- if (!config || !config.name) {
- return Promise.resolve();
- }
+ const config = await this.restApiService.getGroupConfig(
+ this.groupId,
+ errFn
+ );
+ if (!config || !config.name) return;
- this._groupName = config.name;
- this._groupIsInternal = !!config.id.match(INTERNAL_GROUP_REGEX);
+ if (config.description === undefined) {
+ config.description = '';
+ }
- promises.push(
- this.restApiService.getIsAdmin().then(isAdmin => {
- this._isAdmin = !!isAdmin;
- })
- );
+ this.originalName = config.name;
+ this.originalOwnerName = config.owner;
+ this.originalDescriptionName = config.description;
+ this.groupIsInternal = !!config.id.match(INTERNAL_GROUP_REGEX);
- promises.push(
- this.restApiService.getIsGroupOwner(config.name).then(isOwner => {
- this._groupOwner = !!isOwner;
- })
- );
+ promises.push(
+ this.restApiService.getIsAdmin().then(isAdmin => {
+ this.isAdmin = !!isAdmin;
+ })
+ );
- // If visible to all is undefined, set to false. If it is defined
- // as false, setting to false is fine. If any optional values
- // are added with a default of true, then this would need to be an
- // undefined check and not a truthy/falsy check.
- if (config.options && !config.options.visible_to_all) {
- config.options.visible_to_all = false;
- }
- this._groupConfig = config;
+ promises.push(
+ this.restApiService.getIsGroupOwner(config.name).then(isOwner => {
+ this.groupOwner = !!isOwner;
+ })
+ );
- fireTitleChange(this, config.name);
+ // If visible to all is undefined, set to false. If it is defined
+ // as false, setting to false is fine. If any optional values
+ // are added with a default of true, then this would need to be an
+ // undefined check and not a truthy/falsy check.
+ if (config.options && !config.options.visible_to_all) {
+ config.options.visible_to_all = false;
+ }
+ this.groupConfig = config;
+ this.originalOptionsVisibleToAll = config?.options?.visible_to_all;
- return Promise.all(promises).then(() => {
- this._loading = false;
- });
- });
+ fireTitleChange(this, config.name);
+
+ await Promise.all(promises);
+ this.loading = false;
}
- _computeLoadingClass(loading: boolean) {
- return loading ? 'loading' : '';
+ /* private but used in test */
+ computeLoadingClass() {
+ return this.loading ? 'loading' : '';
}
- _isLoading() {
- return this._loading || this._loading === undefined;
- }
-
- _handleSaveName() {
- const groupConfig = this._groupConfig;
+ /* private but used in test */
+ async handleSaveName() {
+ const groupConfig = this.groupConfig;
if (!this.groupId || !groupConfig || !groupConfig.name) {
return Promise.reject(new Error('invalid groupId or config name'));
}
const groupName = groupConfig.name;
- return this.restApiService
- .saveGroupName(this.groupId, groupName)
- .then(config => {
- if (config.status === 200) {
- this._groupName = groupName;
- const detail: GroupNameChangedDetail = {
- name: groupName,
- external: !this._groupIsInternal,
- };
- fireEvent(this, 'name-changed');
- this.dispatchEvent(
- new CustomEvent('name-changed', {
- detail,
- composed: true,
- bubbles: true,
- })
- );
- this._rename = false;
- }
- });
+ const config = await this.restApiService.saveGroupName(
+ this.groupId,
+ groupName
+ );
+ if (config.status === 200) {
+ this.originalName = groupName;
+ const detail: GroupNameChangedDetail = {
+ name: groupName,
+ external: !this.groupIsInternal,
+ };
+ this.dispatchEvent(
+ new CustomEvent('name-changed', {
+ detail,
+ composed: true,
+ bubbles: true,
+ })
+ );
+ this.requestUpdate();
+ }
+
+ return;
}
- _handleSaveOwner() {
- if (!this.groupId || !this._groupConfig) return;
- let owner = this._groupConfig.owner;
- if (this._groupConfigOwner) {
- owner = decodeURIComponent(this._groupConfigOwner);
+ /* private but used in test */
+ async handleSaveOwner() {
+ if (!this.groupId || !this.groupConfig) return;
+ let owner = this.groupConfig.owner;
+ if (this.groupConfigOwner) {
+ owner = decodeURIComponent(this.groupConfigOwner);
}
if (!owner) return;
- return this.restApiService.saveGroupOwner(this.groupId, owner).then(() => {
- this._owner = false;
- });
+ await this.restApiService.saveGroupOwner(this.groupId, owner);
+ this.originalOwnerName = this.groupConfig?.owner;
+ this.groupConfigOwner = undefined;
}
- _handleSaveDescription() {
- if (!this.groupId || !this._groupConfig || !this._groupConfig.description)
+ /* private but used in test */
+ async handleSaveDescription() {
+ if (
+ !this.groupId ||
+ !this.groupConfig ||
+ this.groupConfig.description === undefined
+ )
return;
- return this.restApiService
- .saveGroupDescription(this.groupId, this._groupConfig.description)
- .then(() => {
- this._description = false;
- });
+ await this.restApiService.saveGroupDescription(
+ this.groupId,
+ this.groupConfig.description
+ );
+ this.originalDescriptionName = this.groupConfig.description;
}
- _handleSaveOptions() {
- if (!this.groupId || !this._groupConfig || !this._groupConfig.options)
- return;
- const visible = this._groupConfig.options.visible_to_all;
-
+ /* private but used in test */
+ async handleSaveOptions() {
+ if (!this.groupId || !this.groupConfig || !this.groupConfig.options) return;
+ const visible = this.groupConfig.options.visible_to_all;
const options = {visible_to_all: visible};
-
- return this.restApiService
- .saveGroupOptions(this.groupId, options)
- .then(() => {
- this._options = false;
- });
+ await this.restApiService.saveGroupOptions(this.groupId, options);
+ this.originalOptionsVisibleToAll =
+ this.groupConfig?.options?.visible_to_all;
}
- @observe('_groupConfig.name')
- _handleConfigName() {
- if (this._isLoading()) {
- return;
- }
- this._rename = true;
- }
-
- @observe('_groupConfig.owner', '_groupConfigOwner')
- _handleConfigOwner() {
- if (this._isLoading()) {
- return;
- }
- this._owner = true;
- }
-
- @observe('_groupConfig.description')
- _handleConfigDescription() {
- if (this._isLoading()) {
- return;
- }
- this._description = true;
- }
-
- @observe('_groupConfig.options.visible_to_all')
- _handleConfigOptions() {
- if (this._isLoading()) {
- return;
- }
- this._options = true;
- }
-
- _computeHeaderClass(configChanged: boolean) {
+ private computeHeaderClass(configChanged: boolean) {
return configChanged ? 'edited' : '';
}
- _getGroupSuggestions(input: string) {
+ private getGroupSuggestions(input: string) {
return this.restApiService.getSuggestedGroups(input).then(response => {
const groups: AutocompleteSuggestion[] = [];
for (const [name, group] of Object.entries(response ?? {})) {
@@ -324,37 +449,45 @@
});
}
- _computeGroupDisabled(
- owner: boolean,
- admin: boolean,
- groupIsInternal: boolean
- ) {
- return !(groupIsInternal && (admin || owner));
+ /* private but used in test */
+ computeGroupDisabled() {
+ return !(this.groupIsInternal && (this.isAdmin || this.groupOwner));
}
- _getGroupUUID(id: GroupId) {
+ private getGroupUUID() {
+ const id = this.groupConfig?.id;
if (!id) return;
-
return id.match(INTERNAL_GROUP_REGEX) ? id : decodeURIComponent(id);
}
- handleNameTextChanged(e: CustomEvent) {
- this.set('_groupConfig.name', e.detail.value as GroupName);
+ private handleNameTextChanged(e: CustomEvent) {
+ if (!this.groupConfig || this.loading) return;
+ this.groupConfig.name = e.detail.value as GroupName;
+ this.requestUpdate();
}
- handleOwnerTextChanged(e: CustomEvent) {
- this.set('_groupConfig.owner', e.detail.value);
+ private handleOwnerTextChanged(e: CustomEvent) {
+ if (!this.groupConfig || this.loading) return;
+ this.groupConfig.owner = e.detail.value;
+ this.requestUpdate();
}
- handleOwnerValueChanged(e: CustomEvent) {
- this._groupConfigOwner = e.detail.value;
+ private handleOwnerValueChanged(e: CustomEvent) {
+ if (this.loading) return;
+ this.groupConfigOwner = e.detail.value;
+ this.requestUpdate();
}
- handleDescriptionBindValueChanged(e: BindValueChangeEvent) {
- this.set('_groupConfig.description', e.detail.value);
+ private handleDescriptionBindValueChanged(e: BindValueChangeEvent) {
+ if (!this.groupConfig || this.loading) return;
+ this.groupConfig.description = e.detail.value;
+ this.requestUpdate();
}
- convertToString(value?: unknown) {
- return convertToString(value);
+ private handleOptionsBindValueChanged(e: BindValueChangeEvent) {
+ if (!this.groupConfig || !this.groupConfig.options || this.loading) return;
+ this.groupConfig.options.visible_to_all = e.detail
+ .value as unknown as boolean;
+ this.requestUpdate();
}
}
diff --git a/polygerrit-ui/app/elements/admin/gr-group/gr-group_html.ts b/polygerrit-ui/app/elements/admin/gr-group/gr-group_html.ts
deleted file mode 100644
index 6f27dfd..0000000
--- a/polygerrit-ui/app/elements/admin/gr-group/gr-group_html.ts
+++ /dev/null
@@ -1,174 +0,0 @@
-/**
- * @license
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import {html} from '@polymer/polymer/lib/utils/html-tag';
-
-export const htmlTemplate = html`
- <style include="gr-font-styles">
- /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
- </style>
- <style include="shared-styles">
- /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
- </style>
- <style include="gr-subpage-styles">
- h3.edited:after {
- color: var(--deemphasized-text-color);
- content: ' *';
- }
- </style>
- <style include="gr-form-styles">
- /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
- </style>
- <div class="main gr-form-styles read-only">
- <div id="loading" class$="[[_computeLoadingClass(_loading)]]">
- Loading...
- </div>
- <div id="loadedContent" class$="[[_computeLoadingClass(_loading)]]">
- <h1 id="Title" class="heading-1">[[convertToString(_groupName)]]</h1>
- <h2 id="configurations" class="heading-2">General</h2>
- <div id="form">
- <fieldset>
- <h3 id="groupUUID" class="heading-3">Group UUID</h3>
- <fieldset>
- <gr-copy-clipboard
- id="uuid"
- text="[[_getGroupUUID(_groupConfig.id)]]"
- ></gr-copy-clipboard>
- </fieldset>
- <h3
- id="groupName"
- class$="heading-3 [[_computeHeaderClass(_rename)]]"
- >
- Group Name
- </h3>
- <fieldset>
- <span class="value">
- <gr-autocomplete
- id="groupNameInput"
- text="[[convertToString(_groupConfig.name)]]"
- disabled="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]"
- on-text-changed="handleNameTextChanged"
- ></gr-autocomplete>
- </span>
- <span
- class="value"
- disabled$="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]"
- >
- <gr-button
- id="inputUpdateNameBtn"
- on-click="_handleSaveName"
- disabled="[[!_rename]]"
- >
- Rename Group</gr-button
- >
- </span>
- </fieldset>
- <h3
- id="groupOwner"
- class$="heading-3 [[_computeHeaderClass(_owner)]]"
- >
- Owners
- </h3>
- <fieldset>
- <span class="value">
- <gr-autocomplete
- id="groupOwnerInput"
- text="[[convertToString(_groupConfig.owner)]]"
- value="[[convertToString(_groupConfigOwner)]]"
- query="[[_query]]"
- disabled="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]"
- on-text-changed="handleOwnerTextChanged"
- on-value-changed="handleOwnerValueChanged"
- >
- </gr-autocomplete>
- </span>
- <span
- class="value"
- disabled$="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]"
- >
- <gr-button
- id="inputUpdateOwnerBtn"
- on-click="_handleSaveOwner"
- disabled="[[!_owner]]"
- >
- Change Owners</gr-button
- >
- </span>
- </fieldset>
- <h3 class$="heading-3 [[_computeHeaderClass(_description)]]">
- Description
- </h3>
- <fieldset>
- <div>
- <iron-autogrow-textarea
- class="description"
- autocomplete="on"
- bind-value="[[convertToString(_groupConfig.description)]]"
- disabled="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]"
- on-bind-value-changed="handleDescriptionBindValueChanged"
- ></iron-autogrow-textarea>
- </div>
- <span
- class="value"
- disabled$="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]"
- >
- <gr-button
- on-click="_handleSaveDescription"
- disabled="[[!_description]]"
- >
- Save Description
- </gr-button>
- </span>
- </fieldset>
- <h3 id="options" class$="heading-3 [[_computeHeaderClass(_options)]]">
- Group Options
- </h3>
- <fieldset>
- <section>
- <span class="title">
- Make group visible to all registered users
- </span>
- <span class="value">
- <gr-select
- id="visibleToAll"
- bind-value="{{_groupConfig.options.visible_to_all}}"
- >
- <select
- disabled$="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]"
- >
- <template is="dom-repeat" items="[[_submitTypes]]">
- <option value="[[convertToString(item.value)]]">
- [[item.label]]
- </option>
- </template>
- </select>
- </gr-select>
- </span>
- </section>
- <span
- class="value"
- disabled$="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]"
- >
- <gr-button on-click="_handleSaveOptions" disabled="[[!_options]]">
- Save Group Options
- </gr-button>
- </span>
- </fieldset>
- </fieldset>
- </div>
- </div>
- </div>
-`;
diff --git a/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.ts b/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.ts
index 74e2364..5e96e33 100644
--- a/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.ts
+++ b/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.ts
@@ -21,10 +21,15 @@
import {
addListenerForTest,
mockPromise,
+ queryAndAssert,
stubRestApi,
} from '../../../test/test-utils';
import {createGroupInfo} from '../../../test/test-data-generators.js';
import {GroupId, GroupInfo, GroupName} from '../../../types/common';
+import {GrAutocomplete} from '../../shared/gr-autocomplete/gr-autocomplete';
+import {GrButton} from '../../shared/gr-button/gr-button';
+import {GrCopyClipboard} from '../../shared/gr-copy-clipboard/gr-copy-clipboard';
+import {GrSelect} from '../../shared/gr-select/gr-select';
const basicFixture = fixtureFromElement('gr-group');
@@ -45,24 +50,43 @@
name: 'Administrators' as GroupName,
};
- setup(() => {
+ setup(async () => {
element = basicFixture.instantiate();
+ await element.updateComplete;
groupStub = stubRestApi('getGroupConfig').returns(Promise.resolve(group));
});
test('loading displays before group config is loaded', () => {
- assert.isTrue(element.$.loading.classList.contains('loading'));
- assert.isFalse(getComputedStyle(element.$.loading).display === 'none');
- assert.isTrue(element.$.loadedContent.classList.contains('loading'));
- assert.isTrue(getComputedStyle(element.$.loadedContent).display === 'none');
+ assert.isTrue(
+ queryAndAssert<HTMLDivElement>(element, '#loading').classList.contains(
+ 'loading'
+ )
+ );
+ assert.isFalse(
+ getComputedStyle(queryAndAssert<HTMLDivElement>(element, '#loading'))
+ .display === 'none'
+ );
+ assert.isTrue(
+ queryAndAssert<HTMLDivElement>(
+ element,
+ '#loadedContent'
+ ).classList.contains('loading')
+ );
+ assert.isTrue(
+ getComputedStyle(
+ queryAndAssert<HTMLDivElement>(element, '#loadedContent')
+ ).display === 'none'
+ );
});
test('default values are populated with internal group', async () => {
stubRestApi('getIsGroupOwner').returns(Promise.resolve(true));
element.groupId = '1' as GroupId;
- await element._loadGroup();
- assert.isTrue(element._groupIsInternal);
- assert.isFalse(element.$.visibleToAll.bindValue);
+ await element.loadGroup();
+ assert.isTrue(element.groupIsInternal);
+ assert.isFalse(
+ queryAndAssert<GrSelect>(element, '#visibleToAll').bindValue
+ );
});
test('default values with external group', async () => {
@@ -74,71 +98,102 @@
);
stubRestApi('getIsGroupOwner').returns(Promise.resolve(true));
element.groupId = '1' as GroupId;
- await element._loadGroup();
- assert.isFalse(element._groupIsInternal);
- assert.isFalse(element.$.visibleToAll.bindValue);
+ await element.loadGroup();
+ assert.isFalse(element.groupIsInternal);
+ assert.isFalse(
+ queryAndAssert<GrSelect>(element, '#visibleToAll').bindValue
+ );
});
test('rename group', async () => {
const groupName = 'test-group';
const groupName2 = 'test-group2';
element.groupId = '1' as GroupId;
- element._groupConfig = {
+ element.groupConfig = {
name: groupName as GroupName,
id: '1' as GroupId,
};
- element._groupName = groupName as GroupName;
+ element.originalName = groupName as GroupName;
stubRestApi('getIsGroupOwner').returns(Promise.resolve(true));
stubRestApi('saveGroupName').returns(
Promise.resolve({...new Response(), status: 200})
);
- const button = element.$.inputUpdateNameBtn;
+ const button = queryAndAssert<GrButton>(element, '#inputUpdateNameBtn');
- await element._loadGroup();
+ await element.loadGroup();
assert.isTrue(button.hasAttribute('disabled'));
- assert.isFalse(element.$.Title.classList.contains('edited'));
+ assert.isFalse(
+ queryAndAssert<HTMLHeadingElement>(element, '#Title').classList.contains(
+ 'edited'
+ )
+ );
- element.$.groupNameInput.text = groupName2;
+ queryAndAssert<GrAutocomplete>(element, '#groupNameInput').text =
+ groupName2;
- await flush();
+ await element.updateComplete;
+
assert.isFalse(button.hasAttribute('disabled'));
- assert.isTrue(element.$.groupName.classList.contains('edited'));
+ assert.isTrue(
+ queryAndAssert<HTMLHeadingElement>(
+ element,
+ '#groupName'
+ ).classList.contains('edited')
+ );
- await element._handleSaveName();
- assert.isTrue(button.hasAttribute('disabled'));
- assert.isFalse(element.$.Title.classList.contains('edited'));
- assert.equal(element._groupName, groupName2);
+ await element.handleSaveName();
+ assert.isTrue(button.disabled);
+ assert.isFalse(
+ queryAndAssert<HTMLHeadingElement>(element, '#Title').classList.contains(
+ 'edited'
+ )
+ );
+ assert.equal(element.originalName, groupName2);
});
test('rename group owner', async () => {
const groupName = 'test-group';
element.groupId = '1' as GroupId;
- element._groupConfig = {
+ element.groupConfig = {
name: groupName as GroupName,
id: '1' as GroupId,
};
- element._groupConfigOwner = 'testId';
- element._groupOwner = true;
+ element.groupConfigOwner = 'testId';
+ element.groupOwner = true;
stubRestApi('getIsGroupOwner').returns(Promise.resolve(true));
- const button = element.$.inputUpdateOwnerBtn;
+ const button = queryAndAssert<GrButton>(element, '#inputUpdateOwnerBtn');
- await element._loadGroup();
- assert.isTrue(button.hasAttribute('disabled'));
- assert.isFalse(element.$.Title.classList.contains('edited'));
+ await element.loadGroup();
+ assert.isTrue(button.disabled);
+ assert.isFalse(
+ queryAndAssert<HTMLHeadingElement>(element, '#Title').classList.contains(
+ 'edited'
+ )
+ );
- element.$.groupOwnerInput.text = 'testId2';
+ queryAndAssert<GrAutocomplete>(element, '#groupOwnerInput').text =
+ 'testId2';
- await flush();
- assert.isFalse(button.hasAttribute('disabled'));
- assert.isTrue(element.$.groupOwner.classList.contains('edited'));
+ await element.updateComplete;
+ assert.isFalse(button.disabled);
+ assert.isTrue(
+ queryAndAssert<HTMLHeadingElement>(
+ element,
+ '#groupOwner'
+ ).classList.contains('edited')
+ );
- await element._handleSaveOwner();
- assert.isTrue(button.hasAttribute('disabled'));
- assert.isFalse(element.$.Title.classList.contains('edited'));
+ await element.handleSaveOwner();
+ assert.isTrue(button.disabled);
+ assert.isFalse(
+ queryAndAssert<HTMLHeadingElement>(element, '#Title').classList.contains(
+ 'edited'
+ )
+ );
});
test('test for undefined group name', async () => {
@@ -154,14 +209,18 @@
// Test that loading shows instead of filling
// in group details
- await element._loadGroup();
- assert.isTrue(element.$.loading.classList.contains('loading'));
+ await element.loadGroup();
+ assert.isTrue(
+ queryAndAssert<HTMLDivElement>(element, '#loading').classList.contains(
+ 'loading'
+ )
+ );
- assert.isTrue(element._loading);
+ assert.isTrue(element.loading);
});
test('test fire event', async () => {
- element._groupConfig = {
+ element.groupConfig = {
name: 'test-group' as GroupName,
id: '1' as GroupId,
};
@@ -171,53 +230,37 @@
);
const showStub = sinon.stub(element, 'dispatchEvent');
- await element._handleSaveName();
+ await element.handleSaveName();
assert.isTrue(showStub.called);
});
- test('_computeGroupDisabled', () => {
- let admin = true;
- let owner = false;
- let groupIsInternal = true;
- assert.equal(
- element._computeGroupDisabled(owner, admin, groupIsInternal),
- false
- );
+ test('computeGroupDisabled', () => {
+ element.isAdmin = true;
+ element.groupOwner = false;
+ element.groupIsInternal = true;
+ assert.equal(element.computeGroupDisabled(), false);
- admin = false;
- assert.equal(
- element._computeGroupDisabled(owner, admin, groupIsInternal),
- true
- );
+ element.isAdmin = false;
+ assert.equal(element.computeGroupDisabled(), true);
- owner = true;
- assert.equal(
- element._computeGroupDisabled(owner, admin, groupIsInternal),
- false
- );
+ element.groupOwner = true;
+ assert.equal(element.computeGroupDisabled(), false);
- owner = false;
- assert.equal(
- element._computeGroupDisabled(owner, admin, groupIsInternal),
- true
- );
+ element.groupOwner = false;
+ assert.equal(element.computeGroupDisabled(), true);
- groupIsInternal = false;
- assert.equal(
- element._computeGroupDisabled(owner, admin, groupIsInternal),
- true
- );
+ element.groupIsInternal = false;
+ assert.equal(element.computeGroupDisabled(), true);
- admin = true;
- assert.equal(
- element._computeGroupDisabled(owner, admin, groupIsInternal),
- true
- );
+ element.isAdmin = true;
+ assert.equal(element.computeGroupDisabled(), true);
});
- test('_computeLoadingClass', () => {
- assert.equal(element._computeLoadingClass(true), 'loading');
- assert.equal(element._computeLoadingClass(false), '');
+ test('computeLoadingClass', () => {
+ element.loading = true;
+ assert.equal(element.computeLoadingClass(), 'loading');
+ element.loading = false;
+ assert.equal(element.computeLoadingClass(), '');
});
test('fires page-error', async () => {
@@ -241,21 +284,31 @@
promise.resolve();
});
- element._loadGroup();
+ await element.loadGroup();
await promise;
});
- test('uuid', () => {
- element._groupConfig = {
+ test('uuid', async () => {
+ element.groupConfig = {
id: '6a1e70e1a88782771a91808c8af9bbb7a9871389' as GroupId,
};
- assert.equal(element._groupConfig.id, element.$.uuid.text);
+ await element.updateComplete;
- element._groupConfig = {
+ assert.equal(
+ element.groupConfig.id,
+ queryAndAssert<GrCopyClipboard>(element, '#uuid').text
+ );
+
+ element.groupConfig = {
id: 'user%2Fgroup' as GroupId,
};
- assert.equal('user/group', element.$.uuid.text);
+ await element.updateComplete;
+
+ assert.equal(
+ 'user/group',
+ queryAndAssert<GrCopyClipboard>(element, '#uuid').text
+ );
});
});