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.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" ] }