mirror of
git://git.yoctoproject.org/yocto-autobuilder2.git
synced 2025-07-19 20:59:02 +02:00
yocto_console_view: Add release selector
Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
This commit is contained in:
parent
c8f1590596
commit
58efe8c055
|
@ -16,3 +16,4 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import './views/ConsoleView/ConsoleView';
|
import './views/ConsoleView/ConsoleView';
|
||||||
|
import './releaseselectorfield';
|
||||||
|
|
152
yocto_console_view/src/releaseselectorfield.ts
Normal file
152
yocto_console_view/src/releaseselectorfield.ts
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
import {buildbotSetupPlugin} from "buildbot-plugin-support";
|
||||||
|
buildbotSetupPlugin((reg) => {
|
||||||
|
let selectListName = null;
|
||||||
|
let inputRefs = null;
|
||||||
|
let selectors = null;
|
||||||
|
|
||||||
|
const onTransitionEndEvent = (event) => {
|
||||||
|
/*
|
||||||
|
* We are looking for the transition showing the "forcebuild" dialog.
|
||||||
|
*/
|
||||||
|
if (!event.target.classList.contains("bb-forcebuild-modal"))
|
||||||
|
return
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find modal-body div.
|
||||||
|
*/
|
||||||
|
const modalDialog = Array.from(event.target.children).find(e => e.classList.contains("modal-dialog"));
|
||||||
|
const modalContent = modalDialog ? Array.from(modalDialog.children).find(e => e.classList.contains("modal-content")) : null;
|
||||||
|
const modalBody = modalContent ? Array.from(modalContent.children).find(e => e.classList.contains("modal-body")) : null;
|
||||||
|
if (!modalBody)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate a map of all inputs, identifed by the field name.
|
||||||
|
*/
|
||||||
|
inputRefs = new Map();
|
||||||
|
document.querySelectorAll('input').forEach(input => {
|
||||||
|
let idparent = input;
|
||||||
|
while (!idparent.attributes.getNamedItem('data-bb-test-id')
|
||||||
|
&& idparent.parentElement != modalBody) {
|
||||||
|
idparent = idparent.parentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = idparent.attributes.getNamedItem('data-bb-test-id')
|
||||||
|
if (id)
|
||||||
|
inputRefs.set(id.value, input);
|
||||||
|
|
||||||
|
prepareInterceptor(input);
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only show the pretty name in the release selector field.
|
||||||
|
*/
|
||||||
|
const releaseSelector = inputRefs.get('force-field-branchselector');
|
||||||
|
const releaseSelectorLabel = releaseSelector.parentNode.previousSibling;
|
||||||
|
const sepIdx = releaseSelectorLabel.textContent.indexOf(':');
|
||||||
|
releaseSelectorLabel.textContent = releaseSelectorLabel.textContent.substring(0, sepIdx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the name of the ReleaseSelector field div.
|
||||||
|
*/
|
||||||
|
const branchInputId = releaseSelector.attributes.getNamedItem('id').value;
|
||||||
|
const selectName = branchInputId.substring(0, branchInputId.lastIndexOf('-'));
|
||||||
|
selectListName = selectName + '-listbox';
|
||||||
|
}
|
||||||
|
window.addEventListener('transitionend', onTransitionEndEvent);
|
||||||
|
|
||||||
|
const onClick = (event) => {
|
||||||
|
if (selectListName) {
|
||||||
|
const listDiv = document.getElementById(selectListName);
|
||||||
|
if (listDiv) {
|
||||||
|
/*
|
||||||
|
* The ReleaseSelector menu is shown: save
|
||||||
|
* associated selectors for later and clean menu items.
|
||||||
|
*/
|
||||||
|
selectors = new Map();
|
||||||
|
listDiv.childNodes.forEach(div => {
|
||||||
|
const sepIdx = div.textContent.indexOf(':');
|
||||||
|
const propName = div.textContent.substring(0, sepIdx);
|
||||||
|
const content = div.textContent.substring(sepIdx + 1);
|
||||||
|
div.textContent = propName
|
||||||
|
selectors.set(propName, JSON.parse(content));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.target.parentElement) {
|
||||||
|
const parentId = event.target.parentElement.attributes.getNamedItem('id');
|
||||||
|
if (parentId && parentId.value == selectListName) {
|
||||||
|
/*
|
||||||
|
* One entry was clicked in the ReleaseSelector
|
||||||
|
* menu: update all fields described by the
|
||||||
|
* selector configuration.
|
||||||
|
*/
|
||||||
|
const selector = selectors.get(event.target.textContent);
|
||||||
|
if (selector) {
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
return applySelector(selector, event.target).then(resolve);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.addEventListener('click', onClick);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Apply values from the selected field selector
|
||||||
|
*/
|
||||||
|
async function applySelector(selector, selectList) {
|
||||||
|
for (let [field, value] of Object.entries(selector)) {
|
||||||
|
const input = inputRefs.get('force-field-' + field);
|
||||||
|
if (input && input.value != value) {
|
||||||
|
/*
|
||||||
|
* Setting value using input.value is not enough here: field
|
||||||
|
* would appear modified but this value would not be used on
|
||||||
|
* form submission.
|
||||||
|
*/
|
||||||
|
await setFieldValue(input, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const releaseSelector = inputRefs.get('force-field-branchselector');
|
||||||
|
releaseSelector.parentNode.previousSibling.textContent = selectList.textContent;
|
||||||
|
releaseSelector.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All code below is highly based on work from testing-library/user-event:
|
||||||
|
* https://github.com/testing-library/user-event
|
||||||
|
* The MIT License (MIT)
|
||||||
|
* Copyright (c) 2020 Giorgio Polvara
|
||||||
|
*/
|
||||||
|
function prepareInterceptor(element) {
|
||||||
|
const prototypeDescriptor = Object.getOwnPropertyDescriptor(element.constructor.prototype, 'value');
|
||||||
|
const objectDescriptor = Object.getOwnPropertyDescriptor(element, 'value');
|
||||||
|
Object.defineProperty(element, 'value', {
|
||||||
|
objectDescriptor,
|
||||||
|
['set']: function(v) {
|
||||||
|
const realFunc = prototypeDescriptor['set'];
|
||||||
|
realFunc.call(this, v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const UIValue = Symbol('Displayed value in UI');
|
||||||
|
async function setFieldValue(element, value) {
|
||||||
|
element.focus();
|
||||||
|
element[UIValue] = value;
|
||||||
|
element.value = Object.assign(new String(value), {
|
||||||
|
[UIValue]: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('blur', (e)=>{
|
||||||
|
const event = new Event('change', {bubbles: true, target: e.target, cancelable: false});
|
||||||
|
e.target.dispatchEvent(event);
|
||||||
|
}, {
|
||||||
|
capture: true,
|
||||||
|
passive: true
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
36
yocto_console_view/yocto_console_view/__init__.py
Normal file
36
yocto_console_view/yocto_console_view/__init__.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# This file is part of Buildbot. Buildbot is free software: you can
|
||||||
|
# redistribute it and/or modify it under the terms of the GNU General Public
|
||||||
|
# License as published by the Free Software Foundation, version 2.
|
||||||
|
#
|
||||||
|
# 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 General Public License for more
|
||||||
|
# details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along with
|
||||||
|
# this program; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
#
|
||||||
|
# Copyright Buildbot Team Members
|
||||||
|
|
||||||
|
from buildbot.www.plugin import Application
|
||||||
|
from buildbot.schedulers.forcesched import ChoiceStringParameter
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
# create the interface for the setuptools entry point
|
||||||
|
ep = Application(__package__, "Yocto Buildbot Console View plugin")
|
||||||
|
|
||||||
|
class ReleaseSelector(ChoiceStringParameter):
|
||||||
|
|
||||||
|
spec_attributes = ["selectors"]
|
||||||
|
selectors = None
|
||||||
|
|
||||||
|
def __init__(self, name, selectors, **kw):
|
||||||
|
def format_choice(choice):
|
||||||
|
return choice + ': ' + json.dumps(selectors.get(choice, {}))
|
||||||
|
|
||||||
|
super().__init__(name, **kw)
|
||||||
|
self.choices = [format_choice(choice)
|
||||||
|
for choice in kw.get('choices', {})]
|
||||||
|
self.__dict__['default'] = format_choice(self.__dict__['default'])
|
Loading…
Reference in New Issue
Block a user