Tower Plugin
Developing a tower plugin allows you to use custom Tower with your stack
Tower Plugin Interface
Plugin should be an object of class TowerPlugin
export interface TowerPluginInterface {
getReduxReducer: () => Reducer<CombinedState<{}>>; /* root plugin's redux reducer */
// tslint:disable-next-line: no-any
getReduxSaga: () => any; /* root plugin's redux saga */
getRoutes: (userLoading, isCurrentSession) => JSX.Element[]; /* plugin's routes */
getHeaderActions: () => HeaderActions; /* plugin's header actions like Filter, Refresh at the NavBar */
getMenu: () => MenuItem[]; /* plugin's menu items */
getMenuIcons: (name: string) => JSX.Element; /* plugin's menu icons */
}
Starting
Create a folder with your plugin's name inside tower/src/plugins
Folder should contain root index.ts
file with plugin object.
Example:
export * from './containers';
export * from './components';
export * from './modules';
export const IeoPlugin: TowerPluginInterface =
new TowerPlugin(ieoPluginReducer, rootIEOPluginsSaga, ieoRoutes, ieoActions, ieoMenuItem, ieoMenuIcons);
Each prop of TowerPlugin constructor will be described further
Redux State
In case your plugin can have several redux modules you should export root plugin's state from modules
Example:
typescript
export interface IeoPluginState {
ieoPlugin: StateIEO;
currencies: CurrenciesState;
}
** State's name should have structure ${PluginNameInCamelCase}PluginState
Redux Reducer
Root plugin's reducer has the same structure as root redux state
Example:
typescript
export const ieoPluginReducer = combineReducers({
ieoPlugin: ieoReducer,
currencies: currenciesReducer,
});
Redux Saga
Root plugin's saga is generator function which consist of all plugin's sagas
Example:
typescript
export function* rootIEOPluginsSaga() {
yield all([
call(rootIEOSaga),
call(rootCurrenciesSaga),
]);
}
Routes
As described earlier routes prop of TowerPlugin constructor is a constant which returns an array of JSX elements.
Here you define all routes you need for your plugin. If a route requires admin to be logged in to Tower use
typescript
<PrivateRoute />
constant from src/router/Router
Example:
jsx
export const ieoRoutes = (userLoading, isCurrentSession) => {
return ([
<PrivateRoute
loading={userLoading}
isLogged={isCurrentSession}
exact={true}
path="/tower/plugins/ieo/:id/edit"
component={IEOTabs}
/>,
<PrivateRoute
loading={userLoading}
isLogged={isCurrentSession}
exact={true}
path="/tower/plugins/ieo"
component={IEO}
/>,
]);
};
Props userLoading, isCurrentSession
will be transferred automatically to the function, don't worry about them.
UserLoading
will help your component to render Loading screen if the data about user hasn't loaded yet from the backend.isCurrentSession
shows to the component that user is logged in.
Header Actions
For the case when your page needs ability to open Filter component, manual Refresh of the page or Export action create a constant of type HeaderActions:
export interface HeaderActions {
pagesWithFilter?: string[]; /* routes which requires filter icon */
pagesWithRefresh?: string[]; /* routes which requires refresh icon */
pagesWithExport?: string[]; /* routes which requires export icon */
customHeaderActions?: JSX.Element; /* custom header actions */
}
Example
export const pagesWithFilter = ['/tower/plugins/ieo'];
export const pagesWithRefresh = ['/tower/plugins/ieo'];
export const pagesWithExport = ['/tower/plugins/ieo'];
Custom header actions is using for cases when you need to open a modal by clicking on Tower NavBar, redirect to Add IEO
pages etc.
customHeaderActions
is a JSX Element with
* an icon which you want to show near header action
* an action itself (opening a modal, redirect to a page)
* a route (page where you want to see that action in NavBar).
Example: ```tsx import { createStyles, SvgIcon, Theme, WithStyles } from '@material-ui/core'; import { withStyles } from '@material-ui/core/styles'; import { History } from 'history'; import * as React from 'react'; import { withRouter } from 'react-router-dom'; import { compose } from 'redux';
const styles = () => createStyles({ item: { display: 'flex', alignItems: 'center', marginLeft: 4, cursor: 'pointer', transition: 'background-color 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms', padding: '5px 10px', borderRadius: 5, '&:hover': { backgroundColor: '#ffffff1a', }, }, itemText: { textTransform: 'uppercase', paddingLeft: 6, fontWeight: 'bold', fontSize: 12, letterSpacing: 0.4, }, });
interface RouterProps { history: History; }
interface StyleProps extends WithStyles
type Props = StyleProps & RouterProps;
class IEOHeaderActionsComponent extends React.Component
public shouldShowAddIEO(location: string): boolean { return ['/tower/plugins/ieo'].includes(location); }
public handleAddIEO() { this.props.history.push('/tower/plugins/ieo/add'); }
public render() { const { history, classes } = this.props; return ( this.shouldShowAddIEO(history.location.pathname) && (
export const IEOHeaderActions = compose(
withRouter,
withStyles(styles, { withTheme: true }),
)(IEOHeaderActionsComponent) as React.ComponentClass;
Let's put it all together to header action constant:
typescript
export const ieoActions: HeaderActions = {
pagesWithFilter,
pagesWithRefresh,
customHeaderActions:
Menu
To display plugin's page inside tower Side Bar create an array of MenuItems
typescript
export interface MenuItem {
key: string; /* a route to a page */
value: string; /* menu item's name */
isLink: boolean; /* if true means it opens the page by click, */
} /* false - opens nested sidebar */
Example:
typescript
export const ieoMenuItem: MenuItem[] = [
{ key: '/tower/plugins/ieo', value: 'IEO', isLink: true },
];
Menu icons
If you want to express the meaning of menu item's by some nice icon create a constant which returns a specific icon by route.
Example:
typescript
export const ieoMenuIcons = (name: string) => {
switch (name) {
case '/tower/plugins/ieo':
return (
<svg width="32" height="19" viewBox="0 0 24 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fillRule="evenodd" clipRule="evenodd" d="..." fill="#979797"/>
</svg>
);
default: return;
}
};
Next Steps
- Check that
index.ts
file of your plugin's root folder exports all component, containers, modules and your plugin Object of classTowerPlugin
- Push your plugin folder to a separate github repo
- Now you can return to basic Tower stable version
- In order to build custom image you need to set configs to tower
## Configure tower to use custom plugins
Edit
plugins.json
file in root Tower directoryjson [ { "name": "ieo", "repo": "https://github.com/openware/tower-plugins.git", "branch": "ieo" } ]
- Define plugins name (same as the folder which you developed previously inside
src/plugins/
directory. - Put a link to github repository which contains plugins files
- Define github repository branch
- Define plugins name (same as the folder which you developed previously inside
Put plugin to env.js
file
javascript
plugins: [
{
name: 'ieo',
}
],
Building an image
yarn build
This command will clone the github repository you specified in plugins.json file, generate TowerTemplate file and connect your plugin to basic tower components!