mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
CampaignAdmin and GridField React sections
Also removes watchify because it wasn't working. Add SilverStripeBackend class used to fetch data from endpoints for the front-end
This commit is contained in:
parent
7580d35be8
commit
f8c17bed3b
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,3 +7,4 @@ npm-debug.log
|
||||
css/GridField_print.css
|
||||
admin/thirdparty/chosen/node_modules
|
||||
node_modules/
|
||||
coverage/
|
||||
|
102
admin/code/CampaignAdmin.php
Normal file
102
admin/code/CampaignAdmin.php
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Campaign section of the CMS
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage admin
|
||||
*/
|
||||
class CampaignAdmin extends LeftAndMain implements PermissionProvider {
|
||||
|
||||
private static $allowed_actions = [
|
||||
'createCampaign',
|
||||
'readCampaign',
|
||||
'updateCampaign',
|
||||
'deleteCampaign',
|
||||
];
|
||||
|
||||
private static $menu_priority = 11;
|
||||
|
||||
private static $menu_title = 'Campaigns';
|
||||
|
||||
private static $url_handlers = [
|
||||
'POST item/$ID' => 'createCampaign',
|
||||
'GET item/$ID' => 'readCampaign',
|
||||
'PUT item/$ID' => 'updateCampaign',
|
||||
'DELETE item/$ID' => 'deleteCampaign',
|
||||
];
|
||||
|
||||
private static $url_segment = 'campaigns';
|
||||
|
||||
public function init() {
|
||||
parent::init();
|
||||
|
||||
Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/javascript/dist/bundle-react.js');
|
||||
Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/javascript/dist/campaign-admin.js');
|
||||
}
|
||||
|
||||
public function getEditForm($id = null, $fields = null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* REST endpoint to create a campaign.
|
||||
*
|
||||
* @param SS_HTTPRequest $request
|
||||
*
|
||||
* @return SS_HTTPResponse
|
||||
*/
|
||||
public function createCampaign(SS_HTTPRequest $request) {
|
||||
$response = new SS_HTTPResponse();
|
||||
$response->addHeader('Content-Type', 'application/json');
|
||||
$response->setBody(Convert::raw2json(['campaign' => 'create']));
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST endpoint to get a campaign.
|
||||
*
|
||||
* @param SS_HTTPRequest $request
|
||||
*
|
||||
* @return SS_HTTPResponse
|
||||
*/
|
||||
public function readCampaign(SS_HTTPRequest $request) {
|
||||
$response = new SS_HTTPResponse();
|
||||
$response->addHeader('Content-Type', 'application/json');
|
||||
$response->setBody(Convert::raw2json(['campaign' => 'read']));
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST endpoint to update a campaign.
|
||||
*
|
||||
* @param SS_HTTPRequest $request
|
||||
*
|
||||
* @return SS_HTTPResponse
|
||||
*/
|
||||
public function updateCampaign(SS_HTTPRequest $request) {
|
||||
$response = new SS_HTTPResponse();
|
||||
$response->addHeader('Content-Type', 'application/json');
|
||||
$response->setBody(Convert::raw2json(['campaign' => 'update']));
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST endpoint to delete a campaign.
|
||||
*
|
||||
* @param SS_HTTPRequest $request
|
||||
*
|
||||
* @return SS_HTTPResponse
|
||||
*/
|
||||
public function deleteCampaign(SS_HTTPRequest $request) {
|
||||
$response = new SS_HTTPResponse();
|
||||
$response->addHeader('Content-Type', 'application/json');
|
||||
$response->setBody(Convert::raw2json(['campaign' => 'delete']));
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
}
|
@ -482,6 +482,8 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
||||
Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/javascript/dist/leaktools.js');
|
||||
}
|
||||
|
||||
Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/javascript/dist/boot.js');
|
||||
|
||||
Requirements::css(FRAMEWORK_ADMIN_DIR . '/css/bootstrap/bootstrap.css');
|
||||
Requirements::css(FRAMEWORK_ADMIN_DIR . '/thirdparty/jquery-notice/jquery.notice.css');
|
||||
Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery-ui.css');
|
||||
|
BIN
admin/images/sprites/src/menu-icons/16x16-2x/collection.png
Normal file
BIN
admin/images/sprites/src/menu-icons/16x16-2x/collection.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 212 B |
BIN
admin/images/sprites/src/menu-icons/16x16/collection.png
Normal file
BIN
admin/images/sprites/src/menu-icons/16x16/collection.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 184 B |
BIN
admin/images/sprites/src/menu-icons/24x24/collection.png
Normal file
BIN
admin/images/sprites/src/menu-icons/24x24/collection.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 211 B |
23
admin/javascript/src/boot/campaign-admin.js
Normal file
23
admin/javascript/src/boot/campaign-admin.js
Normal file
@ -0,0 +1,23 @@
|
||||
import reducerRegister from 'reducer-register';
|
||||
import $ from 'jQuery';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import CampaignAdmin from '../sections/campaign-admin/controller';
|
||||
import campaignsReducer from '../state/campaigns/reducer';
|
||||
|
||||
// TODO: Move this to the controller.
|
||||
reducerRegister.add('campaigns', campaignsReducer);
|
||||
|
||||
$.entwine('ss', function ($) {
|
||||
|
||||
$('.cms-content.CampaignAdmin').entwine({
|
||||
onadd: function () {
|
||||
ReactDOM.render(<CampaignAdmin />, this[0]);
|
||||
},
|
||||
|
||||
onremove: function () {
|
||||
ReactDOM.unmountComponentAtNode(this[0]);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
15
admin/javascript/src/boot/index.js
Normal file
15
admin/javascript/src/boot/index.js
Normal file
@ -0,0 +1,15 @@
|
||||
import { combineReducers, createStore, applyMiddleware } from 'redux';
|
||||
import thunkMiddleware from 'redux-thunk';
|
||||
import createLogger from 'redux-logger';
|
||||
import reducerRegister from 'reducer-register';
|
||||
|
||||
function appBoot() {
|
||||
const initialState = {};
|
||||
const rootReducer = combineReducers(reducerRegister.getAll());
|
||||
const createStoreWithMiddleware = applyMiddleware(thunkMiddleware, createLogger())(createStore);
|
||||
const store = createStoreWithMiddleware(rootReducer, initialState);
|
||||
|
||||
console.log(store.getState());
|
||||
}
|
||||
|
||||
window.onload = appBoot;
|
@ -0,0 +1,5 @@
|
||||
# GridFieldCell
|
||||
|
||||
This component represents a data cell in a GridFieldRow.
|
||||
|
||||
## Props
|
14
admin/javascript/src/components/grid-field-cell/index.js
Normal file
14
admin/javascript/src/components/grid-field-cell/index.js
Normal file
@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
import SilverStripeComponent from 'silverstripe-component';
|
||||
|
||||
class GridFieldCellComponent extends SilverStripeComponent {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<td className='grid-field-cell-component'>{this.props.children}</td>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default GridFieldCellComponent;
|
@ -0,0 +1,3 @@
|
||||
.grid-field-cell-component {
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
jest.dontMock('../index');
|
||||
|
||||
describe('GridFieldCellComponent', () => {
|
||||
|
||||
});
|
@ -0,0 +1,6 @@
|
||||
# GridFieldHeaderCell
|
||||
|
||||
This component is a cell in a GridFirldHeader component.
|
||||
|
||||
## Props
|
||||
|
@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
import SilverStripeComponent from 'silverstripe-component';
|
||||
|
||||
class GridFieldHeaderCellComponent extends SilverStripeComponent {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<th className='grid-field-header-cell-component'>{this.props.children}</th>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default GridFieldHeaderCellComponent;
|
@ -0,0 +1,5 @@
|
||||
jest.dontMock('../index');
|
||||
|
||||
describe('GridFieldHeaderCellComponent', () => {
|
||||
|
||||
});
|
@ -0,0 +1,5 @@
|
||||
# GridFieldHeader
|
||||
|
||||
This component is used to display a tabel header row on a GridFieldComponent.
|
||||
|
||||
## Props
|
17
admin/javascript/src/components/grid-field-header/index.js
Normal file
17
admin/javascript/src/components/grid-field-header/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import SilverStripeComponent from 'silverstripe-component';
|
||||
import GridFieldRowComponent from '../grid-field-row';
|
||||
|
||||
class GridFieldHeaderComponent extends SilverStripeComponent {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<thead className='grid-field-header-component'>
|
||||
<GridFieldRowComponent>{this.props.children}</GridFieldRowComponent>
|
||||
</thead>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default GridFieldHeaderComponent;
|
@ -0,0 +1,3 @@
|
||||
.grid-field-header-component {
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
jest.dontMock('../index');
|
||||
|
||||
describe('GridFieldHeaderComponent', () => {
|
||||
|
||||
});
|
9
admin/javascript/src/components/grid-field-row/README.md
Normal file
9
admin/javascript/src/components/grid-field-row/README.md
Normal file
@ -0,0 +1,9 @@
|
||||
# GridFieldRow
|
||||
|
||||
Represents a row in a GridField.
|
||||
|
||||
## Props
|
||||
|
||||
### cells (array)
|
||||
|
||||
The table data to display in the row.
|
14
admin/javascript/src/components/grid-field-row/index.js
Normal file
14
admin/javascript/src/components/grid-field-row/index.js
Normal file
@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
import SilverStripeComponent from 'silverstripe-component';
|
||||
|
||||
class GridFieldRowComponent extends SilverStripeComponent {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<tr className='grid-field-row-component'>{this.props.children}</tr>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default GridFieldRowComponent;
|
@ -0,0 +1,3 @@
|
||||
.grid-field-row-component {
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
jest.dontMock('../index');
|
||||
|
||||
describe('GridFieldRowComponent', () => {
|
||||
|
||||
});
|
13
admin/javascript/src/components/grid-field/README.md
Normal file
13
admin/javascript/src/components/grid-field/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
# GridField
|
||||
|
||||
This component is used to display structured data in an extendible table layout.
|
||||
|
||||
## Props
|
||||
|
||||
### Headings (array)
|
||||
|
||||
The column headings.
|
||||
|
||||
### Rows (array)
|
||||
|
||||
The table rows.
|
65
admin/javascript/src/components/grid-field/index.js
Normal file
65
admin/javascript/src/components/grid-field/index.js
Normal file
@ -0,0 +1,65 @@
|
||||
import React from 'react';
|
||||
import SilverStripeComponent from 'silverstripe-component';
|
||||
|
||||
class GridFieldComponent extends SilverStripeComponent {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<table className='grid-field-component [ table ]'>
|
||||
{this.generateHeader()}
|
||||
<tbody>
|
||||
{this.generateRows()}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the header component.
|
||||
*
|
||||
* Uses the header component passed via the `header` prop if it exists.
|
||||
* Otherwise generates a header from the `data` prop.
|
||||
*
|
||||
* @return object|null
|
||||
*/
|
||||
generateHeader() {
|
||||
if (typeof this.props.header !== 'undefined') {
|
||||
return this.props.header;
|
||||
}
|
||||
|
||||
if (typeof this.props.data !== 'undefined') {
|
||||
// TODO: Generate the header.
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the table rows.
|
||||
*
|
||||
* Uses the components passed via the `rows` props if it exists.
|
||||
* Otherwise generates rows from the `data` prop.
|
||||
*
|
||||
* @return object|null
|
||||
*/
|
||||
generateRows() {
|
||||
if (typeof this.props.rows !== 'undefined') {
|
||||
return this.props.rows;
|
||||
}
|
||||
|
||||
if (typeof this.props.data !== 'undefined') {
|
||||
// TODO: Generate the rows.
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GridFieldComponent.propTypes = {
|
||||
data: React.PropTypes.object,
|
||||
header: React.PropTypes.object,
|
||||
rows: React.PropTypes.array
|
||||
};
|
||||
|
||||
export default GridFieldComponent;
|
3
admin/javascript/src/components/grid-field/styles.scss
Normal file
3
admin/javascript/src/components/grid-field/styles.scss
Normal file
@ -0,0 +1,3 @@
|
||||
.grid-field-component {
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
jest.dontMock('../index');
|
||||
|
||||
describe('GridFieldComponent', () => {
|
||||
|
||||
});
|
17
admin/javascript/src/mocks/jQuery.js
Normal file
17
admin/javascript/src/mocks/jQuery.js
Normal file
@ -0,0 +1,17 @@
|
||||
function jQuery() {
|
||||
return {
|
||||
// Add jQuery methods such as 'find', 'change', 'trigger' as needed.
|
||||
};
|
||||
}
|
||||
|
||||
var mockAjaxFn = jest.genMockFunction();
|
||||
|
||||
mockAjaxFn.mockReturnValue({
|
||||
done: jest.genMockFunction(),
|
||||
fail: jest.genMockFunction(),
|
||||
always: jest.genMockFunction()
|
||||
});
|
||||
|
||||
jQuery.ajax = mockAjaxFn;
|
||||
|
||||
export default jQuery;
|
64
admin/javascript/src/reducer-register.js
Normal file
64
admin/javascript/src/reducer-register.js
Normal file
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* The register of Redux reducers.
|
||||
* @private
|
||||
*/
|
||||
var register = {};
|
||||
|
||||
/**
|
||||
* The central register of Redux reducers for the CMS. All registered reducers are combined when the application boots.
|
||||
*/
|
||||
class ReducerRegister {
|
||||
|
||||
/**
|
||||
* Adds a reducer to the register.
|
||||
*
|
||||
* @param string key - The key to register the reducer against.
|
||||
* @param object reducer - Redux reducer.
|
||||
*/
|
||||
add(key, reducer) {
|
||||
if (typeof register[key] !== 'undefined') {
|
||||
throw new Error(`Reducer already exists at '${key}'`);
|
||||
}
|
||||
|
||||
register[key] = reducer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all reducers from the register.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
getAll() {
|
||||
return register;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reducer from the register.
|
||||
*
|
||||
* @param string [key] - The key the reducer is registered against.
|
||||
*
|
||||
* @return object|undefined
|
||||
*/
|
||||
getByKey(key) {
|
||||
return register[key];
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Removes a reducer from the register.
|
||||
*
|
||||
* @param string key - The key the reducer is registered against.
|
||||
*/
|
||||
remove(key) {
|
||||
delete register[key];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Create an instance to export. The same instance is exported to
|
||||
// each script which imports the reducerRegister. This means the
|
||||
// same register is available throughout the application.
|
||||
let reducerRegister = new ReducerRegister();
|
||||
|
||||
export default reducerRegister;
|
3
admin/javascript/src/sections/campaign-admin/README.md
Normal file
3
admin/javascript/src/sections/campaign-admin/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# CampaignAdmin
|
||||
|
||||
This section is used for managing Campaigns in the CMS.
|
60
admin/javascript/src/sections/campaign-admin/controller.js
Normal file
60
admin/javascript/src/sections/campaign-admin/controller.js
Normal file
@ -0,0 +1,60 @@
|
||||
import React from 'react';
|
||||
import SilverStripeComponent from 'silverstripe-component';
|
||||
import NorthHeader from '../../components/north-header';
|
||||
import GridField from '../../components/grid-field';
|
||||
import GridFieldHeader from '../../components/grid-field-header';
|
||||
import GridFieldHeaderCell from '../../components/grid-field-header-cell';
|
||||
import GridFieldRow from '../../components/grid-field-row';
|
||||
import GridFieldCell from '../../components/grid-field-cell';
|
||||
|
||||
class CampaignAdminContainer extends SilverStripeComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
// TODO: This will be an AJAX call and it's response stored in state.
|
||||
this.mockData = {
|
||||
campaigns: [
|
||||
{
|
||||
title: 'SilverStripe 4.0 release',
|
||||
description: 'All the stuff related to the 4.0 announcement',
|
||||
changes: 20
|
||||
},
|
||||
{
|
||||
title: 'March release',
|
||||
description: 'march release stuff',
|
||||
changes: 2
|
||||
},
|
||||
{
|
||||
title: 'About us',
|
||||
description: 'The team',
|
||||
changes: 1345
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const columnNames = ['title', 'changes', 'description'];
|
||||
|
||||
const headerCells = columnNames.map((columnName, i) => <GridFieldHeaderCell key={i}>{columnName}</GridFieldHeaderCell>);
|
||||
const header = <GridFieldHeader>{headerCells}</GridFieldHeader>;
|
||||
|
||||
const rows = this.mockData.campaigns.map((campaign, i) => {
|
||||
const cells = columnNames.map((columnName, i) => {
|
||||
return <GridFieldCell key={i}>{campaign[columnName]}</GridFieldCell>
|
||||
});
|
||||
return <GridFieldRow key={i}>{cells}</GridFieldRow>;
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<NorthHeader></NorthHeader>
|
||||
<GridField header={header} rows={rows}></GridField>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default CampaignAdminContainer;
|
3
admin/javascript/src/sections/campaign-admin/styles.scss
Normal file
3
admin/javascript/src/sections/campaign-admin/styles.scss
Normal file
@ -0,0 +1,3 @@
|
||||
.CampaignAdmin {
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
jest.dontMock('../controller');
|
||||
|
||||
describe('CampaignAdminContainer', () => {
|
||||
|
||||
});
|
58
admin/javascript/src/silverstripe-backend.js
Normal file
58
admin/javascript/src/silverstripe-backend.js
Normal file
@ -0,0 +1,58 @@
|
||||
import $ from 'jQuery';
|
||||
|
||||
class SilverStripeBackend {
|
||||
|
||||
/**
|
||||
* Makes a network request using the GET HTTP verb.
|
||||
*
|
||||
* @param string url - Endpoint URL.
|
||||
*
|
||||
* @return object - jqXHR. See http://api.jquery.com/Types/#jqXHR
|
||||
*/
|
||||
get(url) {
|
||||
return $.ajax({ type: 'GET', url });
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a network request using the POST HTTP verb.
|
||||
*
|
||||
* @param string url - Endpoint URL.
|
||||
* @param object data - Data to send with the request.
|
||||
*
|
||||
* @return object - jqXHR. See http://api.jquery.com/Types/#jqXHR
|
||||
*/
|
||||
post(url, data) {
|
||||
return $.ajax({ type: 'POST', url, data });
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a newtwork request using the PUT HTTP verb.
|
||||
*
|
||||
* @param string url - Endpoint URL.
|
||||
* @param object data - Data to send with the request.
|
||||
*
|
||||
* @return object - jqXHR. See http://api.jquery.com/Types/#jqXHR
|
||||
*/
|
||||
put(url, data) {
|
||||
return $.ajax({ type: 'PUT', url, data });
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a newtwork request using the DELETE HTTP verb.
|
||||
*
|
||||
* @param string url - Endpoint URL.
|
||||
* @param object data - Data to send with the request.
|
||||
*
|
||||
* @return object - jqXHR. See http://api.jquery.com/Types/#jqXHR
|
||||
*/
|
||||
delete(url, data) {
|
||||
return $.ajax({ type: 'DELETE', url, data });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Exported as a singleton so we can implement things like
|
||||
// global caching and request batching at some stage.
|
||||
let backend = new SilverStripeBackend();
|
||||
|
||||
export default backend;
|
8
admin/javascript/src/state/campaigns/action-types.js
Normal file
8
admin/javascript/src/state/campaigns/action-types.js
Normal file
@ -0,0 +1,8 @@
|
||||
export default {
|
||||
CREATE_CAMPAIGN: 'CREATE_CAMPAIGN',
|
||||
UPDATE_CAMPAIGN: 'UPDATE_CAMPAIGN',
|
||||
DELETE_CAMPAIGN: 'DELETE_CAMPAIGN',
|
||||
FETCH_CAMPAIGN_REQUEST: 'FETCH_CAMPAIGN_REQUEST',
|
||||
FETCH_CAMPAIGN_FAILURE: 'FETCH_CAMPAIGN_FAILURE',
|
||||
FETCH_CAMPAIGN_SUCCESS: 'FETCH_CAMPAIGN_SUCCESS'
|
||||
};
|
0
admin/javascript/src/state/campaigns/actions.js
Normal file
0
admin/javascript/src/state/campaigns/actions.js
Normal file
49
admin/javascript/src/state/campaigns/reducer.js
Normal file
49
admin/javascript/src/state/campaigns/reducer.js
Normal file
@ -0,0 +1,49 @@
|
||||
import deepFreeze from 'deep-freeze';
|
||||
import ACTION_TYPES from './action-types';
|
||||
|
||||
const initialState = {
|
||||
isFetching: false,
|
||||
items: []
|
||||
};
|
||||
|
||||
function campaignsReducer(state = initialState, action) {
|
||||
|
||||
switch (action.type) {
|
||||
|
||||
case ACTION_TYPES.CREATE_CAMPAIGN:
|
||||
return deepFreeze(Object.assign({}, state, {
|
||||
|
||||
}));
|
||||
|
||||
case ACTION_TYPES.UPDATE_CAMPAIGN:
|
||||
return deepFreeze(Object.assign({}, state, {
|
||||
|
||||
}));
|
||||
|
||||
case ACTION_TYPES.DELETE_CAMPAIGN:
|
||||
return deepFreeze(Object.assign({}, state, {
|
||||
|
||||
}));
|
||||
|
||||
case ACTION_TYPES.FETCH_CAMPAIGN_REQUEST:
|
||||
return deepFreeze(Object.assign({}, state, {
|
||||
isFetching: true
|
||||
}));
|
||||
|
||||
case ACTION_TYPES.FETCH_CAMPAIGN_FAILURE:
|
||||
return deepFreeze(Object.assign({}, state, {
|
||||
isFetching: false
|
||||
}));
|
||||
|
||||
case ACTION_TYPES.FETCH_CAMPAIGN_SUCCESS:
|
||||
return deepFreeze(Object.assign({}, state, {
|
||||
isFetching: false
|
||||
}));
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default campaignsReducer;
|
@ -0,0 +1,70 @@
|
||||
jest.dontMock('deep-freeze');
|
||||
jest.dontMock('../reducer');
|
||||
jest.dontMock('../action-types');
|
||||
|
||||
var campaignsReducer = require('../reducer').default,
|
||||
ACTION_TYPES = require('../action-types').default;
|
||||
|
||||
describe('campaignsReducer', () => {
|
||||
|
||||
describe('CREATE_CAMPAIGN', () => {
|
||||
|
||||
});
|
||||
|
||||
describe('UPDATE_CAMPAIGN', () => {
|
||||
|
||||
});
|
||||
|
||||
describe('DELETE_CAMPAIGN', () => {
|
||||
|
||||
});
|
||||
|
||||
describe('FETCH_CAMPAIGN_REQUEST', () => {
|
||||
|
||||
it('should set the "isFetching" flag', () => {
|
||||
const initialState = {
|
||||
isFetching: false
|
||||
};
|
||||
|
||||
const action = { type: ACTION_TYPES.FETCH_CAMPAIGN_REQUEST };
|
||||
|
||||
const nextState = campaignsReducer(initialState, action);
|
||||
|
||||
expect(nextState.isFetching).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('FETCH_CAMPAIGN_FAILURE', () => {
|
||||
|
||||
it('should unset the "isFetching" flag', () => {
|
||||
const initialState = {
|
||||
isFetching: true
|
||||
};
|
||||
|
||||
const action = { type: ACTION_TYPES.FETCH_CAMPAIGN_FAILURE };
|
||||
|
||||
const nextState = campaignsReducer(initialState, action);
|
||||
|
||||
expect(nextState.isFetching).toBe(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('FETCH_CAMPAIGN_SUCCESS', () => {
|
||||
|
||||
it('should unset the "isFetching" flag', () => {
|
||||
const initialState = {
|
||||
isFetching: true
|
||||
};
|
||||
|
||||
const action = { type: ACTION_TYPES.FETCH_CAMPAIGN_FAILURE };
|
||||
|
||||
const nextState = campaignsReducer(initialState, action);
|
||||
|
||||
expect(nextState.isFetching).toBe(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
14
admin/javascript/src/styles/main.scss
Normal file
14
admin/javascript/src/styles/main.scss
Normal file
@ -0,0 +1,14 @@
|
||||
/** -----------------------------
|
||||
* Sections
|
||||
* ------------------------------ */
|
||||
@import "../sections/campaign-admin/styles";
|
||||
|
||||
/** -----------------------------
|
||||
* components
|
||||
* ------------------------------ */
|
||||
@import "../components/grid-field/styles";
|
||||
@import "../components/grid-field-cell/styles";
|
||||
@import "../components/grid-field-header/styles";
|
||||
@import "../components/grid-field-header-cell/styles";
|
||||
@import "../components/grid-field-row/styles";
|
||||
@import "../components/north-header/styles";
|
44
admin/javascript/src/tests/reducer-register-test.js
Normal file
44
admin/javascript/src/tests/reducer-register-test.js
Normal file
@ -0,0 +1,44 @@
|
||||
jest.dontMock('../reducer-register');
|
||||
|
||||
var reducerRegister = require('../reducer-register').default;
|
||||
|
||||
describe('ReducerRegister', () => {
|
||||
|
||||
var reducer = () => null;
|
||||
|
||||
it('should add a reducer to the register', () => {
|
||||
expect(reducerRegister.getAll().test).toBe(undefined);
|
||||
|
||||
reducerRegister.add('test', reducer);
|
||||
expect(reducerRegister.getAll().test).toBe(reducer);
|
||||
|
||||
reducerRegister.remove('test');
|
||||
});
|
||||
|
||||
it('should remove a reducer from the register', () => {
|
||||
reducerRegister.add('test', reducer);
|
||||
expect(reducerRegister.getAll().test).toBe(reducer);
|
||||
|
||||
reducerRegister.remove('test');
|
||||
expect(reducerRegister.getAll().test).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should get all reducers from the register', () => {
|
||||
reducerRegister.add('test1', reducer);
|
||||
reducerRegister.add('test2', reducer);
|
||||
|
||||
expect(reducerRegister.getAll().test1).toBe(reducer);
|
||||
expect(reducerRegister.getAll().test2).toBe(reducer);
|
||||
|
||||
reducerRegister.remove('test1');
|
||||
reducerRegister.remove('test2');
|
||||
});
|
||||
|
||||
it('should get a single reducer from the register', () => {
|
||||
reducerRegister.add('test', reducer);
|
||||
expect(reducerRegister.getByKey('test')).toBe(reducer);
|
||||
|
||||
reducerRegister.remove('test');
|
||||
});
|
||||
|
||||
});
|
106
admin/javascript/src/tests/silverstripe-backend-test.js
Normal file
106
admin/javascript/src/tests/silverstripe-backend-test.js
Normal file
@ -0,0 +1,106 @@
|
||||
jest.mock('jQuery');
|
||||
jest.unmock('../silverstripe-backend');
|
||||
|
||||
import $ from 'jQuery';
|
||||
import backend from '../silverstripe-backend';
|
||||
|
||||
describe('SilverStripeBackend', () => {
|
||||
|
||||
describe('get()', () => {
|
||||
|
||||
it('should return a jqXHR', () => {
|
||||
var jqxhr = backend.get('http://example.com');
|
||||
|
||||
expect(typeof jqxhr).toBe('object');
|
||||
expect(typeof jqxhr.done).toBe('function');
|
||||
expect(typeof jqxhr.fail).toBe('function');
|
||||
expect(typeof jqxhr.always).toBe('function');
|
||||
});
|
||||
|
||||
it('should send a GET request to an endpoint', () => {
|
||||
backend.get('http://example.com');
|
||||
|
||||
expect($.ajax).toBeCalledWith({
|
||||
type: 'GET',
|
||||
url: 'http://example.com'
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('post()', () => {
|
||||
|
||||
it('should return a jqXHR', () => {
|
||||
var jqxhr = backend.get('http://example.com/item');
|
||||
|
||||
expect(typeof jqxhr).toBe('object');
|
||||
expect(typeof jqxhr.done).toBe('function');
|
||||
expect(typeof jqxhr.fail).toBe('function');
|
||||
expect(typeof jqxhr.always).toBe('function');
|
||||
});
|
||||
|
||||
it('should send a POST request to an endpoint', () => {
|
||||
const postData = { id: 1 };
|
||||
|
||||
backend.post('http://example.com', postData);
|
||||
|
||||
expect($.ajax).toBeCalledWith({
|
||||
type: 'POST',
|
||||
url: 'http://example.com',
|
||||
data: postData
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('put()', () => {
|
||||
|
||||
it('should return a jqXHR', () => {
|
||||
var jqxhr = backend.get('http://example.com/item');
|
||||
|
||||
expect(typeof jqxhr).toBe('object');
|
||||
expect(typeof jqxhr.done).toBe('function');
|
||||
expect(typeof jqxhr.fail).toBe('function');
|
||||
expect(typeof jqxhr.always).toBe('function');
|
||||
});
|
||||
|
||||
it('should send a PUT request to an endpoint', () => {
|
||||
const putData = { id: 1 };
|
||||
|
||||
backend.put('http://example.com', putData);
|
||||
|
||||
expect($.ajax).toBeCalledWith({
|
||||
type: 'PUT',
|
||||
url: 'http://example.com',
|
||||
data: putData
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('delete()', () => {
|
||||
|
||||
it('should return a jqXHR', () => {
|
||||
var jqxhr = backend.get('http://example.com/item');
|
||||
|
||||
expect(typeof jqxhr).toBe('object');
|
||||
expect(typeof jqxhr.done).toBe('function');
|
||||
expect(typeof jqxhr.fail).toBe('function');
|
||||
expect(typeof jqxhr.always).toBe('function');
|
||||
});
|
||||
|
||||
it('should send a DELETE request to an endpoint', () => {
|
||||
const deleteData = { id: 1 };
|
||||
|
||||
backend.delete('http://example.com', deleteData);
|
||||
|
||||
expect($.ajax).toBeCalledWith({
|
||||
type: 'DELETE',
|
||||
url: 'http://example.com',
|
||||
data: deleteData
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
@ -16,6 +16,9 @@
|
||||
background-image: sprite-url($sprite);
|
||||
background-size: ceil(image-width(sprite-path($sprite)) / 2) auto;
|
||||
|
||||
&.icon-campaignadmin {
|
||||
background-position: 0 round(nth(sprite-position($sprite, "collection"), 2) / 2);
|
||||
}
|
||||
&.icon-assetadmin {
|
||||
background-position: 0 round(nth(sprite-position($sprite, "picture"), 2) / 2);
|
||||
}
|
||||
@ -289,6 +292,10 @@
|
||||
height: 16px;
|
||||
@extend .retina-menu-icons-16x16-2x;
|
||||
|
||||
&.icon-campaignadmin {
|
||||
@include retina-sprite($menu-icons-16x16-2x-collection);
|
||||
display: inline-block;
|
||||
}
|
||||
&.icon-assetadmin {
|
||||
@include retina-sprite($menu-icons-16x16-2x-picture);
|
||||
display: inline-block;
|
||||
|
@ -68,6 +68,9 @@
|
||||
height: 24px;
|
||||
@extend .icon-menu-icons-24x24;
|
||||
|
||||
&.icon-campaignadmin {
|
||||
@include sprite($menu-icons-24x24-collection, inline-block);
|
||||
}
|
||||
&.icon-assetadmin {
|
||||
@include sprite($menu-icons-24x24-picture, inline-block);
|
||||
}
|
||||
@ -99,6 +102,9 @@
|
||||
height: 16px;
|
||||
@extend .icon-menu-icons-16x16;
|
||||
|
||||
&.icon-campaignadmin {
|
||||
@include sprite($menu-icons-16x16-collection, inline-block);
|
||||
}
|
||||
&.icon-assetadmin {
|
||||
@include sprite($menu-icons-16x16-picture, inline-block);
|
||||
}
|
||||
|
@ -120,39 +120,42 @@ $sprites-64x64-2x-tab-list-hover: -0px -160px 80px 80px;
|
||||
$sprites-64x64-2x-tab-list: -0px -240px 80px 80px;
|
||||
$sprites-64x64-2x-tab-tree-hover: -0px -320px 80px 80px;
|
||||
$sprites-64x64-2x-tab-tree: -0px -400px 80px 80px;
|
||||
$menu-icons-24x24-home: -0px -0px 24px 24px;
|
||||
$menu-icons-24x24-blog: -0px -24px 24px 24px;
|
||||
$menu-icons-24x24-db: -0px -48px 24px 24px;
|
||||
$menu-icons-24x24-document: -0px -72px 24px 24px;
|
||||
$menu-icons-24x24-gears: -0px -96px 24px 24px;
|
||||
$menu-icons-24x24-community: -0px -120px 24px 24px;
|
||||
$menu-icons-24x24-information: -0px -144px 24px 24px;
|
||||
$menu-icons-24x24-network: -0px -168px 24px 24px;
|
||||
$menu-icons-24x24-pencil: -0px -192px 24px 24px;
|
||||
$menu-icons-24x24-picture: -0px -216px 24px 24px;
|
||||
$menu-icons-24x24-pie-chart: -0px -240px 24px 24px;
|
||||
$menu-icons-16x16-2x-home: -0px -0px 32px 32px;
|
||||
$menu-icons-16x16-2x-blog: -0px -32px 32px 32px;
|
||||
$menu-icons-16x16-2x-db: -0px -64px 32px 32px;
|
||||
$menu-icons-16x16-2x-document: -0px -96px 32px 32px;
|
||||
$menu-icons-16x16-2x-gears: -0px -128px 32px 32px;
|
||||
$menu-icons-16x16-2x-community: -0px -160px 32px 32px;
|
||||
$menu-icons-16x16-2x-information: -0px -192px 32px 32px;
|
||||
$menu-icons-16x16-2x-network: -0px -224px 32px 32px;
|
||||
$menu-icons-16x16-2x-pencil: -0px -256px 32px 32px;
|
||||
$menu-icons-16x16-2x-picture: -0px -288px 32px 32px;
|
||||
$menu-icons-16x16-2x-pie-chart: -0px -320px 32px 32px;
|
||||
$menu-icons-16x16-home: -0px -0px 16px 16px;
|
||||
$menu-icons-16x16-blog: -0px -16px 16px 16px;
|
||||
$menu-icons-16x16-db: -0px -32px 16px 16px;
|
||||
$menu-icons-16x16-document: -0px -48px 16px 16px;
|
||||
$menu-icons-16x16-gears: -0px -64px 16px 16px;
|
||||
$menu-icons-16x16-community: -0px -80px 16px 16px;
|
||||
$menu-icons-16x16-information: -0px -96px 16px 16px;
|
||||
$menu-icons-16x16-network: -0px -112px 16px 16px;
|
||||
$menu-icons-16x16-pencil: -0px -128px 16px 16px;
|
||||
$menu-icons-16x16-picture: -0px -144px 16px 16px;
|
||||
$menu-icons-16x16-pie-chart: -0px -160px 16px 16px;
|
||||
$menu-icons-16x16-community: -0px -32px 16px 16px;
|
||||
$menu-icons-16x16-db: -0px -48px 16px 16px;
|
||||
$menu-icons-16x16-document: -0px -64px 16px 16px;
|
||||
$menu-icons-16x16-gears: -0px -80px 16px 16px;
|
||||
$menu-icons-16x16-collection: -0px -96px 16px 16px;
|
||||
$menu-icons-16x16-information: -0px -112px 16px 16px;
|
||||
$menu-icons-16x16-network: -0px -128px 16px 16px;
|
||||
$menu-icons-16x16-pencil: -0px -144px 16px 16px;
|
||||
$menu-icons-16x16-picture: -0px -160px 16px 16px;
|
||||
$menu-icons-16x16-pie-chart: -0px -176px 16px 16px;
|
||||
$menu-icons-16x16-2x-home: -0px -0px 32px 32px;
|
||||
$menu-icons-16x16-2x-blog: -0px -32px 32px 32px;
|
||||
$menu-icons-16x16-2x-community: -0px -64px 32px 32px;
|
||||
$menu-icons-16x16-2x-db: -0px -96px 32px 32px;
|
||||
$menu-icons-16x16-2x-document: -0px -128px 32px 32px;
|
||||
$menu-icons-16x16-2x-gears: -0px -160px 32px 32px;
|
||||
$menu-icons-16x16-2x-collection: -0px -192px 32px 32px;
|
||||
$menu-icons-16x16-2x-information: -0px -224px 32px 32px;
|
||||
$menu-icons-16x16-2x-network: -0px -256px 32px 32px;
|
||||
$menu-icons-16x16-2x-pencil: -0px -288px 32px 32px;
|
||||
$menu-icons-16x16-2x-picture: -0px -320px 32px 32px;
|
||||
$menu-icons-16x16-2x-pie-chart: -0px -352px 32px 32px;
|
||||
$menu-icons-24x24-home: -0px -0px 24px 24px;
|
||||
$menu-icons-24x24-blog: -0px -24px 24px 24px;
|
||||
$menu-icons-24x24-community: -0px -48px 24px 24px;
|
||||
$menu-icons-24x24-db: -0px -72px 24px 24px;
|
||||
$menu-icons-24x24-document: -0px -96px 24px 24px;
|
||||
$menu-icons-24x24-gears: -0px -120px 24px 24px;
|
||||
$menu-icons-24x24-collection: -0px -144px 24px 24px;
|
||||
$menu-icons-24x24-information: -0px -168px 24px 24px;
|
||||
$menu-icons-24x24-network: -0px -192px 24px 24px;
|
||||
$menu-icons-24x24-pencil: -0px -216px 24px 24px;
|
||||
$menu-icons-24x24-picture: -0px -240px 24px 24px;
|
||||
$menu-icons-24x24-pie-chart: -0px -264px 24px 24px;
|
||||
$menu-icons-24x24-2x-home: -0px -0px 48px 48px;
|
||||
$menu-icons-24x24-2x-blog: -0px -48px 48px 48px;
|
||||
$menu-icons-24x24-2x-db: -0px -96px 48px 48px;
|
||||
@ -204,14 +207,14 @@ $menu-icons-24x24-2x-pie-chart: -0px -480px 48px 48px;
|
||||
.icon-sprites-64x64-2x {
|
||||
background-image: url('../images/sprites/dist/sprite-sprites-64x64-2x.png');
|
||||
}
|
||||
.icon-menu-icons-24x24 {
|
||||
background-image: url('../images/sprites/dist/sprite-menu-icons-24x24.png');
|
||||
.icon-menu-icons-16x16 {
|
||||
background-image: url('../images/sprites/dist/sprite-menu-icons-16x16.png');
|
||||
}
|
||||
.icon-menu-icons-16x16-2x {
|
||||
background-image: url('../images/sprites/dist/sprite-menu-icons-16x16-2x.png');
|
||||
}
|
||||
.icon-menu-icons-16x16 {
|
||||
background-image: url('../images/sprites/dist/sprite-menu-icons-16x16.png');
|
||||
.icon-menu-icons-24x24 {
|
||||
background-image: url('../images/sprites/dist/sprite-menu-icons-24x24.png');
|
||||
}
|
||||
.icon-menu-icons-24x24-2x {
|
||||
background-image: url('../images/sprites/dist/sprite-menu-icons-24x24-2x.png');
|
||||
|
@ -29,7 +29,7 @@
|
||||
// $gray-dark: #373a3c;
|
||||
// $gray: #55595c;
|
||||
// $gray-light: #818a91;
|
||||
// $gray-lighter: #eceeef;
|
||||
$gray-lighter: #e8e9ea;
|
||||
// $gray-lightest: #f7f7f9;
|
||||
//
|
||||
// $brand-primary: #0275d8;
|
||||
|
@ -51,6 +51,11 @@
|
||||
@import "SecurityAdmin.scss";
|
||||
@import "CMSSecurity.scss";
|
||||
|
||||
/** -----------------------------
|
||||
* Include React components' css
|
||||
* ------------------------------ */
|
||||
@import "../javascript/src/styles/main.scss";
|
||||
|
||||
/** -----------------------------
|
||||
* Retina graphics
|
||||
* ----------------------------- */
|
||||
|
@ -4,6 +4,8 @@
|
||||
* and leave the actual styling to _style.scss and auxilliary files.
|
||||
*/
|
||||
|
||||
@import "../bootstrap/variables.scss";
|
||||
|
||||
/** -----------------------------------------------
|
||||
* Colours
|
||||
* ------------------------------------------------ */
|
||||
|
@ -295,6 +295,18 @@ $ npm run sanity
|
||||
|
||||
`sanity` makes sure files in `thirdparty` match files copied from `node_modules`. You should never commit custom changes to a library file. This script will catch them if you do :smile:
|
||||
|
||||
```
|
||||
$ npm run test
|
||||
```
|
||||
|
||||
This script runs the JavaScript unit tests.
|
||||
|
||||
```
|
||||
$ npm run coverage
|
||||
```
|
||||
|
||||
This script generates a coverage report for the JavaScript unit tests. The report is generated in the `coverage` directory.
|
||||
|
||||
```
|
||||
$ npm run css
|
||||
```
|
||||
|
89
gulpfile.js
89
gulpfile.js
@ -10,7 +10,6 @@ var gulp = require('gulp'),
|
||||
autoprefixer = require('autoprefixer'),
|
||||
browserify = require('browserify'),
|
||||
babelify = require('babelify'),
|
||||
watchify = require('watchify'),
|
||||
source = require('vinyl-source-stream'),
|
||||
buffer = require('vinyl-buffer'),
|
||||
path = require('path'),
|
||||
@ -42,11 +41,7 @@ var PATHS = {
|
||||
// Folders which contain both scss and css folders to be compiled
|
||||
var rootCompileFolders = [PATHS.FRAMEWORK, PATHS.ADMIN, PATHS.FRAMEWORK_DEV_INSTALL]
|
||||
|
||||
var browserifyOptions = {
|
||||
cache: {},
|
||||
packageCache: {},
|
||||
poll: true
|
||||
};
|
||||
var browserifyOptions = {};
|
||||
|
||||
// Used for autoprefixing css properties (same as Bootstrap Aplha.2 defaults)
|
||||
var supportedBrowsers = [
|
||||
@ -173,17 +168,23 @@ if (!semver.satisfies(process.versions.node, packageJson.engines.node)) {
|
||||
|
||||
if (isDev) {
|
||||
browserifyOptions.debug = true;
|
||||
browserifyOptions.plugin = [watchify];
|
||||
}
|
||||
|
||||
gulp.task('build', ['umd', 'bundle']);
|
||||
gulp.task('build', ['umd', 'bundle'], function () {
|
||||
if (isDev) {
|
||||
gulp.watch([
|
||||
PATHS.ADMIN_JAVASCRIPT_SRC + '/**/*.js',
|
||||
PATHS.FRAMEWORK_JAVASCRIPT_SRC + '/**/*.js',
|
||||
], ['build']);
|
||||
}
|
||||
});
|
||||
|
||||
gulp.task('bundle', ['bundle-lib', 'bundle-leftandmain', 'bundle-react']);
|
||||
gulp.task('bundle', ['bundle-lib', 'bundle-leftandmain', 'bundle-boot', 'bundle-react', 'bundle-campaign-admin']);
|
||||
|
||||
gulp.task('bundle-leftandmain', function bundleLeftAndMain() {
|
||||
return browserify(Object.assign({}, browserifyOptions, {
|
||||
entries: PATHS.ADMIN_JAVASCRIPT_SRC + '/bundles/leftandmain.js'
|
||||
}))
|
||||
var bundleFileName = 'bundle-leftandmain.js';
|
||||
|
||||
return browserify(Object.assign({}, browserifyOptions, { entries: PATHS.ADMIN_JAVASCRIPT_SRC + '/bundles/leftandmain.js' }))
|
||||
.transform(babelify.configure({
|
||||
presets: ['es2015'],
|
||||
ignore: /(thirdparty)/,
|
||||
@ -195,17 +196,17 @@ gulp.task('bundle-leftandmain', function bundleLeftAndMain() {
|
||||
.external('router')
|
||||
.bundle()
|
||||
.on('update', bundleLeftAndMain)
|
||||
.on('error', notify.onError({ message: 'Error: <%= error.message %>' }))
|
||||
.pipe(source('bundle-leftandmain.js'))
|
||||
.on('error', notify.onError({ message: bundleFileName + ': <%= error.message %>' }))
|
||||
.pipe(source(bundleFileName))
|
||||
.pipe(buffer())
|
||||
.pipe(gulpif(!isDev, uglify()))
|
||||
.pipe(gulp.dest(PATHS.ADMIN_JAVASCRIPT_DIST));
|
||||
});
|
||||
|
||||
gulp.task('bundle-lib', function bundleLib() {
|
||||
return browserify(Object.assign({}, browserifyOptions, {
|
||||
entries: PATHS.ADMIN_JAVASCRIPT_SRC + '/bundles/lib.js'
|
||||
}))
|
||||
var bundleFileName = 'bundle-lib.js';
|
||||
|
||||
return browserify(Object.assign({}, browserifyOptions, { entries: PATHS.ADMIN_JAVASCRIPT_SRC + '/bundles/lib.js' }))
|
||||
.transform(babelify.configure({
|
||||
presets: ['es2015'],
|
||||
ignore: /(thirdparty)/,
|
||||
@ -215,10 +216,11 @@ gulp.task('bundle-lib', function bundleLib() {
|
||||
.require(PATHS.FRAMEWORK_JAVASCRIPT_SRC + '/jQuery.js', { expose: 'jQuery' })
|
||||
.require(PATHS.FRAMEWORK_JAVASCRIPT_SRC + '/i18n.js', { expose: 'i18n' })
|
||||
.require(PATHS.FRAMEWORK_JAVASCRIPT_SRC + '/router.js', { expose: 'router' })
|
||||
.require(PATHS.ADMIN_JAVASCRIPT_SRC + '/reducer-register.js', { expose: 'reducer-register' })
|
||||
.bundle()
|
||||
.on('update', bundleLib)
|
||||
.on('error', notify.onError({ message: 'Error: <%= error.message %>' }))
|
||||
.pipe(source('bundle-lib.js'))
|
||||
.on('error', notify.onError({ message: bundleFileName + ': <%= error.message %>' }))
|
||||
.pipe(source(bundleFileName))
|
||||
.pipe(buffer())
|
||||
.pipe(gulpif(!isDev, uglify()))
|
||||
.pipe(gulp.dest(PATHS.ADMIN_JAVASCRIPT_DIST));
|
||||
@ -236,8 +238,53 @@ gulp.task('bundle-react', function bundleReact() {
|
||||
.require(PATHS.ADMIN_JAVASCRIPT_DIST + '/SilverStripeComponent', { expose: 'silverstripe-component' })
|
||||
.bundle()
|
||||
.on('update', bundleReact)
|
||||
.on('error', notify.onError({ message: 'Error: <%= error.message %>' }))
|
||||
.pipe(source('bundle-react.js'))
|
||||
.on('error', notify.onError({ message: bundleFileName + ': <%= error.message %>' }))
|
||||
.pipe(source(bundleFileName))
|
||||
.pipe(buffer())
|
||||
.pipe(gulpif(!isDev, uglify()))
|
||||
.pipe(gulp.dest(PATHS.ADMIN_JAVASCRIPT_DIST));
|
||||
});
|
||||
|
||||
gulp.task('bundle-boot', function bundleBoot() {
|
||||
var bundleFileName = 'boot.js';
|
||||
|
||||
return browserify(Object.assign({}, browserifyOptions, { entries: PATHS.ADMIN_JAVASCRIPT_SRC + '/boot/index.js' }))
|
||||
.transform(babelify.configure({
|
||||
presets: ['es2015'],
|
||||
ignore: /(node_modules)/
|
||||
}))
|
||||
.external('reducer-register')
|
||||
.bundle()
|
||||
.on('error', notify.onError({ message: bundleFileName + ': <%= error.message %>' }))
|
||||
.pipe(source(bundleFileName))
|
||||
.pipe(buffer())
|
||||
.pipe(gulpif(!isDev, uglify()))
|
||||
.pipe(gulp.dest(PATHS.ADMIN_JAVASCRIPT_DIST));
|
||||
});
|
||||
|
||||
gulp.task('bundle-campaign-admin', function bundleCampaignAdmin() {
|
||||
var bundleFileName = 'campaign-admin.js';
|
||||
|
||||
return browserify(Object.assign({}, browserifyOptions, { entries: PATHS.ADMIN_JAVASCRIPT_SRC + '/boot/campaign-admin.js' }))
|
||||
.transform(babelify.configure({
|
||||
presets: ['es2015', 'react'],
|
||||
ignore: /(node_modules)/
|
||||
}))
|
||||
.external('deep-freeze')
|
||||
.external('i18n')
|
||||
.external('jQuery')
|
||||
.external('page.js')
|
||||
.external('react')
|
||||
.external('react-addons-test-utils')
|
||||
.external('react-dom')
|
||||
.external('react-redux')
|
||||
.external('redux')
|
||||
.external('redux-thunk')
|
||||
.external('silverstripe-component')
|
||||
.external('reducer-register')
|
||||
.bundle()
|
||||
.on('error', notify.onError({ message: bundleFileName + ': <%= error.message %>' }))
|
||||
.pipe(source(bundleFileName))
|
||||
.pipe(buffer())
|
||||
.pipe(gulpif(!isDev, uglify()))
|
||||
.pipe(gulp.dest(PATHS.ADMIN_JAVASCRIPT_DIST));
|
||||
|
50
package.json
50
package.json
@ -12,11 +12,13 @@
|
||||
"scripts": {
|
||||
"build": "gulp build",
|
||||
"bundle": "gulp bundle",
|
||||
"sanity": "gulp sanity",
|
||||
"thirdparty": "gulp thirdparty",
|
||||
"coverage": "jest --coverage",
|
||||
"css": "gulp css",
|
||||
"lock": "npm-shrinkwrap --dev",
|
||||
"sanity": "gulp sanity",
|
||||
"sprites": "gulp sprites",
|
||||
"lock": "npm-shrinkwrap --dev"
|
||||
"test": "jest",
|
||||
"thirdparty": "gulp thirdparty"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -35,8 +37,10 @@
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^6.3.1",
|
||||
"babel-core": "^6.4.0",
|
||||
"babel-jest": "^9.0.3",
|
||||
"babel-plugin-transform-es2015-modules-umd": "^6.4.0",
|
||||
"babel-preset-es2015": "^6.3.13",
|
||||
"babel-preset-es2015": "^6.6.0",
|
||||
"babel-preset-react": "^6.5.0",
|
||||
"babelify": "^7.2.0",
|
||||
"browserify": "^13.0.0",
|
||||
"event-stream": "^3.3.2",
|
||||
@ -51,6 +55,10 @@
|
||||
"gulp-sourcemaps": "^1.6.0",
|
||||
"gulp-uglify": "^1.5.1",
|
||||
"gulp-util": "^3.0.7",
|
||||
"jest-cli": "^0.9.2",
|
||||
"npm-shrinkwrap": "^5.4.1",
|
||||
"react-addons-test-utils": "^0.14.6",
|
||||
"redux-logger": "^2.6.1",
|
||||
"semver": "^5.1.0",
|
||||
"sprity": "^1.0.8",
|
||||
"sprity-sass": "^1.0.4",
|
||||
@ -58,22 +66,22 @@
|
||||
"vinyl-source-stream": "^1.1.0",
|
||||
"watchify": "^3.7.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"blueimp-file-upload": "^6.0.3",
|
||||
"blueimp-load-image": "^1.1.3",
|
||||
"blueimp-tmpl": "^1.0.2",
|
||||
"bootstrap": "^4.0.0-alpha.2",
|
||||
"isomorphic-fetch": "^2.2.1",
|
||||
"jquery-sizes": "^0.33.0",
|
||||
"json-js": "^1.1.2",
|
||||
"npm-shrinkwrap": "^5.4.1",
|
||||
"page.js": "^4.13.3",
|
||||
"react": "^0.14.6",
|
||||
"react-addons-test-utils": "^0.14.6",
|
||||
"react-dom": "^0.14.6",
|
||||
"react-redux": "^4.0.6",
|
||||
"redux": "^3.0.5",
|
||||
"redux-thunk": "^1.0.3",
|
||||
"tinymce": "^4.3.3"
|
||||
"jest": {
|
||||
"scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
|
||||
"testPathDirs": [
|
||||
"admin/javascript/src"
|
||||
],
|
||||
"testDirectoryName": "tests",
|
||||
"mocksPattern": "mocks",
|
||||
"unmockedModulePathPatterns": [
|
||||
"<rootDir>/node_modules/react"
|
||||
],
|
||||
"bail": true,
|
||||
"testRunner": "<rootDir>/node_modules/jest-cli/src/testRunners/jasmine/jasmine2.js"
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
"es2015"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user