Extracted ExtensionPoint and extension point store out into a standalone NPM package
Blue Ocean core will be able to create and "export" this (js-modules export). Plugins will "import" that shared instance, allowing them to register/add extension points etc etc. Will need to enhance the render function to do the async loading of modules when it encounters extension points that are not yet registered.
This commit is contained in:
parent
a3a2430603
commit
5b7a7bb5f6
|
@ -8,6 +8,7 @@
|
|||
"jenkins-js-test": "0.0.16"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jenkins-cd/extension-store": "0.0.1",
|
||||
"bootstrap-detached": "^3.3.4-v6",
|
||||
"handlebars": "^3.0.3",
|
||||
"hbsfy": "^2.4.1",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
ExtensionPoint.js
|
|
@ -0,0 +1 @@
|
|||
*.jsx
|
|
@ -0,0 +1,70 @@
|
|||
var React = require('react');
|
||||
var ReactDOM = require('react-dom');
|
||||
var store = require('./store.js');
|
||||
|
||||
var ExtensionPoint = React.createClass({
|
||||
|
||||
componentDidMount: function() {
|
||||
this._renderAllExtensions();
|
||||
},
|
||||
|
||||
componentDidUpdate: function() {
|
||||
this._renderAllExtensions();
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
this._unmountAllExtensions();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var extensionDivs = [];
|
||||
var extensions = store.getExtensions(this.props.name);
|
||||
|
||||
for (var i = 0; i < extensions.length; i++) {
|
||||
extensionDivs.push(<div key={i}/>);
|
||||
}
|
||||
|
||||
return React.createElement(this.props.wrappingElement, null, extensionDivs);
|
||||
},
|
||||
|
||||
_renderAllExtensions: function() {
|
||||
// TODO: This needs to be a lot cleverer if the list of extensions for a specific point can change
|
||||
const el = ReactDOM.findDOMNode(this).children;
|
||||
const extensions = store.getExtensions(this.props.name);
|
||||
for (var i = 0; i < extensions.length; i++) {
|
||||
this._renderExtension(el[i], extensions[i]);
|
||||
}
|
||||
},
|
||||
|
||||
/** Actually render an individual extension */
|
||||
_renderExtension: function(element, extension) {
|
||||
var component = React.createElement(extension, this.props);
|
||||
try {
|
||||
ReactDOM.render(component, element);
|
||||
} catch (e) {
|
||||
console.log("error rendering", extension.name, e);
|
||||
|
||||
var errorDiv = <div className="error alien">Error rendering {extension.name}: {e.toString()}</div>;
|
||||
ReactDOM.render(errorDiv, element);
|
||||
}
|
||||
},
|
||||
|
||||
/** Clean up child extensions */
|
||||
_unmountAllExtensions: function() {
|
||||
var children = ReactDOM.findDOMNode(this).children;
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
ReactDOM.unmountComponentAtNode(children[i]); // TODO: Can this throw?
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ExtensionPoint.defaultProps = {
|
||||
wrappingElement: "div"
|
||||
};
|
||||
|
||||
ExtensionPoint.propTypes = {
|
||||
name: React.PropTypes.string.isRequired,
|
||||
wrappingElement: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element])
|
||||
};
|
||||
|
||||
module.exports = ExtensionPoint;
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
exports.store = require('./store.js');
|
||||
exports.ExtensionPoint = require('./ExtensionPoint.js');
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "@jenkins-cd/js-extensions",
|
||||
"version": "0.0.2",
|
||||
"description": "Jenkins Extension Store",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "jsx ExtensionPoint.jsx > ExtensionPoint.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"prepublish": "npm run build"
|
||||
},
|
||||
"author": "Tom Fennelly <tom.fennelly@gmail.com> (https://github.com/tfennelly)",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^0.14.7",
|
||||
"react-dom": "^0.14.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"react-tools": "^0.13.3"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
var points = {};
|
||||
|
||||
exports.addExtensionPoint = function(key) {
|
||||
points[key] = points[key] || [];
|
||||
};
|
||||
|
||||
exports.addExtension = function (key, extension) {
|
||||
exports.addExtensionPoint(key);
|
||||
points[key].push(extension);
|
||||
};
|
||||
|
||||
exports.getExtensions = function(key) {
|
||||
return points[key] || [];
|
||||
};
|
|
@ -17,6 +17,7 @@
|
|||
"author": "jmcdonald@cloudbees.com",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jenkins-cd/js-extensions": "0.0.2",
|
||||
"babel-runtime": "^6.3.19",
|
||||
"history": "^1.17.0",
|
||||
"react": "^0.14.5",
|
||||
|
|
|
@ -1,114 +0,0 @@
|
|||
import React, {Component, PropTypes} from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// ExtensionPointStore
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// TODO: move this into a proper redux store + reducer
|
||||
|
||||
export class ExtensionPointStore {
|
||||
constructor() {
|
||||
this.points = {};
|
||||
}
|
||||
|
||||
addExtensionPoint(key) {
|
||||
//console.log("addExtensionPoint");
|
||||
this.points[key] = this.points[key] || [];
|
||||
}
|
||||
|
||||
addExtension(key, extension) {
|
||||
//console.log("addExtension");
|
||||
this.addExtensionPoint(key);
|
||||
this.points[key].push(extension);
|
||||
}
|
||||
|
||||
getExtensions(key) {
|
||||
//console.log("getExtensions", key, this.points);
|
||||
return this.points[key] || [];
|
||||
}
|
||||
|
||||
registerListener(key, handler) {
|
||||
}
|
||||
|
||||
unRegisterListener(key, handler) {
|
||||
}
|
||||
}
|
||||
|
||||
// Ugly global
|
||||
export const extensionPointStoreSingleton = new ExtensionPointStore();
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// ExtensionPoint
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* TODO: Docs
|
||||
*/
|
||||
export class ExtensionPoint extends Component {
|
||||
|
||||
componentDidMount() {
|
||||
this._renderAllExtensions();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this._renderAllExtensions();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._unmountAllExtensions();
|
||||
}
|
||||
|
||||
render() {
|
||||
var extensionDivs = [];
|
||||
var extensions = extensionPointStoreSingleton.getExtensions(this.props.name);
|
||||
|
||||
for (var i = 0; i < extensions.length; i++) {
|
||||
extensionDivs.push(<div key={i}/>);
|
||||
}
|
||||
|
||||
return React.createElement(this.props.wrappingElement, null, extensionDivs);
|
||||
}
|
||||
|
||||
_renderAllExtensions() {
|
||||
// TODO: This needs to be a lot cleverer if the list of extensions for a specific point can change
|
||||
const el = ReactDOM.findDOMNode(this).children;
|
||||
const extensions = extensionPointStoreSingleton.getExtensions(this.props.name);
|
||||
for (let i = 0; i < extensions.length; i++) {
|
||||
this._renderExtension(el[i], extensions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Actually render an individual extension */
|
||||
_renderExtension(element, extension) {
|
||||
var component = React.createElement(extension, this.props);
|
||||
try {
|
||||
ReactDOM.render(component, element);
|
||||
} catch (e) {
|
||||
console.log("error rendering", extension.name, e);
|
||||
|
||||
var errorDiv = <div className="error alien">Error rendering {extension.name}: {e.toString()}</div>;
|
||||
ReactDOM.render(errorDiv, element);
|
||||
}
|
||||
}
|
||||
|
||||
/** Clean up child extensions */
|
||||
_unmountAllExtensions() {
|
||||
for (let node of ReactDOM.findDOMNode(this).children) {
|
||||
ReactDOM.unmountComponentAtNode(node); // TODO: Can this throw?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExtensionPoint.defaultProps = {
|
||||
wrappingElement: "div"
|
||||
};
|
||||
|
||||
ExtensionPoint.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
wrappingElement: PropTypes.oneOfType([PropTypes.string, PropTypes.element])
|
||||
};
|
|
@ -2,8 +2,9 @@
|
|||
* Re-export things for convenience.
|
||||
*/
|
||||
|
||||
import {ExtensionPoint, extensionPointStoreSingleton} from './extension-point.jsx';
|
||||
import extensions from '@jenkins-cd/js-extensions';
|
||||
import {store, actions} from './stores';
|
||||
|
||||
export const extensionPointStore = extensionPointStoreSingleton; // TODO: remove ugly global
|
||||
export {ExtensionPoint, store, actions};
|
||||
export const extensionPointStore = extensions.store; // TODO: remove ugly global
|
||||
export const ExtensionPoint = extensions.ExtensionPoint;
|
||||
export {store, actions};
|
||||
|
|
Loading…
Reference in New Issue