This repository has been archived by the owner on Sep 29, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a9bda0a
commit 95650a9
Showing
2 changed files
with
145 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
# Development | ||
|
||
To extend the capabilities of this tool, there are two key classes you must implement, in addition to modifiying the proper files so that those classes may be instantiated. | ||
|
||
## Classes | ||
|
||
### Service Class | ||
|
||
The service class is the backend implementation that performs the API calls and other communication with the remote service. The below is a bare-bones template of a service class that belongs in `./lib/services`: | ||
|
||
```Javascript | ||
const Service = require('./Service') | ||
|
||
class NewService extends Service { | ||
constructor (config) { | ||
// That first parameter becomes this.name | ||
super('newServiceName', config) | ||
} | ||
|
||
exec (dataEmitter) { | ||
// Return a Promise while executing remote communication for the service fetch. | ||
// Once done, return an object like the one below, with a type key and a data | ||
// key containing the information needed on the front end. Services may also | ||
// "emit" data by directly calling the function dataEmitter passed in the | ||
// method signature with the object format below as the first parameter. The | ||
// parent class handles caching of this data. That cache can be accessed at | ||
// this.cachedResponse. Also, access the configuration, passed from the | ||
// frontend at this.config. | ||
return Promise.resolve({ | ||
type: this.name, | ||
data: {} | ||
}) | ||
} | ||
} | ||
|
||
// When a Service is instantiated, this is used to provide a default configuration | ||
NewService.defaultConfig = { | ||
someConfigKey: null | ||
} | ||
|
||
module.exports = NewService | ||
``` | ||
|
||
Also, make sure to update the `index.js` file in `./lib/services` to include this class. | ||
|
||
### ServiceManager | ||
|
||
The method `instantiateServiceByName(name, config)` in `./liv/util/ServiceManager.js` takes a name slug, matching `this.name` from the service class, and returns an instantiated class. This must be updated to include the new class. | ||
|
||
### Widget Class | ||
|
||
The widget class is used to display the information on the front-end. The below is a bare-bones template of a widget class that belongs in `./ui/widgets`: | ||
|
||
```Javascript | ||
import React from 'react' | ||
import Widget from './Widget' | ||
import { bindActionCreators } from 'redux' | ||
import { connect } from 'react-redux' | ||
import { | ||
commitTempConfig, | ||
setTemporaryConfig, | ||
removeService | ||
} from '../util/actions' | ||
import { | ||
WidgetEditorFieldGroup | ||
} from '../util/widgetElements' | ||
|
||
class NewWidget extends Widget { | ||
constructor (props) { | ||
// The first parameter can be any string, used fo the widget's title, but the | ||
// second paramter must match the slug used for the name in the service class | ||
super('Widget Nice Title', 'newServiceName', props) | ||
} | ||
|
||
renderWidget () { | ||
// getWidgetData returns the information sent in "data" by the service class | ||
const data = this.getWidgetData() | ||
return data && ( | ||
<div> | ||
Some widget data | ||
</div> | ||
) | ||
} | ||
|
||
renderEditor () { | ||
// The "tempConfig" is a copy of the configuration made every time the widget | ||
// enters editing-mode. When the user hits "Save" on the editor, that temp | ||
// configuration is committed to the primary config and the service class is | ||
// restarted with that new config data. The easiest way to modify it is by | ||
// calling the method, this.setTempConfigValue('key', value) as seen below. | ||
const tempConfig = this.getWidgetTempConfig() | ||
if (tempConfig) { | ||
return ( | ||
<div> | ||
<WidgetEditorFieldGroup name='Config field name'> | ||
<input | ||
className='widget-editor-input' | ||
type='text' | ||
value={tempConfig.someConfigKey} | ||
onChange={(event) => this.setTempConfigValue('someConfigKey', event.target.value)} /> | ||
</WidgetEditorFieldGroup> | ||
</div> | ||
) | ||
} else { | ||
return null | ||
} | ||
} | ||
} | ||
|
||
const stateToProps = (state) => { | ||
return { | ||
services: state.services | ||
} | ||
} | ||
|
||
const dispatchToProps = (dispatch) => { | ||
return bindActionCreators({ | ||
commitTempConfig, | ||
setTemporaryConfig, | ||
removeService | ||
}, dispatch) | ||
} | ||
|
||
// These are used by the layout engine. "h" is the default h in row-units for | ||
// the widget and "isResizable" dictates whether or not the user may resize | ||
// the widget. | ||
NewWidget.widgetProps = { | ||
h: 3, | ||
isResizable: true | ||
} | ||
|
||
export default connect(stateToProps, dispatchToProps)(NewWidget) | ||
``` | ||
|
||
### Dashboard.js | ||
|
||
The main React class of this application, Dashboard.js, contains two methods that must be updated to accommodate the new widget class: | ||
|
||
* `renderWidget(service)` takes a service object and uses `service.name` to determine which React/widget element to return. | ||
* `getServiceProps(service)` takes a service object and uses `service.name` to determine which React/widget `widgetProps` property to return. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters