diff --git a/admin/javascript/src/__mocks__/silverstripe-component.js b/admin/javascript/src/__mocks__/silverstripe-component.js
new file mode 100644
index 000000000..8a4dcdcc3
--- /dev/null
+++ b/admin/javascript/src/__mocks__/silverstripe-component.js
@@ -0,0 +1,15 @@
+import { Component } from 'react';
+
+export default class SilverStripeComponent extends Component {
+ constructor(props) {
+ super(props);
+ }
+
+ componentDidMount() {
+
+ }
+
+ componentWillUnmount() {
+
+ }
+};
diff --git a/admin/javascript/src/components/grid-field-cell/index.js b/admin/javascript/src/components/grid-field-cell/index.js
index e6a6c137b..54dcef1bc 100644
--- a/admin/javascript/src/components/grid-field-cell/index.js
+++ b/admin/javascript/src/components/grid-field-cell/index.js
@@ -5,7 +5,7 @@ class GridFieldCellComponent extends SilverStripeComponent {
render() {
return (
-
{this.props.children} |
+ {this.props.children}
);
}
diff --git a/admin/javascript/src/components/grid-field-cell/styles.scss b/admin/javascript/src/components/grid-field-cell/styles.scss
index 521563a1e..4599d9084 100644
--- a/admin/javascript/src/components/grid-field-cell/styles.scss
+++ b/admin/javascript/src/components/grid-field-cell/styles.scss
@@ -1,3 +1,11 @@
.grid-field-cell-component {
-
+ display: flex;
+ flex-flow: row nowrap;
+ flex-grow: 1;
+ flex-basis: 0;
+ word-wrap: break-word;
+ overflow-wrap: break-word;
+ word-break: break-word;
+ padding: $grid-y*2 $grid-x*2;
+ line-height: 20px;
}
diff --git a/admin/javascript/src/components/grid-field-header-cell/index.js b/admin/javascript/src/components/grid-field-header-cell/index.js
index 70aab6f55..76ff15b7a 100644
--- a/admin/javascript/src/components/grid-field-header-cell/index.js
+++ b/admin/javascript/src/components/grid-field-header-cell/index.js
@@ -5,7 +5,7 @@ class GridFieldHeaderCellComponent extends SilverStripeComponent {
render() {
return (
- {this.props.children} |
+ {this.props.children}
);
}
diff --git a/admin/javascript/src/components/grid-field-header-cell/styles.scss b/admin/javascript/src/components/grid-field-header-cell/styles.scss
index e69de29bb..7b73e4c41 100644
--- a/admin/javascript/src/components/grid-field-header-cell/styles.scss
+++ b/admin/javascript/src/components/grid-field-header-cell/styles.scss
@@ -0,0 +1,12 @@
+.grid-field-header-cell-component {
+ text-transform: uppercase;
+ display: flex;
+ flex-flow: row nowrap;
+ flex-grow: 1;
+ flex-basis: 0;
+ word-wrap: break-word;
+ overflow-wrap: break-word;
+ word-break: break-word;
+ padding: $grid-y*2 $grid-x*2;
+ line-height: 20px;
+}
\ No newline at end of file
diff --git a/admin/javascript/src/components/grid-field-header/index.js b/admin/javascript/src/components/grid-field-header/index.js
index c5fda9753..025ed54c8 100644
--- a/admin/javascript/src/components/grid-field-header/index.js
+++ b/admin/javascript/src/components/grid-field-header/index.js
@@ -6,9 +6,7 @@ class GridFieldHeaderComponent extends SilverStripeComponent {
render() {
return (
-
- {this.props.children}
-
+ {this.props.children}
);
}
diff --git a/admin/javascript/src/components/grid-field-row/index.js b/admin/javascript/src/components/grid-field-row/index.js
index 3818c00db..a2c945553 100644
--- a/admin/javascript/src/components/grid-field-row/index.js
+++ b/admin/javascript/src/components/grid-field-row/index.js
@@ -5,7 +5,7 @@ class GridFieldRowComponent extends SilverStripeComponent {
render() {
return (
- {this.props.children}
+ {this.props.children}
);
}
diff --git a/admin/javascript/src/components/grid-field-row/styles.scss b/admin/javascript/src/components/grid-field-row/styles.scss
index 3f8dbc1a9..28d3c9012 100644
--- a/admin/javascript/src/components/grid-field-row/styles.scss
+++ b/admin/javascript/src/components/grid-field-row/styles.scss
@@ -1,3 +1,16 @@
-.grid-field-row-component {
+.grid-field-table-component {
+ li.grid-field-row-component {
+ display: flex;
+ flex-flow: row nowrap;
+ width: 100%;
+ border: 0;
+ box-shadow: inset 0 -1px 0 0 $gray-lighter;
+ margin: 0;
+ padding: 0;
+ // Header row
+ &:first-child {
+ background: none;
+ }
+ }
}
diff --git a/admin/javascript/src/components/grid-field/README.md b/admin/javascript/src/components/grid-field-table/README.md
similarity index 86%
rename from admin/javascript/src/components/grid-field/README.md
rename to admin/javascript/src/components/grid-field-table/README.md
index 42e40c433..259fa6494 100644
--- a/admin/javascript/src/components/grid-field/README.md
+++ b/admin/javascript/src/components/grid-field-table/README.md
@@ -1,4 +1,4 @@
-# GridField
+# GridFieldTableComponent
This component is used to display structured data in an extendible table layout.
diff --git a/admin/javascript/src/components/grid-field/index.js b/admin/javascript/src/components/grid-field-table/index.js
similarity index 80%
rename from admin/javascript/src/components/grid-field/index.js
rename to admin/javascript/src/components/grid-field-table/index.js
index c072b00dc..479acdeaa 100644
--- a/admin/javascript/src/components/grid-field/index.js
+++ b/admin/javascript/src/components/grid-field-table/index.js
@@ -1,16 +1,14 @@
import React from 'react';
import SilverStripeComponent from 'silverstripe-component';
-class GridFieldComponent extends SilverStripeComponent {
+class GridFieldTableComponent extends SilverStripeComponent {
render() {
return (
-
+
{this.generateHeader()}
-
- {this.generateRows()}
-
-
+ {this.generateRows()}
+
);
}
@@ -56,10 +54,10 @@ class GridFieldComponent extends SilverStripeComponent {
}
-GridFieldComponent.propTypes = {
+GridFieldTableComponent.propTypes = {
data: React.PropTypes.object,
header: React.PropTypes.object,
rows: React.PropTypes.array
};
-export default GridFieldComponent;
+export default GridFieldTableComponent;
diff --git a/admin/javascript/src/components/grid-field-table/styles.scss b/admin/javascript/src/components/grid-field-table/styles.scss
new file mode 100644
index 000000000..15aceb166
--- /dev/null
+++ b/admin/javascript/src/components/grid-field-table/styles.scss
@@ -0,0 +1,5 @@
+.grid-field-table-component {
+ display: flex;
+ flex-flow: column nowrap;
+ justify-content: space-between;
+}
diff --git a/admin/javascript/src/components/grid-field-table/tests/grid-field-test.js b/admin/javascript/src/components/grid-field-table/tests/grid-field-test.js
new file mode 100644
index 000000000..0869ecdfe
--- /dev/null
+++ b/admin/javascript/src/components/grid-field-table/tests/grid-field-test.js
@@ -0,0 +1,66 @@
+jest.dontMock('../index');
+
+const React = require('react'),
+ ReactTestUtils = require('react-addons-test-utils'),
+ GridFieldTableComponent = require('../index.js').default;
+
+describe('GridFieldTableComponent', () => {
+ var props;
+
+ beforeEach(function () {
+ props = {
+ }
+ });
+
+ describe('generateHeader()', function () {
+ var gridfield;
+
+ it('should return props.header if it is set', function () {
+ props.header = ;
+
+ gridfield = ReactTestUtils.renderIntoDocument(
+
+ );
+
+ expect(gridfield.generateHeader().props.className).toBe('header');
+ });
+
+ it('should generate and return a header from props.data if it is set', function () {
+
+ });
+
+ it('should return null if props.header and props.data are both not set', function () {
+ gridfield = ReactTestUtils.renderIntoDocument(
+
+ );
+
+ expect(gridfield.generateHeader()).toBe(null);
+ });
+ });
+
+ describe('generateRows()', function () {
+ var gridfield;
+
+ it('should return props.rows if it is set', function () {
+ props.rows = ['row1'];
+
+ gridfield = ReactTestUtils.renderIntoDocument(
+
+ );
+
+ expect(gridfield.generateRows()[0]).toBe('row1');
+ });
+
+ it('should generate and return rows from props.data if it is set', function () {
+
+ });
+
+ it('should return null if props.rows and props.data are both not set', function () {
+ gridfield = ReactTestUtils.renderIntoDocument(
+
+ );
+
+ expect(gridfield.generateRows()).toBe(null);
+ });
+ });
+});
diff --git a/admin/javascript/src/components/grid-field/styles.scss b/admin/javascript/src/components/grid-field/styles.scss
deleted file mode 100644
index 1254bf0d1..000000000
--- a/admin/javascript/src/components/grid-field/styles.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-.grid-field-component {
-
-}
diff --git a/admin/javascript/src/components/grid-field/tests/grid-field-test.js b/admin/javascript/src/components/grid-field/tests/grid-field-test.js
deleted file mode 100644
index 426c20aec..000000000
--- a/admin/javascript/src/components/grid-field/tests/grid-field-test.js
+++ /dev/null
@@ -1,5 +0,0 @@
-jest.dontMock('../index');
-
-describe('GridFieldComponent', () => {
-
-});
diff --git a/admin/javascript/src/components/north-header-breadcrumbs/README.md b/admin/javascript/src/components/north-header-breadcrumbs/README.md
new file mode 100644
index 000000000..e2ec18566
--- /dev/null
+++ b/admin/javascript/src/components/north-header-breadcrumbs/README.md
@@ -0,0 +1,36 @@
+#NorthHeaderBreadcrumbs
+
+The breadcrumbs for the current section of the CMS.
+
+## Props
+
+### Crumbs (array)
+
+An array of objects, each object should have a `text` and `href` key.
+
+```
+import NorthHeaderBreadcrumbsComponent from 'north-header-breadcrumbs';
+
+...
+
+getBreadcrumbs() {
+ var breadcrumbs = [
+ {
+ text: 'Pages',
+ href: 'admin/pages'
+ },
+ {
+ text: 'About us',
+ href: 'admin/pages/show/2'
+ }
+ ];
+
+ return breadcrumbs;
+}
+
+render() {
+ return
+}
+
+...
+```
\ No newline at end of file
diff --git a/admin/javascript/src/components/north-header-breadcrumbs/index.js b/admin/javascript/src/components/north-header-breadcrumbs/index.js
new file mode 100644
index 000000000..1bacb6c6e
--- /dev/null
+++ b/admin/javascript/src/components/north-header-breadcrumbs/index.js
@@ -0,0 +1,40 @@
+import React from 'react';
+import SilverStripeComponent from 'silverstripe-component';
+
+class NorthHeaderBreadcrumbsComponent extends SilverStripeComponent {
+
+ render() {
+ return (
+
+
+
+ {this.getBreadcrumbs()}
+
+
+
+ );
+ }
+
+ getBreadcrumbs() {
+ if (typeof this.props.crumbs === 'undefined') {
+ return null;
+ }
+
+ var breadcrumbs = this.props.crumbs.map((crumb, index, crumbs) => {
+ // If its the last item in the array
+ if (index === crumbs.length - 1) {
+ return {crumb.text};
+ } else {
+ return [
+ {crumb.text},
+ /
+ ];
+ }
+ });
+
+ return breadcrumbs;
+ }
+
+}
+
+export default NorthHeaderBreadcrumbsComponent;
diff --git a/admin/javascript/src/components/north-header-breadcrumbs/styles.scss b/admin/javascript/src/components/north-header-breadcrumbs/styles.scss
new file mode 100644
index 000000000..247ccaa6a
--- /dev/null
+++ b/admin/javascript/src/components/north-header-breadcrumbs/styles.scss
@@ -0,0 +1,5 @@
+.breadcrumbs-wrapper {
+ .sep {
+ margin: 0 4px;
+ }
+}
\ No newline at end of file
diff --git a/admin/javascript/src/components/north-header-breadcrumbs/tests/north-header-breadcrumbs-test.js b/admin/javascript/src/components/north-header-breadcrumbs/tests/north-header-breadcrumbs-test.js
new file mode 100644
index 000000000..0e7a9591c
--- /dev/null
+++ b/admin/javascript/src/components/north-header-breadcrumbs/tests/north-header-breadcrumbs-test.js
@@ -0,0 +1,40 @@
+jest.dontMock('../index');
+
+const React = require('react'),
+ ReactTestUtils = require('react-addons-test-utils'),
+ NorthHeaderBreadcrumbsComponent = require('../index').default;
+
+describe('NorthHeaderBreadcrumbsComponent', () => {
+ var props;
+
+ beforeEach(() => {
+ props = {};
+ });
+
+ describe('getBreadcrumbs()', () => {
+ var northHeaderBreadcrumbs;
+
+ it('should convert the props.crumbs array into jsx to be rendered', () => {
+ props.crumbs = [
+ { text: 'breadcrumb1', href: 'href1'},
+ { text: 'breadcrumb2', href: 'href2'}
+ ];
+
+ northHeaderBreadcrumbs = ReactTestUtils.renderIntoDocument(
+
+ );
+
+ expect(northHeaderBreadcrumbs.getBreadcrumbs()[0][0].props.children).toBe('breadcrumb1');
+ expect(northHeaderBreadcrumbs.getBreadcrumbs()[0][1].props.children).toBe('/');
+ expect(northHeaderBreadcrumbs.getBreadcrumbs()[1].props.children).toBe('breadcrumb2');
+ });
+
+ it('should return null if props.crumbs is not set', () => {
+ northHeaderBreadcrumbs = ReactTestUtils.renderIntoDocument(
+
+ );
+
+ expect(northHeaderBreadcrumbs.getBreadcrumbs()).toBe(null);
+ });
+ });
+});
diff --git a/admin/javascript/src/components/north-header/README.md b/admin/javascript/src/components/north-header/README.md
new file mode 100644
index 000000000..9346a030c
--- /dev/null
+++ b/admin/javascript/src/components/north-header/README.md
@@ -0,0 +1,5 @@
+#NorthHeader
+
+The main header for sections in the CMS.
+
+## Props
\ No newline at end of file
diff --git a/admin/javascript/src/components/north-header/index.js b/admin/javascript/src/components/north-header/index.js
new file mode 100644
index 000000000..f6b89c697
--- /dev/null
+++ b/admin/javascript/src/components/north-header/index.js
@@ -0,0 +1,30 @@
+import React from 'react';
+import NorthHeaderBreadcrumbsComponent from '../north-header-breadcrumbs';
+import SilverStripeComponent from 'silverstripe-component';
+
+class NorthHeaderComponent extends SilverStripeComponent {
+
+ render() {
+ return (
+
+
+
+ );
+ }
+
+ getBreadcrumbs() {
+ return [
+ {
+ text: 'Campaigns',
+ href: 'admin/campaigns'
+ },
+ {
+ text: 'March release',
+ href: 'admin/campaigns/show/1'
+ }
+ ]
+ }
+
+}
+
+export default NorthHeaderComponent;
diff --git a/admin/javascript/src/components/north-header/styles.scss b/admin/javascript/src/components/north-header/styles.scss
new file mode 100644
index 000000000..4a18d3340
--- /dev/null
+++ b/admin/javascript/src/components/north-header/styles.scss
@@ -0,0 +1,4 @@
+.north-header-component {
+ @extend .cms-content-header;
+ width: 100%;
+}
\ No newline at end of file
diff --git a/admin/javascript/src/components/north-header/tests/north-header-test.js b/admin/javascript/src/components/north-header/tests/north-header-test.js
new file mode 100644
index 000000000..dbace78df
--- /dev/null
+++ b/admin/javascript/src/components/north-header/tests/north-header-test.js
@@ -0,0 +1,5 @@
+jest.dontMock('../index');
+
+describe('NorthHeaderComponent', () => {
+
+});
diff --git a/admin/javascript/src/sections/campaign-admin/controller.js b/admin/javascript/src/sections/campaign-admin/controller.js
index f8d3cca14..3046a2cae 100644
--- a/admin/javascript/src/sections/campaign-admin/controller.js
+++ b/admin/javascript/src/sections/campaign-admin/controller.js
@@ -1,56 +1,15 @@
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';
+import GridField from '../grid-field';
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) => {columnName});
- const header = {headerCells};
-
- const rows = this.mockData.campaigns.map((campaign, i) => {
- const cells = columnNames.map((columnName, i) => {
- return {campaign[columnName]}
- });
- return {cells};
- });
-
return (
-
+
);
}
diff --git a/admin/javascript/src/sections/grid-field/README.md b/admin/javascript/src/sections/grid-field/README.md
new file mode 100644
index 000000000..5aa85f47b
--- /dev/null
+++ b/admin/javascript/src/sections/grid-field/README.md
@@ -0,0 +1,3 @@
+# GridField
+
+General purpose component for tabular data.
diff --git a/admin/javascript/src/sections/grid-field/index.js b/admin/javascript/src/sections/grid-field/index.js
new file mode 100644
index 000000000..9a8d2b61d
--- /dev/null
+++ b/admin/javascript/src/sections/grid-field/index.js
@@ -0,0 +1,56 @@
+import React from 'react';
+import SilverStripeComponent from 'silverstripe-component';
+import GridFieldTable from '../../components/grid-field-table';
+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 GridField 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) => {columnName});
+ const header = {headerCells};
+
+ const rows = this.mockData.campaigns.map((campaign, i) => {
+ const cells = columnNames.map((columnName, i) => {
+ return {campaign[columnName]}
+ });
+ return {cells};
+ });
+
+ return (
+
+ );
+ }
+
+}
+
+export default GridField;
diff --git a/admin/javascript/src/styles/main.scss b/admin/javascript/src/styles/main.scss
index e7e3af67b..6448bf5b6 100644
--- a/admin/javascript/src/styles/main.scss
+++ b/admin/javascript/src/styles/main.scss
@@ -6,9 +6,10 @@
/** -----------------------------
* components
* ------------------------------ */
-@import "../components/grid-field/styles";
+@import "../components/grid-field-table/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";
+@import "../components/north-header-breadcrumbs/styles";
diff --git a/package.json b/package.json
index 28d66a896..b17c8c49f 100644
--- a/package.json
+++ b/package.json
@@ -81,6 +81,7 @@
},
"babel": {
"presets": [
+ "react",
"es2015"
]
}