blob: 8da308345f0e21eb4d8ffa341af130c3d2c34434 [file] [log] [blame]
Copybara854996b2021-09-07 19:36:02 +00001// Copyright 2019 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import {LitElement, html, css} from 'lit-element';
6
7import {store, connectStore} from 'reducers/base.js';
8import * as issueV0 from 'reducers/issueV0.js';
9import * as projectV0 from 'reducers/projectV0.js';
10import 'elements/chops/chops-button/chops-button.js';
11import 'elements/chops/chops-dialog/chops-dialog.js';
12import 'elements/framework/mr-error/mr-error.js';
13import {SHARED_STYLES} from 'shared/shared-styles.js';
14
15// TODO(zhangtiff): Make dialog components subclass chops-dialog instead of
16// using slots/containment once we switch to LitElement.
17/**
18 * `<mr-convert-issue>`
19 *
20 * This allows a user to update the structure of an issue to that of
21 * a chosen project template.
22 *
23 */
24export class MrConvertIssue extends connectStore(LitElement) {
25 /** @override */
26 static get styles() {
27 return [
28 SHARED_STYLES,
29 css`
30 label {
31 font-weight: bold;
32 text-align: right;
33 }
34 form {
35 padding: 1em 8px;
36 display: block;
37 font-size: var(--chops-main-font-size);
38 }
39 textarea {
40 font-family: var(--mr-toggled-font-family);
41 min-height: 80px;
42 border: var(--chops-accessible-border);
43 padding: 0.5em 4px;
44 }
45 .edit-actions {
46 width: 100%;
47 margin: 0.5em 0;
48 text-align: right;
49 }
50 `,
51 ];
52 }
53
54 /** @override */
55 render() {
56 return html`
57 <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
58 <chops-dialog closeOnOutsideClick>
59 <h3 class="medium-heading">Convert issue to new template structure</h3>
60 <form id="convertIssueForm">
61 <div class="input-grid">
62 <label for="templateInput">Pick a template: </label>
63 <select id="templateInput" @change=${this._templateInputChanged}>
64 <option value="">--Please choose a project template--</option>
65 ${this.projectTemplates.map((projTempl) => html`
66 <option value=${projTempl.templateName}>
67 ${projTempl.templateName}
68 </option>`)}
69 </select>
70 <label for="commentContent">Comment: </label>
71 <textarea id="commentContent" placeholder="Add a comment"></textarea>
72 <span></span>
73 <chops-checkbox
74 @checked-change=${this._sendEmailChecked}
75 checked=${this.sendEmail}
76 >Send email</chops-checkbox>
77 </div>
78 <mr-error ?hidden=${!this.convertIssueError}>
79 ${this.convertIssueError && this.convertIssueError.description}
80 </mr-error>
81 <div class="edit-actions">
82 <chops-button @click=${this.close} class="de-emphasized discard-button">
83 Discard
84 </chops-button>
85 <chops-button @click=${this.save} class="emphasized" ?disabled=${!this.selectedTemplate}>
86 Convert issue
87 </chops-button>
88 </div>
89 </form>
90 </chops-dialog>
91 `;
92 }
93
94 /** @override */
95 static get properties() {
96 return {
97 convertingIssue: {
98 type: Boolean,
99 },
100 convertIssueError: {
101 type: Object,
102 },
103 issuePermissions: {
104 type: Object,
105 },
106 issueRef: {
107 type: Object,
108 },
109 projectTemplates: {
110 type: Array,
111 },
112 selectedTemplate: {
113 type: String,
114 },
115 sendEmail: {
116 type: Boolean,
117 },
118 };
119 }
120
121 /** @override */
122 stateChanged(state) {
123 this.convertingIssue = issueV0.requests(state).convert.requesting;
124 this.convertIssueError = issueV0.requests(state).convert.error;
125 this.issueRef = issueV0.viewedIssueRef(state);
126 this.issuePermissions = issueV0.permissions(state);
127 this.projectTemplates = projectV0.viewedTemplates(state);
128 }
129
130 /** @override */
131 constructor() {
132 super();
133 this.selectedTemplate = '';
134 this.sendEmail = true;
135 }
136
137 /** @override */
138 updated(changedProperties) {
139 if (changedProperties.has('convertingIssue')) {
140 if (!this.convertingIssue && !this.convertIssueError) {
141 this.close();
142 }
143 }
144 }
145
146 open() {
147 this.reset();
148 const dialog = this.shadowRoot.querySelector('chops-dialog');
149 dialog.open();
150 }
151
152 close() {
153 const dialog = this.shadowRoot.querySelector('chops-dialog');
154 dialog.close();
155 }
156
157 /**
158 * Resets the user's input.
159 */
160 reset() {
161 this.shadowRoot.querySelector('#convertIssueForm').reset();
162 }
163
164 /**
165 * Dispatches a Redux action to convert the issue to a new template.
166 */
167 save() {
168 const commentContent = this.shadowRoot.querySelector('#commentContent');
169 store.dispatch(issueV0.convert(this.issueRef, {
170 templateName: this.selectedTemplate,
171 commentContent: commentContent.value,
172 sendEmail: this.sendEmail,
173 }));
174 }
175
176 _sendEmailChecked(evt) {
177 this.sendEmail = evt.detail.checked;
178 }
179
180 _templateInputChanged() {
181 this.selectedTemplate = this.shadowRoot.querySelector(
182 '#templateInput').value;
183 }
184}
185
186customElements.define('mr-convert-issue', MrConvertIssue);