Statistically significant A/B testing results should be color coded in details view
[WebKit.git] / Websites / perf.webkit.org / public / v3 / components / customizable-test-group-form.js
1
2 class CustomizableTestGroupForm extends TestGroupForm {
3
4     constructor()
5     {
6         super('customizable-test-group-form');
7         this._rootSetMap = null;
8         this._disabled = true;
9         this._renderedRepositorylist = null;
10         this._customized = false;
11         this.content().querySelector('a').onclick = this._customize.bind(this);
12     }
13
14     setRootSetMap(map)
15     {
16         this._rootSetMap = map;
17         this._customized = false;
18         this.setDisabled(!map);
19     }
20
21     _submitted()
22     {
23         if (this._startCallback)
24             this._startCallback(this.content().querySelector('.name').value, this._repetitionCount, this._computeRootSetMap());
25     }
26
27     _customize(event)
28     {
29         event.preventDefault();
30         this._customized = true;
31         this.render();
32     }
33
34     _computeRootSetMap()
35     {
36         console.assert(this._rootSetMap);
37         if (!this._customized)
38             return this._rootSetMap;
39
40         console.assert(this._renderedRepositorylist);
41         var map = {};
42         for (var label in this._rootSetMap) {
43             var customRootSet = new CustomRootSet;
44             for (var repository of this._renderedRepositorylist) {
45                 var id = CustomizableTestGroupForm._idForLabelAndRepository(label, repository);
46                 var revision = this.content().getElementById(id).value;
47                 console.assert(revision);
48                 if (revision)
49                     customRootSet.setRevisionForRepository(repository, revision);
50             }
51             map[label] = customRootSet;
52         }
53         return map;
54     }
55
56     render()
57     {
58         super.render();
59         this.content().querySelector('.customize-link').style.display = this._disabled ? 'none' : null;
60
61         if (!this._customized) {
62             this.renderReplace(this.content().querySelector('.custom-table-container'), []);
63             return;
64         }
65         var map = this._rootSetMap;
66         console.assert(map);
67
68         var repositorySet = new Set;
69         var rootSetLabels = [];
70         for (var label in map) {
71             for (var repository of map[label].repositories())
72                 repositorySet.add(repository);
73             rootSetLabels.push(label);
74         }
75
76         this._renderedRepositorylist = Repository.sortByNamePreferringOnesWithURL(Array.from(repositorySet.values()));
77
78         var element = ComponentBase.createElement;
79         this.renderReplace(this.content().querySelector('.custom-table-container'),
80             element('table', {class: 'custom-table'}, [
81                 element('thead',
82                     element('tr',
83                         [element('td', 'Repository'), rootSetLabels.map(function (label) {
84                             return element('td', {colspan: rootSetLabels.length + 1}, label);
85                         })])),
86                 element('tbody',
87                     this._renderedRepositorylist.map(function (repository) {
88                         var cells = [element('th', repository.label())];
89                         for (var label in map)
90                             cells.push(CustomizableTestGroupForm._constructRevisionRadioButtons(map, repository, label));
91                         return element('tr', cells);
92                     }))]));
93     }
94
95     static _idForLabelAndRepository(label, repository) { return label + '-' + repository.id(); }
96
97     static _constructRevisionRadioButtons(rootSetMap, repository, rowLabel)
98     {
99         var id = this._idForLabelAndRepository(rowLabel, repository);
100         var groupName = id + '-group';
101         var element = ComponentBase.createElement;
102         var revisionEditor = element('input', {id: id});
103
104         var nodes = [];
105         for (var labelToChoose in rootSetMap) {
106             var commit = rootSetMap[labelToChoose].commitForRepository(repository);
107             var checked = labelToChoose == rowLabel;
108             var radioButton = this._createRadioButton(groupName, revisionEditor, commit, checked);
109             if (checked)
110                 revisionEditor.value = commit ? commit.revision() : '';
111             nodes.push(element('td', element('label', [radioButton, labelToChoose])));
112         }
113         nodes.push(element('td', revisionEditor));
114
115         return nodes;
116     }
117
118     static _createRadioButton(groupName, revisionEditor, commit, checked)
119     {
120         var button = ComponentBase.createElement('input', {
121             type: 'radio',
122             name: groupName + '-radio',
123             onchange: function () { revisionEditor.value = commit ? commit.revision() : ''; },
124         });
125         if (checked) // FIXME: createElement should be able to set boolean attribute properly.
126             button.checked = true;
127         return button;
128     }
129
130     static cssTemplate()
131     {
132         return `
133             .customize-link {
134                 color: #333;
135             }
136
137             .customize-link a {
138                 color: inherit;
139             }
140
141             .custom-table {
142                 margin: 1rem 0;
143             }
144
145             .custom-table,
146             .custom-table td,
147             .custom-table th {
148                 font-weight: inherit;
149                 border-collapse: collapse;
150                 border-top: solid 1px #ddd;
151                 border-bottom: solid 1px #ddd;
152                 padding: 0.4rem 0.2rem;
153                 font-size: 0.9rem;
154             }
155
156             .custom-table thead td,
157             .custom-table th {
158                 text-align: center;
159             }
160             `;
161     }
162
163     static formContent()
164     {
165         return `
166             <input class="name" type="text" placeholder="Test group name">
167             ${super.formContent()}
168             <span class="customize-link">(<a href="">Customize</a>)</span>
169             <div class="custom-table-container"></div>
170         `;
171     }
172 }
173
174 ComponentBase.defineElement('customizable-test-group-form', CustomizableTestGroupForm);